@sap/cds-compiler 2.10.4 → 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.
- package/CHANGELOG.md +50 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +4 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +9 -23
- package/lib/api/options.js +12 -4
- package/lib/api/validate.js +23 -2
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/message-registry.js +10 -2
- package/lib/base/messages.js +23 -9
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +7 -0
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +28 -1
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +58 -91
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +93 -34
- package/lib/compiler/shared.js +29 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +31 -36
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +169 -34
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/genericAntlrParser.js +72 -14
- package/lib/language/language.g4 +73 -0
- package/lib/main.d.ts +136 -17
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +108 -31
- package/lib/model/csnUtils.js +63 -29
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +29 -18
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +9 -3
- package/lib/render/toHdbcds.js +16 -36
- package/lib/render/toSql.js +23 -5
- package/lib/transform/db/constraints.js +278 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +17 -1
- package/lib/transform/db/transformExists.js +61 -2
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +56 -435
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/transformUtilsNew.js +10 -0
- package/lib/transform/translateAssocsToJoins.js +5 -13
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,56 @@
|
|
|
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 2.11.0 - 2021-12-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Option `defaultBinaryLength` to set a `length` type facet for all definitions with type `cds.Binary`. This option
|
|
15
|
+
overrides the default binary length in the database backends and is also used as `MaxLength` attribute in Odata.
|
|
16
|
+
- If doc-comments are ignored by the compiler, an info message is now emitted. A doc-comment is ignored,
|
|
17
|
+
if it can't be assigned to an artifact. For example for two subsequent doc-comments, the first doc-comment
|
|
18
|
+
is ignored. To suppress these info messages, explicitly set option `docComment` to `false`.
|
|
19
|
+
- `cdsc`:
|
|
20
|
+
- `cdsc explain list` can now be used to get a list of message IDs with explanation texts.
|
|
21
|
+
- `cdsc` now respects the environment variable `NO_COLOR`. If set, no ANSI escape codes will be used.
|
|
22
|
+
Can be overwritten by `cdsc --color always`.
|
|
23
|
+
- to.sql/hdi: Support SQL Window Functions
|
|
24
|
+
- to.sql/hdi/hdbcds:
|
|
25
|
+
+ Support configuration of `$session` and `$user` via option `variableReplacements`.
|
|
26
|
+
+ Restricted support for SQL foreign key constraints if option `assertIntegrityType` is set to `"DB"`.
|
|
27
|
+
The behavior of this feature might change in the future.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Updated OData vocabularies 'Common' and 'UI'.
|
|
32
|
+
- to.sql/hdi/hdbcds: The default length of `cds.Binary` is set to `5000` similar to `cds.String`.
|
|
33
|
+
|
|
34
|
+
### Removed
|
|
35
|
+
|
|
36
|
+
- to.hdbcds: Doc comments on view columns are not rendered anymore. Doc comments on string literals will make the deployment fail
|
|
37
|
+
as the SAP HANA CDS compiler concatenates the doc comment with the string literal. Besides that, doc comments on view columns
|
|
38
|
+
are not transported to the database by SAP HANA CDS.
|
|
39
|
+
- to.hdbcds/sql/hdi: Forbid associations in filters after `exists` (except for nested `exists`), as the final behavior is not yet specified.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- CSN parser: doc-comment extensions are no longer ignored.
|
|
44
|
+
- Properly check for duplicate annotation definitions.
|
|
45
|
+
- Correctly apply annotations on inherited enum symbols.
|
|
46
|
+
- Correctly apply annotations on elements in an inherited structure array.
|
|
47
|
+
- Fix a bug in API `defaultStringLength` value evaluation.
|
|
48
|
+
- Fix crash if named arguments are used in a function that's inside a `CASE` statement.
|
|
49
|
+
- to.sql/hdi/hdbcds:
|
|
50
|
+
+ Properly flatten ad-hoc defined elements in `returns` / `params` of `actions` and `functions`.
|
|
51
|
+
+ Correctly handle `*` in non-first position.
|
|
52
|
+
+ Correctly handle action return types
|
|
53
|
+
+ Correctly handle mixin association named `$self`
|
|
54
|
+
- to.cdl: doc-comments are no longer rendered twice.
|
|
55
|
+
- to.edm(x):
|
|
56
|
+
+ Fix a bug in V2/V4 partner ship calculation.
|
|
57
|
+
+ Remove warning of unknown types for Open Types in `@Core.Dictionary`.
|
|
58
|
+
+ An empty CSN no longer results in a JavaScript type error
|
|
59
|
+
|
|
10
60
|
## Version 2.10.4 - 2021-11-05
|
|
11
61
|
|
|
12
62
|
### Fixed
|
package/bin/cdsc.js
CHANGED
|
@@ -26,8 +26,10 @@ const path = require('path');
|
|
|
26
26
|
const { reveal } = require('../lib/model/revealInternalProperties');
|
|
27
27
|
const enrichCsn = require('../lib/model/enrichCsn');
|
|
28
28
|
const { optionProcessor } = require('../lib/optionProcessor');
|
|
29
|
-
const {
|
|
30
|
-
|
|
29
|
+
const {
|
|
30
|
+
explainMessage, hasMessageExplanation, sortMessages, messageIdsWithExplanation,
|
|
31
|
+
} = require('../lib/base/messages');
|
|
32
|
+
const { term } = require('../lib/utils/term');
|
|
31
33
|
const { splitLines } = require('../lib/utils/file');
|
|
32
34
|
const { addLocalizationViews } = require('../lib/transform/localized');
|
|
33
35
|
const { availableBetaFlags } = require('../lib/base/model');
|
|
@@ -52,9 +54,11 @@ function remapCmdOptions(options, cmdOptions) {
|
|
|
52
54
|
options.sqlMapping = value;
|
|
53
55
|
break;
|
|
54
56
|
case 'user':
|
|
55
|
-
if (!options.
|
|
56
|
-
options.
|
|
57
|
-
options.
|
|
57
|
+
if (!options.variableReplacements)
|
|
58
|
+
options.variableReplacements = {};
|
|
59
|
+
if (!options.variableReplacements.$user)
|
|
60
|
+
options.variableReplacements.$user = {};
|
|
61
|
+
options.variableReplacements.$user.id = value;
|
|
58
62
|
break;
|
|
59
63
|
case 'dialect':
|
|
60
64
|
options.sqlDialect = value;
|
|
@@ -63,9 +67,11 @@ function remapCmdOptions(options, cmdOptions) {
|
|
|
63
67
|
options.odataVersion = value;
|
|
64
68
|
break;
|
|
65
69
|
case 'locale':
|
|
66
|
-
if (!options.
|
|
67
|
-
options.
|
|
68
|
-
options.
|
|
70
|
+
if (!options.variableReplacements)
|
|
71
|
+
options.variableReplacements = {};
|
|
72
|
+
if (!options.variableReplacements.$user)
|
|
73
|
+
options.variableReplacements.$user = {};
|
|
74
|
+
options.variableReplacements.$user.locale = value;
|
|
69
75
|
break;
|
|
70
76
|
default:
|
|
71
77
|
options[key] = value;
|
|
@@ -124,8 +130,6 @@ try {
|
|
|
124
130
|
global.cds = {};
|
|
125
131
|
global.cds.home = cmdLine.options.cdsHome;
|
|
126
132
|
}
|
|
127
|
-
// Default color mode is 'auto'
|
|
128
|
-
term.useColor(cmdLine.options.color || 'auto');
|
|
129
133
|
|
|
130
134
|
// Set default command if required
|
|
131
135
|
cmdLine.command = cmdLine.command || 'toCsn';
|
|
@@ -137,10 +141,7 @@ try {
|
|
|
137
141
|
if (cmdLine.command === 'parseCdl') {
|
|
138
142
|
cmdLine.command = 'toCsn';
|
|
139
143
|
cmdLine.options.parseCdl = true;
|
|
140
|
-
|
|
141
|
-
const err = `'parseCdl' expects exactly one file! ${cmdLine.args.files.length} provided.`;
|
|
142
|
-
displayUsage(err, optionProcessor.commands.parseCdl.helpText, 2);
|
|
143
|
-
}
|
|
144
|
+
cmdLine.args.files = [ cmdLine.args.file ];
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
if (cmdLine.options.directBackend)
|
|
@@ -155,6 +156,14 @@ try {
|
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
// remap string values for `assertIntegrity` option to boolean
|
|
160
|
+
if (cmdLine.options.assertIntegrity &&
|
|
161
|
+
cmdLine.options.assertIntegrity === 'true' ||
|
|
162
|
+
cmdLine.options.assertIntegrity === 'false'
|
|
163
|
+
)
|
|
164
|
+
cmdLine.options.assertIntegrity = Boolean(cmdLine.options.assertIntegrity);
|
|
165
|
+
|
|
166
|
+
|
|
158
167
|
// Enable all beta-flags if betaMode is set to true
|
|
159
168
|
if (cmdLine.options.betaMode)
|
|
160
169
|
cmdLine.options.beta = availableBetaFlags;
|
|
@@ -231,6 +240,8 @@ function executeCommandLine(command, options, args) {
|
|
|
231
240
|
if (options.out && options.out !== '-' && !fs.existsSync(options.out))
|
|
232
241
|
fs.mkdirSync(options.out);
|
|
233
242
|
|
|
243
|
+
// Default color mode is 'auto'
|
|
244
|
+
const colorTerm = term(options.color || 'auto');
|
|
234
245
|
|
|
235
246
|
// Add implementation functions corresponding to commands here
|
|
236
247
|
const commands = {
|
|
@@ -393,14 +404,15 @@ function executeCommandLine(command, options, args) {
|
|
|
393
404
|
}
|
|
394
405
|
|
|
395
406
|
function explain() {
|
|
396
|
-
if (args.
|
|
397
|
-
|
|
407
|
+
if (args.messageId === 'list') {
|
|
408
|
+
console.log(messageIdsWithExplanation().join('\n'));
|
|
409
|
+
throw new ProcessExitError(0);
|
|
410
|
+
}
|
|
398
411
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
console.error(`Message '${id}' does not have an explanation!`);
|
|
412
|
+
if (!hasMessageExplanation(args.messageId))
|
|
413
|
+
console.error(`Message '${args.messageId}' does not have an explanation!`);
|
|
402
414
|
else
|
|
403
|
-
console.log(explainMessage(
|
|
415
|
+
console.log(explainMessage(args.messageId));
|
|
404
416
|
}
|
|
405
417
|
|
|
406
418
|
// Display error messages in `err` resulting from a compilation. Also set
|
|
@@ -464,16 +476,20 @@ function executeCommandLine(command, options, args) {
|
|
|
464
476
|
hasAtLeastOneExplanation = hasAtLeastOneExplanation || main.hasMessageExplanation(msg.messageId);
|
|
465
477
|
const name = msg.location && msg.location.file;
|
|
466
478
|
const fullFilePath = name ? path.resolve('', name) : undefined;
|
|
467
|
-
const context = fullFilePath
|
|
479
|
+
const context = fullFilePath ? sourceLines(fullFilePath) : [];
|
|
468
480
|
log(main.messageStringMultiline(msg, {
|
|
469
|
-
normalizeFilename,
|
|
481
|
+
normalizeFilename,
|
|
482
|
+
noMessageId: !options.showMessageId,
|
|
483
|
+
withLineSpacer: true,
|
|
484
|
+
hintExplanation: true,
|
|
485
|
+
color: options.color,
|
|
470
486
|
}));
|
|
471
487
|
if (context)
|
|
472
|
-
log(main.messageContext(context, msg));
|
|
488
|
+
log(main.messageContext(context, msg, { color: options.color }));
|
|
473
489
|
log(); // newline
|
|
474
490
|
});
|
|
475
491
|
if (options.showMessageId && hasAtLeastOneExplanation)
|
|
476
|
-
log(`${
|
|
492
|
+
log(`${colorTerm.help('help')}: Messages marked with '…' have an explanation text. Use \`cdsc explain <message-id>\` for a more detailed error description.`);
|
|
477
493
|
}
|
|
478
494
|
return model;
|
|
479
495
|
}
|
|
@@ -549,10 +565,11 @@ function executeCommandLine(command, options, args) {
|
|
|
549
565
|
}
|
|
550
566
|
|
|
551
567
|
function catchErrors(err) {
|
|
568
|
+
// @ts-ignore
|
|
552
569
|
if (err instanceof Error && err.hasBeenReported)
|
|
553
570
|
return;
|
|
554
571
|
console.error( '' );
|
|
555
|
-
console.error( 'INTERNAL ERROR:
|
|
572
|
+
console.error( 'INTERNAL ERROR:' );
|
|
556
573
|
console.error( util.inspect(err, false, null) );
|
|
557
574
|
console.error( '' );
|
|
558
575
|
process.exitCode = 70;
|
package/bin/cdsse.js
CHANGED
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,10 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 2.11.0
|
|
12
|
+
|
|
13
|
+
### Removed `foreignKeyConstraints`
|
|
14
|
+
|
|
11
15
|
## Version 2.10.4
|
|
12
16
|
|
|
13
17
|
### Fixed `nestedProjections`
|
package/lib/api/.eslintrc.json
CHANGED
package/lib/api/main.js
CHANGED
|
@@ -14,7 +14,7 @@ const { toSqlDdl } = require('../render/toSql');
|
|
|
14
14
|
const { compareModels } = require('../modelCompare/compare');
|
|
15
15
|
const sortViews = require('../model/sortViews');
|
|
16
16
|
const { getResultingName } = require('../model/csnUtils');
|
|
17
|
-
const timetrace = require('../utils/timetrace');
|
|
17
|
+
const { timetrace } = require('../utils/timetrace');
|
|
18
18
|
const { transformForHanaWithCsn } = require('../transform/forHanaNew');
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -135,7 +135,7 @@ function odataInternal(csn, internalOptions) {
|
|
|
135
135
|
* Return a odata-transformed CSN
|
|
136
136
|
*
|
|
137
137
|
* @param {CSN.Model} csn Clean input CSN
|
|
138
|
-
* @param {
|
|
138
|
+
* @param {ODataOptions} [options={}] Options
|
|
139
139
|
* @returns {oDataCSN} Return an oData-pre-processed CSN
|
|
140
140
|
*/
|
|
141
141
|
function odata(csn, options = {}) {
|
|
@@ -454,7 +454,7 @@ function hdbcds(csn, options = {}) {
|
|
|
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 {
|
|
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 {
|
|
487
|
+
* @param {ODataOptions} [options={}] Options
|
|
488
488
|
* @returns {edms} { <service>:<JSON representation>, ...}
|
|
489
489
|
*/
|
|
490
490
|
function edmall(csn, options = {}) {
|
|
@@ -516,7 +516,7 @@ function edmall(csn, options = {}) {
|
|
|
516
516
|
* Generate a edmx document for the given service
|
|
517
517
|
*
|
|
518
518
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
519
|
-
* @param {
|
|
519
|
+
* @param {ODataOptions} [options={}] Options
|
|
520
520
|
* @returns {edmx} The XML representation of the service
|
|
521
521
|
*/
|
|
522
522
|
function edmx(csn, options = {}) {
|
|
@@ -547,7 +547,7 @@ edmx.all = edmxall;
|
|
|
547
547
|
* Generate edmx documents for all services
|
|
548
548
|
*
|
|
549
549
|
* @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
|
|
550
|
-
* @param {
|
|
550
|
+
* @param {ODataOptions} [options={}] Options
|
|
551
551
|
* @returns {edmxs} { <service>:<XML representation>, ...}
|
|
552
552
|
*/
|
|
553
553
|
function edmxall(csn, options = {}) {
|
|
@@ -709,20 +709,6 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
709
709
|
* @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
|
|
710
710
|
*/
|
|
711
711
|
|
|
712
|
-
/**
|
|
713
|
-
* Options available for all oData-based functions
|
|
714
|
-
*
|
|
715
|
-
* @typedef {object} oDataOptions
|
|
716
|
-
* @property {object} [beta] Enable experimental features - not for productive use!
|
|
717
|
-
* @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
|
|
718
|
-
* @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
|
|
719
|
-
* @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
|
|
720
|
-
* @property {oDataVersion} [odataVersion='v4'] Odata version to use
|
|
721
|
-
* @property {oDataFormat} [odataFormat='flat'] Wether to generate oData as flat or as structured. Structured only with v4.
|
|
722
|
-
* @property {NamingMode} [sqlMapping='plain'] Naming mode to use
|
|
723
|
-
* @property {string} [service] If a single service is to be rendered
|
|
724
|
-
*/
|
|
725
|
-
|
|
726
712
|
/**
|
|
727
713
|
* Options available for to.hdi
|
|
728
714
|
*
|
|
@@ -753,9 +739,9 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
753
739
|
* @typedef {object} sqlOptions
|
|
754
740
|
* @property {NamingMode} [sqlMapping='plain'] Naming mode to use
|
|
755
741
|
* @property {SQLDialect} [sqlDialect='sqlite'] SQL dialect to use
|
|
756
|
-
* @property {object} [
|
|
757
|
-
* @property {string} [
|
|
758
|
-
* @property {string} [
|
|
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
|
|
759
745
|
* @property {object} [beta] Enable experimental features - not for productive use!
|
|
760
746
|
* @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
|
|
761
747
|
* @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
|
package/lib/api/options.js
CHANGED
|
@@ -15,6 +15,7 @@ const publicOptionsNewAPI = [
|
|
|
15
15
|
'severities',
|
|
16
16
|
'messages',
|
|
17
17
|
'withLocations',
|
|
18
|
+
'defaultBinaryLength',
|
|
18
19
|
'defaultStringLength',
|
|
19
20
|
'csnFlavor',
|
|
20
21
|
// DB
|
|
@@ -23,7 +24,8 @@ const publicOptionsNewAPI = [
|
|
|
23
24
|
'sqlChangeMode',
|
|
24
25
|
'allowCsnDowngrade',
|
|
25
26
|
'joinfk',
|
|
26
|
-
'magicVars',
|
|
27
|
+
'magicVars', // deprecated
|
|
28
|
+
'variableReplacements',
|
|
27
29
|
// ODATA
|
|
28
30
|
'odataVersion',
|
|
29
31
|
'odataFormat',
|
|
@@ -47,9 +49,10 @@ const privateOptions = [
|
|
|
47
49
|
'traceParserAmb',
|
|
48
50
|
'testMode',
|
|
49
51
|
'testSortCsn',
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
52
|
+
'integrityNotEnforced',
|
|
53
|
+
'integrityNotValidated',
|
|
54
|
+
'assertIntegrity',
|
|
55
|
+
'assertIntegrityType',
|
|
53
56
|
'noRecompile',
|
|
54
57
|
'internalMsg',
|
|
55
58
|
'disableHanaComments', // in case of issues with hana comment rendering
|
|
@@ -109,6 +112,10 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
109
112
|
mapToOldNames(optionName, optionValue);
|
|
110
113
|
}
|
|
111
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
|
+
|
|
112
119
|
/**
|
|
113
120
|
* Map a new-style option to it's old format
|
|
114
121
|
*
|
|
@@ -130,6 +137,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
130
137
|
case 'sqlMapping':
|
|
131
138
|
options.names = optionValue;
|
|
132
139
|
break;
|
|
140
|
+
// No need to remap variableReplacements here - we use the new mechanism with that directly
|
|
133
141
|
case 'magicVars':
|
|
134
142
|
if (optionValue.user)
|
|
135
143
|
options.user = optionValue.user;
|
package/lib/api/validate.js
CHANGED
|
@@ -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,10 +97,17 @@ 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',
|
|
95
|
-
found: val =>
|
|
110
|
+
found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
|
|
96
111
|
},
|
|
97
112
|
csnFlavor: {
|
|
98
113
|
validate: val => typeof val === 'string',
|
|
@@ -102,6 +117,12 @@ const validators = {
|
|
|
102
117
|
dictionaryPrototype: {
|
|
103
118
|
validate: () => true,
|
|
104
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' ]),
|
|
105
126
|
};
|
|
106
127
|
|
|
107
128
|
const allCombinationValidators = {
|
package/lib/backends.js
CHANGED
|
@@ -14,7 +14,7 @@ const { csn2edm, csn2edmAll } = require('./edm/csn2edm');
|
|
|
14
14
|
const { mergeOptions } = require('./model/csnUtils');
|
|
15
15
|
const { isBetaEnabled } = require('./base/model');
|
|
16
16
|
const { optionProcessor } = require('./optionProcessor')
|
|
17
|
-
const timetrace = require('./utils/timetrace');
|
|
17
|
+
const { timetrace } = require('./utils/timetrace');
|
|
18
18
|
const { makeMessageFunction } = require('./base/messages');
|
|
19
19
|
const { forEachDefinition } = require('./model/csnUtils');
|
|
20
20
|
|
|
@@ -92,9 +92,9 @@ function toOdataWithCsn(csn, options) {
|
|
|
92
92
|
// Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
93
93
|
// using 'options'
|
|
94
94
|
function preparedCsnToEdmx(csn, service, options) {
|
|
95
|
-
|
|
95
|
+
const e = csn2edm(csn, service, options)
|
|
96
96
|
return {
|
|
97
|
-
edmx
|
|
97
|
+
edmx: (e ? e.toXML('all') : undefined)
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -115,9 +115,9 @@ function preparedCsnToEdmxAll(csn, options) {
|
|
|
115
115
|
function preparedCsnToEdm(csn, service, options) {
|
|
116
116
|
// Merge options; override OData version as edm json is always v4
|
|
117
117
|
options = mergeOptions(options, { toOdata : { version : 'v4' }});
|
|
118
|
-
const
|
|
118
|
+
const e = csn2edm(csn, service, options);
|
|
119
119
|
return {
|
|
120
|
-
edmj
|
|
120
|
+
edmj: (e ? e.toJSON() : undefined)
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -405,9 +405,6 @@ function toRenameWithCsn(csn, options) {
|
|
|
405
405
|
|
|
406
406
|
function alterConstraintsWithCsn(csn, options) {
|
|
407
407
|
const { error } = makeMessageFunction(csn, options, 'manageConstraints');
|
|
408
|
-
// Requires beta mode
|
|
409
|
-
if (!isBetaEnabled(options, 'foreignKeyConstraints'))
|
|
410
|
-
error(null, null, 'ALTER TABLE statements for adding/modifying referential constraints are only available in beta mode');
|
|
411
408
|
|
|
412
409
|
const {
|
|
413
410
|
drop, alter, names, src, violations
|
|
@@ -420,6 +417,10 @@ function alterConstraintsWithCsn(csn, options) {
|
|
|
420
417
|
dialect: 'hana',
|
|
421
418
|
names: names || 'plain'
|
|
422
419
|
}
|
|
420
|
+
|
|
421
|
+
// Of course we want the database constraints
|
|
422
|
+
options.assertIntegrityType = 'DB';
|
|
423
|
+
|
|
423
424
|
const transformedOptions = transformSQLOptions(csn, options);
|
|
424
425
|
const mergedOptions = mergeOptions(transformedOptions.options, { forHana : transformedOptions.forHanaOptions });
|
|
425
426
|
const forSqlCsn = transformForHanaWithCsn(csn, mergedOptions, 'to.sql');
|
package/lib/base/dictionaries.js
CHANGED
|
@@ -28,6 +28,7 @@ function dictAdd( dict, name, entry, duplicateCallback ) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function dictForEach( dict, callback ) {
|
|
31
|
+
// TODO: probably define an extra dictForEachArray()
|
|
31
32
|
for (const name in dict) {
|
|
32
33
|
const entry = dict[name];
|
|
33
34
|
if (Array.isArray(entry)) {
|
|
@@ -35,7 +36,7 @@ function dictForEach( dict, callback ) {
|
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
38
|
callback( entry );
|
|
38
|
-
if (Array.isArray(entry.$duplicates))
|
|
39
|
+
if (Array.isArray( entry.$duplicates ))
|
|
39
40
|
entry.$duplicates.forEach( callback );
|
|
40
41
|
}
|
|
41
42
|
}
|
|
@@ -118,6 +118,7 @@ const centralMessages = {
|
|
|
118
118
|
'syntax-anno-after-params': { severity: 'Error', configurableFor: true }, // does not hurt
|
|
119
119
|
'syntax-anno-after-struct': { severity: 'Error', configurableFor: true }, // does not hurt
|
|
120
120
|
'syntax-csn-expected-cardinality': { severity: 'Error' }, // TODO: more than 30 chars
|
|
121
|
+
'syntax-csn-expected-length': { severity: 'Error' },
|
|
121
122
|
'syntax-csn-expected-translation': { severity: 'Error' }, // TODO: more than 30 chars
|
|
122
123
|
'syntax-csn-required-subproperty': { severity: 'Error' }, // TODO: more than 30 chars
|
|
123
124
|
'syntax-csn-unexpected-property': { severity: 'Error', configurableFor: true }, // is the removed
|
|
@@ -133,6 +134,7 @@ const centralMessages = {
|
|
|
133
134
|
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
134
135
|
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
135
136
|
'odata-spec-violation-type': { severity: 'Error', configurableFor: [ 'to.edmx' ] },
|
|
137
|
+
'odata-spec-violation-no-key': { severity: 'Warning' },
|
|
136
138
|
'odata-spec-violation-key-array': { severity: 'Error' }, // more than 30 chars
|
|
137
139
|
'odata-spec-violation-key-null': { severity: 'Error' }, // more than 30 chars
|
|
138
140
|
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
@@ -160,6 +162,11 @@ const centralMessageTexts = {
|
|
|
160
162
|
$tableImplicit: 'The resulting table alias starts with $(NAME) and might shadow a special variable - specify another name with $(KEYWORD)',
|
|
161
163
|
mixin: 'A mixin name starting with $(NAME) might shadow a special variable - replace by another name' ,
|
|
162
164
|
},
|
|
165
|
+
'syntax-csn-expected-length': {
|
|
166
|
+
std: 'Expected array in $(PROP) to have at least $(N) items',
|
|
167
|
+
one: 'Expected array in $(PROP) to have at least one item',
|
|
168
|
+
suffix: 'With sibling property $(OTHERPROP), expected array in $(PROP) to have at least one item',
|
|
169
|
+
},
|
|
163
170
|
'ref-undefined-def': {
|
|
164
171
|
std: 'Artifact $(ART) has not been found',
|
|
165
172
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -203,13 +210,13 @@ const centralMessageTexts = {
|
|
|
203
210
|
'duplicate-definition': {
|
|
204
211
|
std: 'Duplicate definition of $(NAME)',
|
|
205
212
|
absolute: 'Duplicate definition of artifact $(NAME)',
|
|
206
|
-
|
|
213
|
+
annotation: 'Duplicate definition of annotation vocabulary $(NAME)',
|
|
207
214
|
element: 'Duplicate definition of element $(NAME)',
|
|
208
215
|
enum: 'Duplicate definition of enum $(NAME)',
|
|
209
216
|
key: 'Duplicate definition of key $(NAME)',
|
|
210
217
|
action: 'Duplicate definition of action or function $(NAME)',
|
|
211
218
|
param: 'Duplicate definition of parameter $(NAME)',
|
|
212
|
-
|
|
219
|
+
alias: 'Duplicate definition of table alias or mixin $(NAME)',
|
|
213
220
|
},
|
|
214
221
|
|
|
215
222
|
// TODO: rename to ref-expected-XYZ
|
|
@@ -262,6 +269,7 @@ const centralMessageTexts = {
|
|
|
262
269
|
std: 'Unexpected $(TYPE) mapped to $(ID) as type for key element $(NAME)', // structured
|
|
263
270
|
scalar: 'Unexpected $(TYPE) mapped to $(ID) as type for key element' // flat
|
|
264
271
|
},
|
|
272
|
+
'odata-spec-violation-no-key': 'Expected entity to have a primary key',
|
|
265
273
|
'odata-spec-violation-type': 'Expected element to have a type',
|
|
266
274
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(KIND)',
|
|
267
275
|
'odata-spec-violation-namespace': 'Expected service name not to be one of the reserved names $(NAMES)',
|
package/lib/base/messages.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
-
const term = require('../utils/term');
|
|
7
|
+
const { term } = require('../utils/term');
|
|
8
8
|
const { locationString } = require('./location');
|
|
9
9
|
const { isDeprecatedEnabled } = require('./model');
|
|
10
10
|
const { centralMessages, centralMessageTexts } = require('./message-registry');
|
|
@@ -15,6 +15,8 @@ const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
|
|
18
|
+
// term instance for messages
|
|
19
|
+
const colorTerm = term();
|
|
18
20
|
|
|
19
21
|
// Functions ensuring message consistency during runtime with --test-mode
|
|
20
22
|
|
|
@@ -853,19 +855,26 @@ function messageHash(msg) {
|
|
|
853
855
|
*
|
|
854
856
|
* Example:
|
|
855
857
|
* ```txt
|
|
856
|
-
* Error[message-id]: Can't find type `nu` in this scope
|
|
858
|
+
* Error[message-id]: Can't find type `nu` in this scope
|
|
857
859
|
* |
|
|
858
|
-
* <source>.cds:3:11, at entity:“E”
|
|
860
|
+
* <source>.cds:3:11, at entity:“E”/element:“e”
|
|
859
861
|
* ```
|
|
862
|
+
*
|
|
860
863
|
* @param {CSN.Message} err
|
|
861
864
|
* @param {object} [config = {}]
|
|
862
865
|
* @param {boolean} [config.normalizeFilename] Replace windows `\` with forward slashes `/`.
|
|
863
866
|
* @param {boolean} [config.noMessageId]
|
|
864
867
|
* @param {boolean} [config.hintExplanation] If true, messages with explanations will get a "…" marker.
|
|
865
|
-
* @param {boolean} [config.withLineSpacer]
|
|
868
|
+
* @param {boolean} [config.withLineSpacer] If true, an additional line (with `|`) will be inserted between message and location.
|
|
869
|
+
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the severity. If false, no
|
|
870
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
871
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
872
|
+
* unset.
|
|
866
873
|
* @returns {string}
|
|
867
874
|
*/
|
|
868
875
|
function messageStringMultiline( err, config = {} ) {
|
|
876
|
+
colorTerm.changeColorMode(config ? config.color : 'auto');
|
|
877
|
+
|
|
869
878
|
const explainHelp = (config.hintExplanation && hasMessageExplanation(err.messageId)) ? '…' : '';
|
|
870
879
|
const msgId = (err.messageId && !config.noMessageId) ? `[${ err.messageId }${ explainHelp }]` : '';
|
|
871
880
|
const home = !err.home ? '' : ('at ' + err.home);
|
|
@@ -878,7 +887,7 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
878
887
|
location += ', '
|
|
879
888
|
}
|
|
880
889
|
else if (!home)
|
|
881
|
-
return
|
|
890
|
+
return colorTerm.severity(severity, severity + msgId) + ' ' + err.message;
|
|
882
891
|
|
|
883
892
|
let lineSpacer = '';
|
|
884
893
|
if (config.withLineSpacer) {
|
|
@@ -886,8 +895,7 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
886
895
|
lineSpacer = `\n ${ ' '.repeat( additionalIndent ) }|`;
|
|
887
896
|
}
|
|
888
897
|
|
|
889
|
-
|
|
890
|
-
return term.asSeverity(severity, severity + msgId) + ': ' + err.message + lineSpacer + '\n ' + location + home;
|
|
898
|
+
return colorTerm.severity(severity, severity + msgId) + ': ' + err.message + lineSpacer + '\n ' + location + home;
|
|
891
899
|
}
|
|
892
900
|
|
|
893
901
|
/**
|
|
@@ -901,9 +909,15 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
901
909
|
* @param {string[]} sourceLines The source code split up into lines, e.g. by `splitLines(src)`
|
|
902
910
|
* from `lib/utils/file.js`
|
|
903
911
|
* @param {CSN.Message} err Error object containing all details like line, message, etc.
|
|
912
|
+
* @param {object} [config = {}]
|
|
913
|
+
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the `^`. If false, no
|
|
914
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
915
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
916
|
+
* unset.
|
|
904
917
|
* @returns {string}
|
|
905
918
|
*/
|
|
906
|
-
function messageContext(sourceLines, err) {
|
|
919
|
+
function messageContext(sourceLines, err, config) {
|
|
920
|
+
colorTerm.changeColorMode(config ? config.color : 'auto');
|
|
907
921
|
const MAX_COL_LENGTH = 100;
|
|
908
922
|
|
|
909
923
|
const loc = err.$location;
|
|
@@ -955,7 +969,7 @@ function messageContext(sourceLines, err) {
|
|
|
955
969
|
// Indicate that the error is further to the right.
|
|
956
970
|
if (endColumn === MAX_COL_LENGTH)
|
|
957
971
|
highlighter = highlighter.replace(' ^', '..^');
|
|
958
|
-
msg += indent + '| ' +
|
|
972
|
+
msg += indent + '| ' + colorTerm.severity(severity, highlighter);
|
|
959
973
|
|
|
960
974
|
} else if (maxLine !== endLine) {
|
|
961
975
|
// error spans more lines which we don't print
|