@sap/cds-compiler 5.4.4 → 5.5.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.
- package/CHANGELOG.md +17 -1
- package/bin/cds_remove_invalid_whitespace.js +4 -4
- package/bin/cds_update_annotations.js +3 -3
- package/bin/cds_update_identifiers.js +3 -3
- package/lib/api/main.js +18 -30
- package/lib/api/validate.js +6 -1
- package/lib/base/lazyload.js +28 -0
- package/lib/base/location.js +1 -0
- package/lib/base/message-registry.js +47 -11
- package/lib/base/messages.js +17 -3
- package/lib/checks/{dbFeatureFlags.js → featureFlags.js} +1 -1
- package/lib/checks/parameters.js +61 -4
- package/lib/checks/validator.js +14 -6
- package/lib/compiler/index.js +7 -7
- package/lib/gen/BaseParser.js +345 -235
- package/lib/gen/CdlParser.js +4434 -4492
- package/lib/gen/Dictionary.json +2 -2
- package/lib/language/antlrParser.js +2 -111
- package/lib/main.js +16 -37
- package/lib/modelCompare/utils/filter.js +47 -21
- package/lib/parsers/AstBuildingParser.js +59 -49
- package/lib/parsers/CdlGrammar.g4 +91 -130
- package/lib/parsers/index.js +123 -0
- package/lib/render/toSql.js +8 -2
- package/lib/render/utils/delta.js +33 -1
- package/lib/transform/db/{transformExists.js → assocsToQueries/transformExists.js} +12 -407
- package/lib/transform/db/assocsToQueries/utils.js +440 -0
- package/lib/transform/db/expansion.js +2 -2
- package/lib/transform/draft/db.js +14 -3
- package/lib/transform/effective/annotations.js +3 -3
- package/lib/transform/effective/main.js +5 -7
- package/lib/transform/featureFlags.js +5 -0
- package/lib/transform/forRelationalDB.js +125 -192
- package/lib/transform/transformUtils.js +0 -51
- package/package.json +2 -2
- package/lib/transform/db/featureFlags.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@
|
|
|
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
|
+
|
|
11
|
+
## Version 5.5.0 - 2024-11-22
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- CDL parser: when the new experimental option `newParser` is used, the compiler
|
|
16
|
+
uses a CDL parser with a significantly smaller footprint (among other things).
|
|
17
|
+
- to.sql|hdi.migration: For SAP HANA, render `ALTER` statements as one big statement to improve performance.
|
|
18
|
+
- to.sql.migration: Give more helpful comments when using option `script`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Update OData vocabularies: 'Common', 'PersonalData', 'UI'.
|
|
23
|
+
|
|
10
24
|
## Version 5.4.4 - 2024-11-14
|
|
11
25
|
|
|
12
26
|
### Fixed
|
|
@@ -14,18 +28,20 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
14
28
|
- Re-allow referring to mixins (and table aliases) in added columns
|
|
15
29
|
- Re-add foreign keys of named aspects to the OData CSN.
|
|
16
30
|
|
|
31
|
+
|
|
17
32
|
## Version 5.4.2 - 2024-11-06
|
|
18
33
|
|
|
19
34
|
### Fixed
|
|
20
35
|
|
|
21
36
|
- to.sql: For SQLite, map `cds.Map` to `JSON_TEXT` to ensure text affinity.
|
|
22
37
|
|
|
38
|
+
|
|
23
39
|
## Version 5.4.0 - 2024-10-24
|
|
24
40
|
|
|
25
41
|
### Added
|
|
26
42
|
|
|
27
43
|
- 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
|
|
44
|
+
has been assigned `@open: false` as empty closed complex type `cds_Map_closed` in OData V4.
|
|
29
45
|
|
|
30
46
|
### Changed
|
|
31
47
|
|
|
@@ -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
|
|
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
|
-
//
|
|
61
|
+
// parseCdl does not throw on CompilationError, so
|
|
62
62
|
// we do not need a try...catch block.
|
|
63
|
-
const ast =
|
|
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
|
|
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
|
-
//
|
|
61
|
+
// parseCdl does not throw on CompilationError, so
|
|
62
62
|
// we do not need a try...catch block.
|
|
63
|
-
const ast =
|
|
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
|
|
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
|
-
//
|
|
59
|
+
// parseCdl does not throw on CompilationError, so
|
|
60
60
|
// we do not need a try...catch block.
|
|
61
|
-
const ast =
|
|
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?.['
|
|
480
|
-
internalOptions.severities
|
|
481
|
-
internalOptions.severities['
|
|
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 {
|
|
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,
|
|
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.
|
package/lib/api/validate.js
CHANGED
|
@@ -58,7 +58,12 @@ const validators = {
|
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
60
|
severities: {
|
|
61
|
-
validate: 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;
|
package/lib/base/location.js
CHANGED
|
@@ -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
|
-
|
|
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)
|
|
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
|
/**
|
package/lib/base/messages.js
CHANGED
|
@@ -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} [
|
|
360
|
+
* @param {string|null} [_moduleName]
|
|
361
361
|
*/
|
|
362
|
-
function makeMessageFunction( model, options,
|
|
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.
|
|
652
|
+
* @param {CSN.Options} _options
|
|
639
653
|
*/
|
|
640
654
|
function setOptions( _options ) {
|
|
641
655
|
options = _options;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { setProp } = require('../base/model');
|
|
4
|
-
const { featureFlags } = require('../transform/
|
|
4
|
+
const { featureFlags } = require('../transform/featureFlags');
|
|
5
5
|
const { isSqlService } = require('../transform/db/processSqlServices');
|
|
6
6
|
|
|
7
7
|
/**
|
package/lib/checks/parameters.js
CHANGED
|
@@ -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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
76
|
+
csnValidator: {
|
|
77
|
+
params: checkForParams,
|
|
78
|
+
},
|
|
79
|
+
memberValidator: checkAssocsWithParams,
|
|
23
80
|
};
|
package/lib/checks/validator.js
CHANGED
|
@@ -49,7 +49,7 @@ const {
|
|
|
49
49
|
checkSqlAnnotationOnArtifact,
|
|
50
50
|
checkSqlAnnotationOnElement,
|
|
51
51
|
} = require('./sql-snippets');
|
|
52
|
-
const
|
|
52
|
+
const featureFlags = require('./featureFlags');
|
|
53
53
|
|
|
54
54
|
const forRelationalDBMemberValidators
|
|
55
55
|
= [
|
|
@@ -85,7 +85,7 @@ const forRelationalDBCsnValidators = [
|
|
|
85
85
|
nonexpandableStructuredInExpression,
|
|
86
86
|
navigationIntoMany,
|
|
87
87
|
checkPathsInStoredCalcElement,
|
|
88
|
-
|
|
88
|
+
featureFlags,
|
|
89
89
|
];
|
|
90
90
|
/**
|
|
91
91
|
* @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
|
|
@@ -188,10 +188,14 @@ function _validate( csn, that,
|
|
|
188
188
|
* @returns {any[]} Array of validator functions (or objects?)
|
|
189
189
|
*/
|
|
190
190
|
function getDBCsnValidators( options ) {
|
|
191
|
-
|
|
192
|
-
return [ ...forRelationalDBCsnValidators, checkForHanaTypes, checkForParams ];
|
|
191
|
+
const validations = [ ...forRelationalDBCsnValidators ];
|
|
193
192
|
|
|
194
|
-
|
|
193
|
+
if (options.transformation !== 'effective')
|
|
194
|
+
validations.push(checkForParams.csnValidator);
|
|
195
|
+
if (options.sqlDialect === 'h2' || options.sqlDialect === 'postgres')
|
|
196
|
+
validations.push(checkForHanaTypes);
|
|
197
|
+
|
|
198
|
+
return validations;
|
|
195
199
|
}
|
|
196
200
|
|
|
197
201
|
/**
|
|
@@ -200,9 +204,13 @@ function getDBCsnValidators( options ) {
|
|
|
200
204
|
* @returns {Function} the validator function with the respective checks for the HANA backend
|
|
201
205
|
*/
|
|
202
206
|
function forRelationalDB( csn, that ) {
|
|
207
|
+
const memberValidators = [ ...forRelationalDBMemberValidators, ...commonMemberValidators ];
|
|
208
|
+
if (that.options.transformation === 'hdbcds')
|
|
209
|
+
memberValidators.push(checkForParams.memberValidator);
|
|
210
|
+
|
|
203
211
|
return _validate(csn, that,
|
|
204
212
|
getDBCsnValidators(that.options),
|
|
205
|
-
|
|
213
|
+
memberValidators,
|
|
206
214
|
forRelationalDBArtifactValidators.concat(commonArtifactValidators).concat(
|
|
207
215
|
// why is this hana exclusive
|
|
208
216
|
(artifact) => {
|
package/lib/compiler/index.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
15
|
const { makeModuleResolver, makeModuleResolverSync } = require('../utils/moduleResolve');
|
|
16
|
-
const
|
|
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:
|
|
47
|
-
cdl:
|
|
48
|
-
hdbcds:
|
|
49
|
-
hdbdd:
|
|
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 :
|
|
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 `
|
|
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 ) {
|