@sap/cds-compiler 2.5.2 → 2.11.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 (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. package/lib/utils/string.js +0 -17
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
  /**
@@ -132,7 +135,7 @@ function odataInternal(csn, internalOptions) {
132
135
  * Return a odata-transformed CSN
133
136
  *
134
137
  * @param {CSN.Model} csn Clean input CSN
135
- * @param {oDataOptions} [options={}] Options
138
+ * @param {ODataOptions} [options={}] Options
136
139
  * @returns {oDataCSN} Return an oData-pre-processed CSN
137
140
  */
138
141
  function odata(csn, options = {}) {
@@ -188,8 +191,11 @@ function forHdi(csn, options = {}) {
188
191
  */
189
192
  function forHdbcds(csn, options = {}) {
190
193
  const internalOptions = prepareOptions.to.hdbcds(options);
191
- internalOptions.toHana.csn = true;
192
- return backends.toHanaWithCsn(csn, internalOptions).csn;
194
+ internalOptions.transformation = 'hdbcds';
195
+
196
+ const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
197
+
198
+ return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
193
199
  }
194
200
 
195
201
  /**
@@ -205,18 +211,7 @@ function sql(csn, options = {}) {
205
211
  // we need the CSN for view sorting
206
212
  internalOptions.toSql.csn = true;
207
213
 
208
- let intermediateResult;
209
- if (isBetaEnabled(internalOptions, 'pretransformedCSN') && isPreTransformed(csn, 'sql')) {
210
- internalOptions.noRecompile = true; // pre-transformed cannot be recompiled!
211
- checkPreTransformedCsn(csn, internalOptions, relevantSqlOptions, warnAboutMismatchOdata, 'to.sql');
212
-
213
- intermediateResult = backends.renderSqlWithCsn(csn, internalOptions);
214
- // attach CSN for view sorting
215
- intermediateResult.csn = cloneCsn(csn, internalOptions);
216
- }
217
- else {
218
- intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
219
- }
214
+ const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
220
215
 
221
216
  const result = sortViews(intermediateResult);
222
217
 
@@ -445,16 +440,21 @@ hdi.migration = hdiMigration;
445
440
  * @returns {HDBCDS} { <filename>:<content>, ...}
446
441
  */
447
442
  function hdbcds(csn, options = {}) {
443
+ timetrace.start('to.hdbcds');
448
444
  const internalOptions = prepareOptions.to.hdbcds(options);
449
- const intermediateResult = backends.toHanaWithCsn(csn, internalOptions);
445
+ internalOptions.transformation = 'hdbcds';
450
446
 
451
- return flattenResultStructure(intermediateResult);
447
+ const hanaCsn = forHdbcds(csn, internalOptions);
448
+
449
+ const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
450
+ timetrace.stop();
451
+ return result;
452
452
  }
453
453
  /**
454
454
  * Generate a edm document for the given service
455
455
  *
456
456
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
457
- * @param {oDataOptions} [options={}] Options
457
+ * @param {ODataOptions} [options={}] Options
458
458
  * @returns {edm} The JSON representation of the service
459
459
  */
460
460
  function edm(csn, options = {}) {
@@ -484,7 +484,7 @@ edm.all = edmall;
484
484
  * Generate edm documents for all services
485
485
  *
486
486
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
487
- * @param {oDataOptions} [options={}] Options
487
+ * @param {ODataOptions} [options={}] Options
488
488
  * @returns {edms} { <service>:<JSON representation>, ...}
489
489
  */
490
490
  function edmall(csn, options = {}) {
@@ -494,19 +494,17 @@ function edmall(csn, options = {}) {
494
494
  if (internalOptions.version === 'v2')
495
495
  error(null, null, 'OData JSON output is not available for OData V2');
496
496
 
497
- let servicesAll;
498
-
499
497
  const result = {};
498
+ let oDataCsn = csn;
500
499
 
501
- if (isPreTransformed(csn, 'odata')) {
500
+ if (isPreTransformed(csn, 'odata'))
502
501
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
503
- servicesAll = backends.preparedCsnToEdmAll(csn, internalOptions);
504
- }
505
- else {
506
- const oDataCsn = odataInternal(csn, internalOptions);
507
- servicesAll = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
508
- }
509
- 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;
510
508
  for (const serviceName in services) {
511
509
  const lEdm = services[serviceName];
512
510
  // FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
@@ -518,7 +516,7 @@ function edmall(csn, options = {}) {
518
516
  * Generate a edmx document for the given service
519
517
  *
520
518
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
521
- * @param {oDataOptions} [options={}] Options
519
+ * @param {ODataOptions} [options={}] Options
522
520
  * @returns {edmx} The XML representation of the service
523
521
  */
524
522
  function edmx(csn, options = {}) {
@@ -549,7 +547,7 @@ edmx.all = edmxall;
549
547
  * Generate edmx documents for all services
550
548
  *
551
549
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
552
- * @param {oDataOptions} [options={}] Options
550
+ * @param {ODataOptions} [options={}] Options
553
551
  * @returns {edmxs} { <service>:<XML representation>, ...}
554
552
  */
555
553
  function edmxall(csn, options = {}) {
@@ -595,41 +593,6 @@ function flattenResultStructure(toProcess) {
595
593
  return result;
596
594
  }
597
595
 
598
- /**
599
- * Compute the .hdbcds files that would have been generated by the csn for an undeploy.json.
600
- * This is needed for the handover between hdbcds and hdbtable - the existing hdbcds artifacts need to be undeployed.
601
- *
602
- * @param {any} csn A clean input CSN
603
- * @param {hdbcdsOptions} [options={}] Options
604
- * @returns {string[]} Array of .hdbcds filenames
605
- */
606
- function undeploy(csn, options) {
607
- const hdbcdsResult = hdbcds(csn, options);
608
-
609
- return Object.keys(hdbcdsResult);
610
- }
611
-
612
- /**
613
- * This function simply renders the given CSN to SQL - no integrity checks, no transformations, no guarantees for correctness.
614
- * Strictly for internal evaluation!
615
- *
616
- * @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.
617
- * @param {sqlOptions} [options={}] Options
618
- * @returns {SQL[]} Array of SQL statements, tables first, views second - the resulting statements might not be a consistent, deployable state!
619
- */
620
- function renderSQL(csn, options) {
621
- const internalOptions = prepareOptions.to.sql(options);
622
- if (!isBetaEnabled(internalOptions, 'renderSQL'))
623
- throw new Error('renderSQL is only available with beta-flag "renderSQL".');
624
-
625
-
626
- // Add flag so it thinks we ran through forHanaNew
627
- internalOptions.forHana = true;
628
-
629
- const sqlResult = toSqlDdl(csn, internalOptions);
630
- return Object.values(flattenResultStructure(sqlResult));
631
- }
632
-
633
596
  module.exports = {
634
597
  odata: publishCsnProcessor(odata, 'for.odata'),
635
598
  cdl: publishCsnProcessor(cdl, 'to.cdl'),
@@ -642,45 +605,10 @@ module.exports = {
642
605
  for_sql: publishCsnProcessor(forSql, 'for.sql'),
643
606
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
644
607
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
645
- renderSQL,
646
- undeploy,
647
608
  /** */
648
609
  };
649
610
 
650
611
 
651
- /**
652
- * Recompile the given CSN
653
- *
654
- * @param {object} csn Input CSN to recompile to XSN
655
- * @param {object} options Options
656
- * @returns {object} XSN
657
- *
658
- * TODO: move to lib/compiler/, consider new $recompile option, probaby issue
659
- * message api-recompiled-csn there.
660
- */
661
- function recompile(csn, options) {
662
- // TODO: is it really a good idea to set options in the input parameter?
663
- // (as long as we have the messages meddling of the option processor...)
664
- // OK, I try a copy
665
- options = { ...options };
666
- // Explicitly set parseCdl to false because backends cannot handle
667
- // the option and is only intended for CDL sources.
668
- options.parseCdl = false; // TODO: delete this option
669
- // Explicitly delete all toCsn options
670
- delete options.toCsn;
671
- /* eslint-disable global-require */
672
- const { augment } = require('../json/from-csn');
673
- const { compileSourcesX } = require('../compiler');
674
- /* eslint-enable global-require */
675
- const file = csn.$location && csn.$location.file &&
676
- csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
677
- const xsn = augment(csn, file, options); // in-place
678
- const compiled = compileSourcesX( { [file]: xsn }, { ...options, $recompile: true } );
679
- if (options.messages)
680
- deduplicateMessages(options.messages);
681
- return compiled;
682
- }
683
-
684
612
  /**
685
613
  * @param {any} processor CSN processor
686
614
  * @param {string} _name Name of the processor
@@ -720,7 +648,7 @@ function publishCsnProcessor( processor, _name ) {
720
648
  const { info } = makeMessageFunction( csn, options, 'compile' );
721
649
  info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
722
650
  // next line to be replaced by CSN parser call which reads the CSN object
723
- const xsn = recompile(csn, options);
651
+ const xsn = recompileX(csn, options);
724
652
  const recompiledCsn = compactModel(xsn);
725
653
  return processor( recompiledCsn, options, ...args );
726
654
  }
@@ -781,20 +709,6 @@ function publishCsnProcessor( processor, _name ) {
781
709
  * @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
782
710
  */
783
711
 
784
- /**
785
- * Options available for all oData-based functions
786
- *
787
- * @typedef {object} oDataOptions
788
- * @property {object} [beta] Enable experimental features - not for productive use!
789
- * @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
790
- * @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
791
- * @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
792
- * @property {oDataVersion} [odataVersion='v4'] Odata version to use
793
- * @property {oDataFormat} [odataFormat='flat'] Wether to generate oData as flat or as structured. Structured only with v4.
794
- * @property {NamingMode} [sqlMapping='plain'] Naming mode to use
795
- * @property {string} [service] If a single service is to be rendered
796
- */
797
-
798
712
  /**
799
713
  * Options available for to.hdi
800
714
  *
@@ -825,9 +739,9 @@ function publishCsnProcessor( processor, _name ) {
825
739
  * @typedef {object} sqlOptions
826
740
  * @property {NamingMode} [sqlMapping='plain'] Naming mode to use
827
741
  * @property {SQLDialect} [sqlDialect='sqlite'] SQL dialect to use
828
- * @property {object} [magicVars] Object containing values for magic variables like "$user"
829
- * @property {string} [magicVars.locale] Value for the "$user.locale" in "sqlite" dialect
830
- * @property {string} [magicVars.user] Value for the "$user" variable in "sqlite" dialect
742
+ * @property {object} [variableReplacements] Object containing values for magic variables like "$user"
743
+ * @property {string} [variableReplacements.$user.locale] Value for the "$user.locale" variable
744
+ * @property {string} [variableReplacements.$user.id] Value for the "$userid" variable
831
745
  * @property {object} [beta] Enable experimental features - not for productive use!
832
746
  * @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
833
747
  * @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
@@ -10,18 +10,22 @@ 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',
18
+ 'defaultBinaryLength',
17
19
  'defaultStringLength',
20
+ 'csnFlavor',
18
21
  // DB
19
22
  'sqlDialect',
20
23
  'sqlMapping',
21
24
  'sqlChangeMode',
22
25
  'allowCsnDowngrade',
23
26
  'joinfk',
24
- 'magicVars',
27
+ 'magicVars', // deprecated
28
+ 'variableReplacements',
25
29
  // ODATA
26
30
  'odataVersion',
27
31
  'odataFormat',
@@ -45,9 +49,10 @@ const privateOptions = [
45
49
  'traceParserAmb',
46
50
  'testMode',
47
51
  'testSortCsn',
48
- 'constraintsNotEnforced',
49
- 'constraintsNotValidated',
50
- 'skipDbConstraints',
52
+ 'integrityNotEnforced',
53
+ 'integrityNotValidated',
54
+ 'assertIntegrity',
55
+ 'assertIntegrityType',
51
56
  'noRecompile',
52
57
  'internalMsg',
53
58
  'disableHanaComments', // in case of issues with hana comment rendering
@@ -68,10 +73,11 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
68
73
  * @param {FlatOptions} [hardRequire={}] Hard requirements to enforce
69
74
  * @param {object} [customValidators] Custom validations to run instead of defaults
70
75
  * @param {string[]} [combinationValidators] Option combinations to validate
76
+ * @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
71
77
  * @returns {TranslatedOptions} General cds options
72
78
  */
73
79
  function translateOptions(input = {}, defaults = {}, hardRequire = {},
74
- customValidators = {}, combinationValidators = []) {
80
+ customValidators = {}, combinationValidators = [], moduleName = '') {
75
81
  const options = Object.assign({}, defaults);
76
82
  const inputOptionNames = Object.keys(input);
77
83
  for (const name of overallOptions) {
@@ -90,6 +96,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
90
96
  // Validate the filtered input options
91
97
  // only "new-style" options are here
92
98
  validate(options,
99
+ moduleName,
93
100
  // TODO: is there a better place to specify the type of option values?
94
101
  Object.assign( {
95
102
  localizedLanguageFallback: generateStringValidator([ 'none', 'coalesce' ]),
@@ -105,6 +112,10 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
105
112
  mapToOldNames(optionName, optionValue);
106
113
  }
107
114
 
115
+ // Convenience for $user -> $user.id replacement
116
+ if (options.variableReplacements && options.variableReplacements.$user && typeof options.variableReplacements.$user === 'string')
117
+ options.variableReplacements.$user = { id: options.variableReplacements.$user };
118
+
108
119
  /**
109
120
  * Map a new-style option to it's old format
110
121
  *
@@ -126,6 +137,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
126
137
  case 'sqlMapping':
127
138
  options.names = optionValue;
128
139
  break;
140
+ // No need to remap variableReplacements here - we use the new mechanism with that directly
129
141
  case 'magicVars':
130
142
  if (optionValue.user)
131
143
  options.user = optionValue.user;
@@ -141,11 +153,11 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
141
153
 
142
154
  module.exports = {
143
155
  to: {
144
- cdl: options => translateOptions(options),
156
+ cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
145
157
  sql: (options) => {
146
158
  const hardOptions = { src: 'sql' };
147
159
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
148
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ]);
160
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
149
161
 
150
162
  const result = Object.assign({}, processed);
151
163
  result.toSql = Object.assign({}, processed);
@@ -155,7 +167,7 @@ module.exports = {
155
167
  hdi: (options) => {
156
168
  const hardOptions = { src: 'hdi' };
157
169
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
158
- const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) });
170
+ const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
159
171
 
160
172
  const result = Object.assign({}, processed);
161
173
  result.toSql = Object.assign({}, processed);
@@ -164,17 +176,17 @@ module.exports = {
164
176
  },
165
177
  hdbcds: (options) => {
166
178
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
167
- const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) });
179
+ const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdbcds');
168
180
 
169
181
  const result = Object.assign({}, processed);
170
- result.toHana = Object.assign({}, processed);
182
+ result.forHana = Object.assign({}, processed);
171
183
 
172
184
  return result;
173
185
  },
174
186
  edm: (options) => {
175
187
  const hardOptions = { json: true, combined: true };
176
188
  const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
177
- const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ]);
189
+ const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ], 'to.edm');
178
190
 
179
191
  const result = Object.assign({}, processed);
180
192
  result.toOdata = Object.assign({}, processed);
@@ -186,7 +198,7 @@ module.exports = {
186
198
  const defaultOptions = {
187
199
  odataVersion: 'v4', odataFormat: 'flat',
188
200
  };
189
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ]);
201
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ], 'to.edmx');
190
202
 
191
203
  const result = Object.assign({}, processed);
192
204
  result.toOdata = Object.assign({}, processed);
@@ -198,7 +210,7 @@ module.exports = {
198
210
 
199
211
  odata: (options) => {
200
212
  const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
201
- const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ]);
213
+ const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ], 'for.odata');
202
214
 
203
215
  const result = Object.assign({}, processed);
204
216
  result.toOdata = Object.assign({}, processed);
@@ -208,10 +220,10 @@ module.exports = {
208
220
  },
209
221
  hana: (options) => {
210
222
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
211
- const processed = translateOptions(options, defaultOptions);
223
+ const processed = translateOptions(options, defaultOptions, undefined, undefined, undefined, 'for.hana');
212
224
 
213
225
  const result = Object.assign({}, processed);
214
- result.toHana = Object.assign({}, processed);
226
+ result.forHana = Object.assign({}, processed);
215
227
 
216
228
 
217
229
  return result;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { makeMessageFunction, handleMessages } = require('../base/messages');
3
+ const { makeMessageFunction } = require('../base/messages');
4
4
 
5
5
  /* eslint-disable arrow-body-style */
6
6
  const booleanValidator = {
@@ -70,6 +70,14 @@ const validators = {
70
70
  return val === null ? val : `type ${ typeof val }`;
71
71
  },
72
72
  },
73
+ // TODO: Maybe do a deep validation of the whole object with leafs?
74
+ variableReplacements: {
75
+ validate: val => val !== null && typeof val === 'object' && !Array.isArray(val),
76
+ expected: () => 'type object',
77
+ found: (val) => {
78
+ return val === null ? val : `type ${ typeof val }`;
79
+ },
80
+ },
73
81
  messages: {
74
82
  validate: val => Array.isArray(val),
75
83
  expected: () => 'type array',
@@ -89,14 +97,32 @@ const validators = {
89
97
  expected: () => 'type array of string',
90
98
  found: val => `type ${ typeof val }`,
91
99
  },
100
+ defaultBinaryLength: {
101
+ validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
102
+
103
+ expected: () => 'Integer literal',
104
+ found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
105
+ },
92
106
  defaultStringLength: {
93
- validate: val => Number.isInteger(val),
107
+ validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
108
+
94
109
  expected: () => 'Integer literal',
110
+ found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
111
+ },
112
+ csnFlavor: {
113
+ validate: val => typeof val === 'string',
114
+ expected: () => 'type string',
95
115
  found: val => `type ${ typeof val }`,
96
116
  },
97
117
  dictionaryPrototype: {
98
118
  validate: () => true,
99
119
  },
120
+ assertIntegrity: {
121
+ validate: val => typeof val === 'string' && val === 'individual' || typeof val === 'boolean',
122
+ expected: () => 'a boolean or a string with value \'individual\'',
123
+ found: val => (typeof val === 'string' ? val : `type ${ typeof val }`),
124
+ },
125
+ assertIntegrityType: generateStringValidator([ 'DB', 'RT' ]),
100
126
  };
101
127
 
102
128
  const allCombinationValidators = {
@@ -122,15 +148,17 @@ const allCombinationValidators = {
122
148
  * Use a custom validator or "default" custom validator, fallback to Boolean validator.
123
149
  *
124
150
  * @param {object} options Flat options object to validate
151
+ * @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
125
152
  * @param {object} [customValidators] Map of custom validators to use
126
153
  * @param {string[]} [combinationValidators] Validate option combinations
127
154
  * @returns {void}
128
155
  * @throws {CompilationError} Throws in case of invalid option usage
129
156
  */
130
- function validate(options, customValidators = {}, combinationValidators = []) {
157
+ function validate(options, moduleName, customValidators = {}, combinationValidators = []) {
158
+ // TODO: issuing messages in this function looks very strange...
131
159
  {
132
160
  const messageCollector = { messages: [] };
133
- const { error, throwWithError } = makeMessageFunction(null, messageCollector);
161
+ const { error, throwWithError } = makeMessageFunction(null, messageCollector, moduleName);
134
162
 
135
163
  for (const optionName of Object.keys(options)) {
136
164
  const optionValue = options[optionName];
@@ -142,7 +170,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
142
170
  throwWithError();
143
171
  }
144
172
 
145
- const message = makeMessageFunction(null, options);
173
+ const message = makeMessageFunction(null, options, moduleName);
146
174
 
147
175
  for (const combinationValidatorName of combinationValidators.concat([ 'beta-no-test' ])) {
148
176
  const combinationValidator = allCombinationValidators[combinationValidatorName];
@@ -150,10 +178,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
150
178
  message[combinationValidator.severity]('invalid-option-combination', null, {}, combinationValidator.getMessage(options));
151
179
  }
152
180
 
153
- // TODO: Replace with message.throwWithError():
154
- // But be aware that it only throws with non-configurable errors and that this
155
- // will lead to issues in test3. See #6037
156
- handleMessages(undefined, options);
181
+ message.throwWithError();
157
182
  }
158
183
  /* eslint-enable jsdoc/no-undefined-types */
159
184
 
package/lib/backends.js CHANGED
@@ -6,7 +6,6 @@
6
6
  const { transformForHanaWithCsn } = require('./transform/forHanaNew');
7
7
  const { compactModel, sortCsn } = require('./json/to-csn')
8
8
  const { toCdsSourceCsn } = require('./render/toCdl');
9
- const { toHdbcdsSource } = require('./render/toHdbcds');
10
9
  const { toSqlDdl } = require('./render/toSql');
11
10
  const { toRenameDdl } = require('./render/toRename');
12
11
  const { manageConstraints, listReferentialIntegrityViolations } = require('./render/manageConstraints');
@@ -15,89 +14,10 @@ const { csn2edm, csn2edmAll } = require('./edm/csn2edm');
15
14
  const { mergeOptions } = require('./model/csnUtils');
16
15
  const { isBetaEnabled } = require('./base/model');
17
16
  const { optionProcessor } = require('./optionProcessor')
18
- const timetrace = require('./utils/timetrace');
17
+ const { timetrace } = require('./utils/timetrace');
19
18
  const { makeMessageFunction } = require('./base/messages');
20
19
  const { forEachDefinition } = require('./model/csnUtils');
21
20
 
22
- /**
23
- * Transform a CSN into HANA-compatible CDS source.
24
- * The following options control what is actually generated (see help above):
25
- * options : {
26
- * toHana.names
27
- * toHana.src
28
- * toHana.csn
29
- * }
30
- * Options provided here are merged with (and take precedence over) options from 'model'.
31
- * If 'toHana.names' is not provided, 'quoted' is used.
32
- * If neither 'toHana.src' nor 'toHana.csn' are provided, the default is to generate only HANA CDS
33
- * source files.
34
- * If all provided options are part of 'toHana', the 'toHana' wrapper can be omitted.
35
- * The result object contains the generation results as follows (as enabled in 'options'):
36
- * result : {
37
- * csn : the (compact) transformed CSN model
38
- * hdbcds : a dictionary of top-level artifact names, containing for each name 'X':
39
- * <X> : the HANA CDS source string of the artifact 'X'. Please note that the
40
- * name of 'X' may contain characters that are not legal for filenames on
41
- * all operating systems (e.g. ':', '\' or '/').
42
- * X reflects the naming policy set by toHana.names
43
- * }
44
- * Throws a CompilationError on errors.
45
- *
46
- * @param {CSN.Model} csn
47
- * @param {CSN.Options} [options]
48
- */
49
- function toHanaWithCsn(csn, options) {
50
- timetrace.start('toHanaWithCsn');
51
- // In case of API usage the options are in the 'options' argument
52
- // put the OData specific options under the 'options.toHana' wrapper
53
- // and leave the rest under 'options'
54
- if (options && !options.toHana) {
55
- _wrapRelevantOptionsForCmd(options, 'toHana');
56
- }
57
- // Provide defaults and merge options with those from model
58
- options = mergeOptions({ toHana : getDefaultBackendOptions().toHana }, options);
59
-
60
- // Provide something to generate if nothing else was given (conditional default)
61
- if (!options.toHana.src && !options.toHana.csn) {
62
- options.toHana.src = true;
63
- }
64
-
65
- const { warning } = makeMessageFunction(csn, options, 'to.hdbcds');
66
-
67
- // Verify options
68
- optionProcessor.verifyOptions(options, 'toHana', true).forEach(complaint => warning(null, null, `${complaint}`));
69
-
70
- // Special case: For naming variant 'hdbcds' in combination with 'toHana' (and only there!), 'forHana'
71
- // must leave namespaces, structs and associations alone.
72
- if (options.toHana.names === 'hdbcds') {
73
- options = mergeOptions(options, { forHana : { keepNamespaces: true, keepStructsAssocs: true } });
74
- }
75
-
76
- options = mergeOptions(options, { forHana: { dialect: 'hana' } }, { forHana : options.toHana });
77
-
78
- // Prepare model for HANA (transferring the options to forHana, and setting 'dialect' to 'hana', because 'toHana' is only used for that)
79
- let forHanaCsn = transformForHanaWithCsn(csn, options, 'to.hdbcds');
80
-
81
- // Assemble result
82
- let result = {};
83
-
84
- if (options.toHana.src) {
85
- if(options.testMode){
86
- const sorted = sortCsn(forHanaCsn, options);
87
- result = toHdbcdsSource(sorted, options);
88
- } else {
89
- result = toHdbcdsSource(forHanaCsn, options);
90
- }
91
- }
92
-
93
- if (options.toHana.csn) {
94
- result.csn = options.testMode ? sortCsn(forHanaCsn, options) : forHanaCsn;
95
- }
96
-
97
- timetrace.stop();
98
- return result;
99
- }
100
-
101
21
  /**
102
22
  * Generate ODATA for `csn` using `options`.
103
23
  * The twin of the toOdata function but using CSN
@@ -172,9 +92,9 @@ function toOdataWithCsn(csn, options) {
172
92
  // Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
173
93
  // using 'options'
174
94
  function preparedCsnToEdmx(csn, service, options) {
175
- let edmx = csn2edm(csn, service, options).toXML('all');
95
+ const e = csn2edm(csn, service, options)
176
96
  return {
177
- edmx,
97
+ edmx: (e ? e.toXML('all') : undefined)
178
98
  };
179
99
  }
180
100
 
@@ -195,9 +115,9 @@ function preparedCsnToEdmxAll(csn, options) {
195
115
  function preparedCsnToEdm(csn, service, options) {
196
116
  // Merge options; override OData version as edm json is always v4
197
117
  options = mergeOptions(options, { toOdata : { version : 'v4' }});
198
- const edmj = csn2edm(csn, service, options).toJSON();
118
+ const e = csn2edm(csn, service, options);
199
119
  return {
200
- edmj,
120
+ edmj: (e ? e.toJSON() : undefined)
201
121
  };
202
122
  }
203
123
 
@@ -485,9 +405,6 @@ function toRenameWithCsn(csn, options) {
485
405
 
486
406
  function alterConstraintsWithCsn(csn, options) {
487
407
  const { error } = makeMessageFunction(csn, options, 'manageConstraints');
488
- // Requires beta mode
489
- if (!isBetaEnabled(options, 'foreignKeyConstraints'))
490
- error(null, null, 'ALTER TABLE statements for adding/modifying referential constraints are only available in beta mode');
491
408
 
492
409
  const {
493
410
  drop, alter, names, src, violations
@@ -500,6 +417,10 @@ function alterConstraintsWithCsn(csn, options) {
500
417
  dialect: 'hana',
501
418
  names: names || 'plain'
502
419
  }
420
+
421
+ // Of course we want the database constraints
422
+ options.assertIntegrityType = 'DB';
423
+
503
424
  const transformedOptions = transformSQLOptions(csn, options);
504
425
  const mergedOptions = mergeOptions(transformedOptions.options, { forHana : transformedOptions.forHanaOptions });
505
426
  const forSqlCsn = transformForHanaWithCsn(csn, mergedOptions, 'to.sql');
@@ -603,7 +524,6 @@ function _wrapRelevantOptionsForCmd(options, command) {
603
524
  }
604
525
 
605
526
  module.exports = {
606
- toHanaWithCsn,
607
527
  toOdataWithCsn,
608
528
  preparedCsnToEdmx,
609
529
  preparedCsnToEdmxAll,