@sap/cds-compiler 5.4.4 → 5.5.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 (40) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/bin/cds_remove_invalid_whitespace.js +4 -4
  3. package/bin/cds_update_annotations.js +3 -3
  4. package/bin/cds_update_identifiers.js +3 -3
  5. package/lib/api/main.js +18 -30
  6. package/lib/api/validate.js +6 -1
  7. package/lib/base/lazyload.js +28 -0
  8. package/lib/base/location.js +1 -0
  9. package/lib/base/message-registry.js +47 -11
  10. package/lib/base/messages.js +17 -3
  11. package/lib/checks/cdsMap.js +27 -0
  12. package/lib/checks/{dbFeatureFlags.js → featureFlags.js} +1 -1
  13. package/lib/checks/parameters.js +61 -4
  14. package/lib/checks/validator.js +17 -7
  15. package/lib/compiler/define.js +1 -0
  16. package/lib/compiler/index.js +7 -7
  17. package/lib/gen/BaseParser.js +345 -235
  18. package/lib/gen/CdlParser.js +4438 -4492
  19. package/lib/gen/Dictionary.json +2 -2
  20. package/lib/language/antlrParser.js +2 -111
  21. package/lib/main.js +16 -37
  22. package/lib/model/cloneCsn.js +1 -5
  23. package/lib/modelCompare/utils/filter.js +47 -21
  24. package/lib/parsers/AstBuildingParser.js +92 -73
  25. package/lib/parsers/CdlGrammar.g4 +110 -137
  26. package/lib/parsers/index.js +123 -0
  27. package/lib/render/toSql.js +8 -2
  28. package/lib/render/utils/delta.js +33 -1
  29. package/lib/transform/db/{transformExists.js → assocsToQueries/transformExists.js} +12 -407
  30. package/lib/transform/db/assocsToQueries/utils.js +440 -0
  31. package/lib/transform/db/expansion.js +2 -2
  32. package/lib/transform/draft/db.js +14 -3
  33. package/lib/transform/effective/annotations.js +3 -3
  34. package/lib/transform/effective/main.js +5 -7
  35. package/lib/transform/featureFlags.js +5 -0
  36. package/lib/transform/forRelationalDB.js +125 -192
  37. package/lib/transform/transformUtils.js +0 -51
  38. package/lib/utils/objectUtils.js +13 -0
  39. package/package.json +2 -2
  40. package/lib/transform/db/featureFlags.js +0 -5
package/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 5.5.2 - 2024-12-02
11
+
12
+ ### Fixed
13
+
14
+ - to.hdi|sql|edm[x]: Correctly handle `cds.Map`, ensure that it does not have `.elements` yet.
15
+
16
+ ## Version 5.5.0 - 2024-11-22
17
+
18
+ ### Added
19
+
20
+ - CDL parser: when the new experimental option `newParser` is used, the compiler
21
+ uses a CDL parser with a significantly smaller footprint (among other things).
22
+ - to.sql|hdi.migration: For SAP HANA, render `ALTER` statements as one big statement to improve performance.
23
+ - to.sql.migration: Give more helpful comments when using option `script`.
24
+
25
+ ### Changed
26
+
27
+ - Update OData vocabularies: 'Common', 'PersonalData', 'UI'.
28
+
10
29
  ## Version 5.4.4 - 2024-11-14
11
30
 
12
31
  ### Fixed
@@ -14,18 +33,20 @@ The compiler behavior concerning `beta` features can change at any time without
14
33
  - Re-allow referring to mixins (and table aliases) in added columns
15
34
  - Re-add foreign keys of named aspects to the OData CSN.
16
35
 
36
+
17
37
  ## Version 5.4.2 - 2024-11-06
18
38
 
19
39
  ### Fixed
20
40
 
21
41
  - to.sql: For SQLite, map `cds.Map` to `JSON_TEXT` to ensure text affinity.
22
42
 
43
+
23
44
  ## Version 5.4.0 - 2024-10-24
24
45
 
25
46
  ### Added
26
47
 
27
48
  - to.edm(x): `cds.Map` as empty open complex type with name `cds_Map` or if the definition
28
- has been assigned `@open: false` as empty open complex type `cds_Map_closed` in OData V4.
49
+ has been assigned `@open: false` as empty closed complex type `cds_Map_closed` in OData V4.
29
50
 
30
51
  ### Changed
31
52
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // Remove whitespace where the compiler does not expect it and will
4
- // emit an error in the next major version.
4
+ // emit an error in the next major version. (Remark: not necessarily)
5
5
  //
6
6
  // This script removes whitespace where necessary.
7
7
  // It does not remove comments, even though they count as whitespace.
@@ -23,7 +23,7 @@
23
23
 
24
24
  'use strict';
25
25
 
26
- const parseLanguage = require('../lib/language/antlrParser');
26
+ const parsers = require('../lib/parsers');
27
27
  const { createMessageFunctions } = require('../lib/base/messages');
28
28
 
29
29
  const fs = require('fs');
@@ -58,9 +58,9 @@ function modernizeWhitespace( source, filename ) {
58
58
  const options = { messages: [], attachTokens: true };
59
59
  const messageFunctions = createMessageFunctions( options, 'parse', null );
60
60
 
61
- // parseLanguage does not throw on CompilationError, so
61
+ // parseCdl does not throw on CompilationError, so
62
62
  // we do not need a try...catch block.
63
- const ast = parseLanguage(source, filename, options, messageFunctions);
63
+ const ast = parsers.parseCdl(source, filename, options, messageFunctions);
64
64
 
65
65
  // To avoid spam, only report errors.
66
66
  // Users should use the compiler to get all messages.
@@ -22,7 +22,7 @@
22
22
 
23
23
  'use strict';
24
24
 
25
- const parseLanguage = require('../lib/language/antlrParser');
25
+ const parsers = require('../lib/parsers');
26
26
  const { createMessageFunctions } = require('../lib/base/messages');
27
27
 
28
28
  const fs = require('fs');
@@ -58,9 +58,9 @@ function modernizeAnnotationExpressions( source, filename ) {
58
58
  const options = { messages: [], attachTokens: true };
59
59
  const messageFunctions = createMessageFunctions( options, 'parse', null );
60
60
 
61
- // parseLanguage does not throw on CompilationError, so
61
+ // parseCdl does not throw on CompilationError, so
62
62
  // we do not need a try...catch block.
63
- const ast = parseLanguage(source, filename, options, messageFunctions);
63
+ const ast = parsers.parseCdl(source, filename, options, messageFunctions);
64
64
 
65
65
  // To avoid spam, only report errors.
66
66
  // Users should use the compiler to get all messages.
@@ -22,7 +22,7 @@
22
22
 
23
23
  'use strict';
24
24
 
25
- const parseLanguage = require('../lib/language/antlrParser');
25
+ const parsers = require('../lib/parsers');
26
26
  const { createMessageFunctions } = require('../lib/base/messages');
27
27
 
28
28
  const fs = require('fs');
@@ -56,9 +56,9 @@ function modernizeIdentifierStyle( source, filename ) {
56
56
  const options = { messages: [], attachTokens: true };
57
57
  const messageFunctions = createMessageFunctions( options, 'parse', null );
58
58
 
59
- // parseLanguage does not throw on CompilationError, so
59
+ // parseCdl does not throw on CompilationError, so
60
60
  // we do not need a try...catch block.
61
- const ast = parseLanguage(source, filename, options, messageFunctions);
61
+ const ast = parsers.parseCdl(source, filename, options, messageFunctions);
62
62
 
63
63
  // To avoid spam, only report errors.
64
64
  // Users should use the compiler to get all messages.
package/lib/api/main.js CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  'use strict';
4
4
 
5
+ const lazyload = require('../base/lazyload')( module );
6
+
5
7
  const prepareOptions = lazyload('./options');
6
8
  const baseModel = lazyload('../base/model');
7
9
  const location = lazyload('../base/location');
@@ -470,18 +472,29 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
470
472
  const internalOptions = prepareOptions.to.sql(options);
471
473
 
472
474
  messageFunctions.setOptions( internalOptions );
475
+ if (internalOptions.script)
476
+ messageFunctions.setModuleName( `${ messageFunctions.moduleName }-script` );
477
+
473
478
  handleTenantDiscriminator(options, internalOptions, messageFunctions);
474
479
  if (!options.dry && internalOptions.script) {
475
480
  messageFunctions.error('api-invalid-combination', null, { '#': 'dry-and-script', value: options.dry || 'undefined' });
476
481
  messageFunctions.throwWithError();
477
482
  }
478
483
 
479
- if (internalOptions.script && !internalOptions.severities?.['type-unsupported-key-change']) {
480
- internalOptions.severities ??= {};
481
- internalOptions.severities['type-unsupported-key-change'] = 'warning';
484
+ if (internalOptions.script && !internalOptions.severities?.['migration-unsupported-key-change']) {
485
+ internalOptions.severities = Object.assign({}, internalOptions.severities ?? {});
486
+ internalOptions.severities['migration-unsupported-key-change'] = 'Warning';
487
+ }
488
+
489
+ if (internalOptions.script) {
490
+ internalOptions.severities = Object.assign({}, internalOptions.severities ?? {});
491
+ const turnToWarning = [ 'migration-unsupported-element-drop', 'migration-unsupported-length-change', 'migration-unsupported-scale-change', 'migration-unsupported-precision-change', 'migration-unsupported-change', 'migration-unsupported-table-drop' ];
492
+ turnToWarning.forEach((id) => {
493
+ internalOptions.severities[id] = 'Warning';
494
+ });
482
495
  }
483
496
 
484
- const { error, throwWithError } = messageFunctions;
497
+ const { throwWithError } = messageFunctions;
485
498
 
486
499
  // Prepare after-image.
487
500
  let afterImage = csnForSql(csn, internalOptions, messageFunctions);
@@ -495,7 +508,7 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
495
508
  if (diffFilterObj) {
496
509
  diff.extensions = diff.extensions.filter(ex => diffFilterObj.extension(ex, messageFunctions));
497
510
  diff.migrations.forEach(migration => diffFilterObj.migration(migration, messageFunctions));
498
- Object.entries(diff.deletions).forEach(entry => diffFilterObj.deletion(entry, error));
511
+ Object.entries(diff.deletions).forEach(entry => diffFilterObj.deletion(entry, messageFunctions));
499
512
  diff.changedPrimaryKeys = diff.changedPrimaryKeys
500
513
  .filter(an => diffFilterObj.changedPrimaryKeys(an));
501
514
 
@@ -1271,31 +1284,6 @@ function ensureClientCsn( csn, options, messageFunctions, module ) {
1271
1284
  return csn;
1272
1285
  }
1273
1286
 
1274
- /**
1275
- * Load the module on-demand and not immediately.
1276
- *
1277
- * @param {string} moduleName Name of the module to load - like with require
1278
- * @returns {object} A Proxy that handles the on-demand loading
1279
- */
1280
- function lazyload( moduleName ) {
1281
- let module;
1282
- return new Proxy(((...args) => {
1283
- if (!module)
1284
- module = require(moduleName);
1285
-
1286
- if (module.apply && typeof module.apply === 'function')
1287
- return module.apply(this, args);
1288
- return module; // for destructured calls
1289
- }), {
1290
- get(target, name) {
1291
- if (!module)
1292
- module = require(moduleName);
1293
-
1294
- return module[name];
1295
- },
1296
- });
1297
- }
1298
-
1299
1287
  /**
1300
1288
  * Error when tenantDiscriminator and withHanaAssociations is set by the user, or
1301
1289
  * if tenantDiscriminator is used with anything but "plain" mode.
@@ -58,7 +58,12 @@ const validators = {
58
58
  },
59
59
  },
60
60
  severities: {
61
- validate: val => val !== null && typeof val === 'object' && !Array.isArray(val),
61
+ validate: (val) => {
62
+ if (val !== null && typeof val === 'object' && !Array.isArray(val))
63
+ return true;
64
+
65
+ return false;
66
+ },
62
67
  expected: () => 'type object',
63
68
  found: (val) => {
64
69
  return val === null ? val : `type ${ typeof val }`;
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ function createLazyload( callingModule ) {
4
+ /**
5
+ * Load the module on-demand and not immediately.
6
+ *
7
+ * @param {string} moduleName Name of the module to load - like with require
8
+ * @returns {object} A Proxy that handles the on-demand loading
9
+ */
10
+ return function lazyload( moduleName ) {
11
+ let module;
12
+ return new Proxy(((...args) => {
13
+ if (!module)
14
+ module = callingModule.require(moduleName);
15
+ if (module.apply && typeof module.apply === 'function')
16
+ return module.apply(this, args);
17
+ return module; // for destructured calls
18
+ }), {
19
+ get(target, name) {
20
+ if (!module)
21
+ module = callingModule.require(moduleName);
22
+ return module[name];
23
+ },
24
+ });
25
+ }
26
+ }
27
+
28
+ module.exports = createLazyload;
@@ -11,6 +11,7 @@ class Location {
11
11
  col;
12
12
  endLine;
13
13
  endCol;
14
+ tokenIndex;
14
15
  constructor( file, line, col, endLine, endCol ) {
15
16
  this.file = file;
16
17
  this.line = line;
@@ -154,7 +154,8 @@ const centralMessages = {
154
154
  // then either issue an error or produce a CSN missing some annotations):
155
155
  'syntax-duplicate-annotate': { severity: 'Error' },
156
156
  'syntax-duplicate-clause': { severity: 'Error', configurableFor: true },
157
- 'syntax-duplicate-equal-clause': { severity: 'Warning' },
157
+ // remark: a hard syntax error in new parser for `null` together with `not null`
158
+ 'syntax-duplicate-equal-clause': { severity: 'Warning', errorFor: [ 'v6' ] },
158
159
  'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
159
160
  'syntax-missing-as': { severity: 'Error', configurableFor: true },
160
161
  'syntax-missing-proj-semicolon': { severity: 'Warning', errorFor: [ 'v6' ] },
@@ -168,9 +169,7 @@ const centralMessages = {
168
169
  'syntax-invalid-space': { severity: 'Error', configurableFor: 'test' },
169
170
  'syntax-expecting-space': { severity: 'Error' },
170
171
  'syntax-unexpected-anno': { severity: 'Error' },
171
-
172
- 'type-unsupported-precision-change': { severity: 'Error' },
173
- 'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
172
+ 'migration-unsupported-key-change': { severity: 'Error', configurableFor: [ 'to.sql.migration', 'to.sql.migration-script' ] },
174
173
  'type-missing-enum-value': { severity: 'Error', configurableFor: 'test' },
175
174
 
176
175
  'def-missing-element': { severity: 'Error' },
@@ -204,7 +203,15 @@ const centralMessages = {
204
203
  'odata-anno-value': { severity: 'Warning' },
205
204
  'odata-anno-type': { severity: 'Warning' },
206
205
  'odata-anno-def': { severity: 'Info' },
207
- 'query-ignoring-assoc-in-union': { severity: 'Info' }
206
+ 'query-ignoring-assoc-in-union': { severity: 'Info' },
207
+ // for to.sql.migration - cannot be supplied by the user!
208
+ 'migration-unsupported-precision-change': { severity: 'Error', configurableFor: [ 'to.sql.migration-script'] },
209
+ 'migration-unsupported-element-drop': { severity: 'Error', configurableFor: [ 'to.sql.migration-script'] },
210
+ 'migration-unsupported-length-change': { severity: 'Error', configurableFor: [ 'to.sql.migration-script'] },
211
+ 'migration-unsupported-scale-change': { severity: 'Error', configurableFor: [ 'to.sql.migration-script'] },
212
+ 'migration-unsupported-change': { severity: 'Error', configurableFor: [ 'to.sql.migration-script'] },
213
+ 'migration-unsupported-table-drop': { severity: 'Error', configurableFor: [ 'to.sql.migration-script'] }
214
+ // end of to.sql.migration specific
208
215
  };
209
216
 
210
217
  // Old/Deprecated message IDs that we only still use for backwards-compatibility.
@@ -416,7 +423,7 @@ const centralMessageTexts = {
416
423
  'syntax-duplicate-equal-clause': {
417
424
  std: 'You have already provided the same clause',
418
425
  cardinality: 'You have already provided the target cardinality $(CODE) at line $(LINE), column $(COL)',
419
- notNull: 'You have already provided $(CODE) at line $(LINE), column $(COL) below',
426
+ notNull: 'You have already provided $(CODE) at line $(LINE), column $(COL)',
420
427
  },
421
428
  'syntax-duplicate-extend': {
422
429
  std: 'You can\'t define and refer to $(NAME) repeatedly in the same extend statement',
@@ -1051,11 +1058,6 @@ const centralMessageTexts = {
1051
1058
  entity: 'Entity $(ART) with managed compositions can\'t be used in types', // yet
1052
1059
  },
1053
1060
 
1054
- 'type-unsupported-key-change': {
1055
- std: 'Added element $(ID) is a primary key change and will not work if the table contains data',
1056
- changed: 'Changed element $(ID) is a primary key change and will not work if the table contains data'
1057
- },
1058
-
1059
1061
  'type-unsupported-key-sqlite': {
1060
1062
  std: 'Added element $(ID) is a primary key change and will not work with dialect $(NAME)',
1061
1063
  changed: 'Changed element $(ID) is a primary key change and will not work with dialect $(NAME)'
@@ -1273,6 +1275,40 @@ const centralMessageTexts = {
1273
1275
  // -----------------------------------------------------------------------------------
1274
1276
  // OData Message section ends here, no messages below this line
1275
1277
  // -----------------------------------------------------------------------------------
1278
+ // -----------------------------------------------------------------------------------
1279
+ // to.sql.migration specific error messages
1280
+ // -----------------------------------------------------------------------------------
1281
+ 'migration-unsupported-key-change': {
1282
+ std: 'Added element $(ID) is a primary key change and will not work if the table contains data',
1283
+ changed: 'Changed element $(ID) is a primary key change and will not work if the table contains data'
1284
+ },
1285
+ 'migration-unsupported-precision-change': {
1286
+ std: 'Changed element $(ID) is a precision change and is not supported',
1287
+ script: 'Changed element $(ID) is a precision change and might lead to data loss'
1288
+ },
1289
+ 'migration-unsupported-element-drop': {
1290
+ std: 'Dropping elements is not supported',
1291
+ script: 'Dropping elements leads to data loss'
1292
+ },
1293
+ 'migration-unsupported-length-change': {
1294
+ std: 'Changed element $(ID) is a length reduction and is not supported',
1295
+ script: 'Changed element $(ID) is a length reduction and might lead to data loss'
1296
+ },
1297
+ 'migration-unsupported-scale-change': {
1298
+ std: 'Changed element $(ID) is a scale change and is not supported',
1299
+ script: 'Changed element $(ID) is a scale change and might lead to data loss'
1300
+ },
1301
+ 'migration-unsupported-change': {
1302
+ std: 'Changed element $(ID) is a lossy type change from $(NAME) to $(TYPE) and is not supported',
1303
+ script: 'Changed element $(ID) is a lossy type change from $(NAME) to $(TYPE) and might lead to data loss'
1304
+ },
1305
+ 'migration-unsupported-table-drop': {
1306
+ std: 'Dropping tables is not supported',
1307
+ script: 'Dropping tables leads to data loss'
1308
+ },
1309
+ // -----------------------------------------------------------------------------------
1310
+ // to.sql.migration specific error messages end here
1311
+ // -----------------------------------------------------------------------------------
1276
1312
  }
1277
1313
 
1278
1314
  /**
@@ -357,9 +357,11 @@ function createMessageFunctions( options, moduleName, model = null ) {
357
357
  * ```
358
358
  * @param {object} model
359
359
  * @param {CSN.Options} [options]
360
- * @param {string|null} [moduleName]
360
+ * @param {string|null} [_moduleName]
361
361
  */
362
- function makeMessageFunction( model, options, moduleName = null ) {
362
+ function makeMessageFunction( model, options, _moduleName = null ) {
363
+ let moduleName = _moduleName;
364
+
363
365
  if (options.testMode) {
364
366
  // ensure message consistency during runtime with --test-mode
365
367
  _check$Init( options );
@@ -395,6 +397,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
395
397
  callTransparently,
396
398
  moduleName,
397
399
  setModel,
400
+ setModuleName,
398
401
  setOptions,
399
402
  };
400
403
 
@@ -631,11 +634,22 @@ function makeMessageFunction( model, options, moduleName = null ) {
631
634
  model = _model;
632
635
  }
633
636
 
637
+ /**
638
+ *
639
+ * Change the moduleName used for reclassifying messages.
640
+ * Needed for to.sql.migration + script
641
+ *
642
+ * @param {string} __moduleName
643
+ */
644
+ function setModuleName( __moduleName ) {
645
+ moduleName = __moduleName;
646
+ }
647
+
634
648
  /**
635
649
  * Change the options used to determine message severities.
636
650
  * This is necessary if you change `options.severities`, as otherwise they may not be picked up.
637
651
  *
638
- * @param {CSN.Model} _model
652
+ * @param {CSN.Options} _options
639
653
  */
640
654
  function setOptions( _options ) {
641
655
  options = _options;
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const { hasNonEnumerable } = require('../utils/objectUtils');
4
+
5
+ /**
6
+ * We don't support cds.Map in conjunction with .elements yet. To ensure that no one uses it and accidentally creates an
7
+ * empty structured type, we check for it and forbid it.
8
+ *
9
+ * Non-enumerable .elements are added by cds.linked - we silently remove them and proceed as usual.
10
+ *
11
+ * @param {*} parent
12
+ * @param {*} prop
13
+ * @param {*} type
14
+ * @param {*} path
15
+ */
16
+ function checkCdsMap( parent, prop, type, path ) {
17
+ if (type === 'cds.Map' && parent.elements) {
18
+ if (hasNonEnumerable(parent, 'elements'))
19
+ delete parent.elements; // linked CSN sets a non-enumerable empty elements on cds.Map
20
+ else
21
+ this.error('type-unexpected-elements-for-map', path, { id: path.at(-1), type: 'cds.Map' }, 'Unexpected .elements for element $(ID) of type $(TYPE)');
22
+ }
23
+ }
24
+
25
+ module.exports = {
26
+ type: checkCdsMap,
27
+ };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { setProp } = require('../base/model');
4
- const { featureFlags } = require('../transform/db/featureFlags');
4
+ const { featureFlags } = require('../transform/featureFlags');
5
5
  const { isSqlService } = require('../transform/db/processSqlServices');
6
6
 
7
7
  /**
@@ -12,12 +12,69 @@ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
12
12
  */
13
13
  function checkForParams( parent, name, params, path ) {
14
14
  const artifact = this.csn.definitions[path[1]];
15
- if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && parent.kind === 'entity') {
16
- this.error('ref-unexpected-params', [ ...path, 'params' ], { value: this.options.sqlDialect },
17
- 'Parameterized views can\'t be used with sqlDialect $(VALUE)');
15
+ if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && artifact['@cds.persistence.exists'] !== true && parent.kind === 'entity') {
16
+ if (artifact.query || artifact.projection) {
17
+ if (this.options.sqlDialect === 'hana') {
18
+ for (const pname in artifact.params) {
19
+ if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
20
+ this.warning(null, [ ...path, 'params', pname ], 'Expecting regular SQL-Identifier');
21
+ }
22
+ else if (this.options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
23
+ this.warning(null, [ ...path, 'params', pname ], { name: this.options.sqlMapping },
24
+ 'Expecting parameter to be uppercase in naming mode $(NAME)');
25
+ }
26
+ }
27
+ }
28
+ else {
29
+ this.error('ref-unexpected-params', [ ...path, 'params' ], { value: this.options.sqlDialect },
30
+ 'Parameterized views can\'t be used with sqlDialect $(VALUE)');
31
+ }
32
+ }
33
+ else {
34
+ this.error(null, path, { '#': this.options.toSql ? 'sql' : 'std' }, {
35
+ std: 'Table-like entities with parameters are not supported for conversion to SAP HANA CDS',
36
+ sql: 'Table-like entities with parameters are not supported for conversion to SQL',
37
+ });
38
+ }
39
+ }
40
+ }
41
+
42
+ function checkAssocsWithParams( member, memberName, prop, path ) {
43
+ // Report an error on
44
+ // - view with parameters that has an element of type association/composition
45
+ // - association that points to entity with parameters
46
+ if (member.target && this.csnUtils.isAssocOrComposition(member)) {
47
+ if (this.artifact.params) {
48
+ // HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
49
+ // SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
50
+ this.message('def-unexpected-paramview-assoc', path, { '#': 'source' });
51
+ }
52
+ else if (this.artifact['@cds.persistence.udf'] || this.artifact['@cds.persistence.calcview']) {
53
+ // UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
54
+ const anno = this.artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
55
+ this.message('def-unexpected-calcview-assoc', path, { '#': 'source', anno });
56
+ }
57
+ if (this.csn.definitions[member.target].params) {
58
+ // HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
59
+ // SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
60
+ this.message('def-unexpected-paramview-assoc', path, { '#': 'target' });
61
+ }
62
+ else if (this.csn.definitions[member.target]['@cds.persistence.udf'] || this.csn.definitions[member.target]['@cds.persistence.calcview']) {
63
+ // HANA won't check the assoc target but when querying an association with target UDF, this is the error:
64
+ // SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
65
+ // CREATE TABLE F (id INTEGER NOT NULL);
66
+ // CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
67
+ // CREATE TABLE Y ( id INTEGER NOT NULL, toUDF_id INTEGER) WITH ASSOCIATIONS (MANY TO ONE JOIN UDF AS toUDF ON (toUDF.id = toUDF_id));
68
+ // CREATE VIEW U AS SELECT id, toUDF.a FROM Y;
69
+ const anno = this.csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
70
+ this.message('def-unexpected-calcview-assoc', path, { '#': 'target', anno });
71
+ }
18
72
  }
19
73
  }
20
74
 
21
75
  module.exports = {
22
- params: checkForParams,
76
+ csnValidator: {
77
+ params: checkForParams,
78
+ },
79
+ memberValidator: checkAssocsWithParams,
23
80
  };
@@ -26,6 +26,7 @@ const {
26
26
  checkTemporalAnnotationsAssignment,
27
27
  } = require('./annotationsOData');
28
28
  // both
29
+ const checkCdsMap = require('./cdsMap');
29
30
  const { validateOnCondition, validateMixinOnCondition } = require('./onConditions');
30
31
  const validateForeignKeys = require('./foreignKeys');
31
32
  const {
@@ -49,7 +50,7 @@ const {
49
50
  checkSqlAnnotationOnArtifact,
50
51
  checkSqlAnnotationOnElement,
51
52
  } = require('./sql-snippets');
52
- const dbFeatureFlags = require('./dbFeatureFlags');
53
+ const featureFlags = require('./featureFlags');
53
54
 
54
55
  const forRelationalDBMemberValidators
55
56
  = [
@@ -80,12 +81,13 @@ const forRelationalDBArtifactValidators = [
80
81
  ];
81
82
 
82
83
  const forRelationalDBCsnValidators = [
84
+ checkCdsMap,
83
85
  existsMustEndInAssoc,
84
86
  forbidAssocInExists,
85
87
  nonexpandableStructuredInExpression,
86
88
  navigationIntoMany,
87
89
  checkPathsInStoredCalcElement,
88
- dbFeatureFlags,
90
+ featureFlags,
89
91
  ];
90
92
  /**
91
93
  * @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
@@ -118,7 +120,7 @@ const forOdataArtifactValidators
118
120
  checkReadOnlyAndInsertOnly,
119
121
  ];
120
122
 
121
- const forOdataCsnValidators = [ nonexpandableStructuredInExpression ];
123
+ const forOdataCsnValidators = [ checkCdsMap, nonexpandableStructuredInExpression ];
122
124
 
123
125
  const forOdataQueryValidators = [];
124
126
 
@@ -188,10 +190,14 @@ function _validate( csn, that,
188
190
  * @returns {any[]} Array of validator functions (or objects?)
189
191
  */
190
192
  function getDBCsnValidators( options ) {
191
- if (options.sqlDialect === 'postgres' || options.sqlDialect === 'h2')
192
- return [ ...forRelationalDBCsnValidators, checkForHanaTypes, checkForParams ];
193
+ const validations = [ ...forRelationalDBCsnValidators ];
193
194
 
194
- return forRelationalDBCsnValidators;
195
+ if (options.transformation !== 'effective')
196
+ validations.push(checkForParams.csnValidator);
197
+ if (options.sqlDialect === 'h2' || options.sqlDialect === 'postgres')
198
+ validations.push(checkForHanaTypes);
199
+
200
+ return validations;
195
201
  }
196
202
 
197
203
  /**
@@ -200,9 +206,13 @@ function getDBCsnValidators( options ) {
200
206
  * @returns {Function} the validator function with the respective checks for the HANA backend
201
207
  */
202
208
  function forRelationalDB( csn, that ) {
209
+ const memberValidators = [ ...forRelationalDBMemberValidators, ...commonMemberValidators ];
210
+ if (that.options.transformation === 'hdbcds')
211
+ memberValidators.push(checkForParams.memberValidator);
212
+
203
213
  return _validate(csn, that,
204
214
  getDBCsnValidators(that.options),
205
- forRelationalDBMemberValidators.concat(commonMemberValidators),
215
+ memberValidators,
206
216
  forRelationalDBArtifactValidators.concat(commonArtifactValidators).concat(
207
217
  // why is this hana exclusive
208
218
  (artifact) => {
@@ -917,6 +917,7 @@ function define( model ) {
917
917
  else {
918
918
  // a late syntax error (this code also runs with parse-cdl), i.e.
919
919
  // no semantic loc (wouldn't be available for expand/inline anyway)
920
+ // TODO: why here and not in parser?
920
921
  error( 'syntax-duplicate-wildcard', [ col.location, null ], {
921
922
  '#': (wildcard.location.col ? 'col' : 'std'),
922
923
  prop: '*',
@@ -13,7 +13,7 @@
13
13
  'use strict';
14
14
 
15
15
  const { makeModuleResolver, makeModuleResolverSync } = require('../utils/moduleResolve');
16
- const parseLanguage = require('../language/antlrParser'); // TODO: should we do some lazyload here?
16
+ const parsers = require('../parsers');
17
17
  const parseCsn = require('../json/from-csn');
18
18
 
19
19
  const assertConsistency = require('./assert-consistency');
@@ -43,10 +43,10 @@ const { XsnSource } = require('./xsn-model');
43
43
  const extensionParsers = {
44
44
  csn: parseCsn.parse,
45
45
  json: parseCsn.parse,
46
- cds: parseLanguage,
47
- cdl: parseLanguage,
48
- hdbcds: parseLanguage,
49
- hdbdd: parseLanguage,
46
+ cds: parsers.parseCdl,
47
+ cdl: parsers.parseCdl,
48
+ hdbcds: parsers.parseCdl,
49
+ hdbdd: parsers.parseCdl,
50
50
  };
51
51
 
52
52
  // Class for command invocation errors. Additional members:
@@ -108,7 +108,7 @@ function parseX( source, filename, options = {}, messageFunctions = null ) {
108
108
  function parserForFile( source, ext, options ) {
109
109
  // 'auto!' ignores the file's extension
110
110
  if (options.fallbackParser === 'auto!')
111
- return (source?.startsWith( '{' ) ? parseCsn.parse : parseLanguage);
111
+ return (source?.startsWith( '{' ) ? parseCsn.parse : parsers.parseCdl);
112
112
 
113
113
  if (options.fallbackParser === 'csn!')
114
114
  return parseCsn.parse;
@@ -458,7 +458,7 @@ function recompileX( csn, options ) {
458
458
  * On the given model (AST like CSN) run the definer, resolver as well as semantic checks.
459
459
  * Creates an augmented CSN (XSN) and returns it.
460
460
  *
461
- * @param {object} model AST like CSN generated e.g. by `parseLanguage()`
461
+ * @param {object} model AST like CSN generated e.g. by `parsers.parseCdl()`
462
462
  * @returns {XSN.Model} Augmented CSN (XSN)
463
463
  */
464
464
  function compileDoX( model ) {