@sap/cds-compiler 4.3.0 → 4.4.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 +36 -0
- package/lib/api/main.js +14 -24
- package/lib/api/options.js +1 -0
- package/lib/api/trace.js +38 -0
- package/lib/base/location.js +46 -1
- package/lib/base/message-registry.js +68 -16
- package/lib/base/messages.js +8 -3
- package/lib/checks/.eslintrc.json +1 -0
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/selectItems.js +4 -1
- package/lib/compiler/assert-consistency.js +3 -2
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/builtins.js +25 -1
- package/lib/compiler/checks.js +6 -5
- package/lib/compiler/define.js +12 -10
- package/lib/compiler/extend.js +22 -22
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +70 -53
- package/lib/compiler/kick-start.js +9 -5
- package/lib/compiler/populate.js +31 -22
- package/lib/compiler/propagator.js +6 -2
- package/lib/compiler/resolve.js +52 -17
- package/lib/compiler/shared.js +74 -38
- package/lib/compiler/tweak-assocs.js +64 -23
- package/lib/compiler/utils.js +40 -23
- package/lib/edm/.eslintrc.json +2 -0
- package/lib/edm/EdmPrimitiveTypeDefinitions.js +252 -0
- package/lib/edm/annotations/edmJson.js +994 -0
- package/lib/edm/annotations/genericTranslation.js +75 -421
- package/lib/edm/annotations/vocabularyDefinitions.js +160 -0
- package/lib/edm/csn2edm.js +12 -5
- package/lib/edm/edm.js +14 -73
- package/lib/edm/edmPreprocessor.js +6 -0
- package/lib/gen/Dictionary.json +187 -16
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageLexer.interp +1 -1
- package/lib/gen/languageLexer.js +1129 -671
- package/lib/gen/languageParser.js +4285 -4283
- package/lib/json/from-csn.js +13 -18
- package/lib/json/to-csn.js +11 -6
- package/lib/language/antlrParser.js +0 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +95 -30
- package/lib/language/genericAntlrParser.js +21 -1
- package/lib/main.js +13 -3
- package/lib/model/csnRefs.js +42 -8
- package/lib/model/csnUtils.js +14 -2
- package/lib/model/enrichCsn.js +33 -5
- package/lib/model/revealInternalProperties.js +5 -0
- package/lib/modelCompare/compare.js +76 -14
- package/lib/modelCompare/utils/filter.js +19 -12
- package/lib/optionProcessor.js +2 -0
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toHdbcds.js +3 -0
- package/lib/render/toRename.js +3 -1
- package/lib/render/toSql.js +46 -92
- package/lib/render/utils/common.js +76 -0
- package/lib/render/utils/delta.js +17 -3
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/db/.eslintrc.json +1 -0
- package/lib/transform/db/applyTransformations.js +30 -4
- package/lib/transform/db/associations.js +22 -10
- package/lib/transform/db/backlinks.js +6 -2
- package/lib/transform/db/expansion.js +2 -2
- package/lib/transform/db/transformExists.js +13 -39
- package/lib/transform/draft/db.js +14 -3
- package/lib/transform/draft/odata.js +5 -18
- package/lib/transform/effective/associations.js +46 -15
- package/lib/transform/effective/main.js +7 -2
- package/lib/transform/effective/misc.js +43 -24
- package/lib/transform/effective/queries.js +20 -22
- package/lib/transform/effective/types.js +6 -2
- package/lib/transform/forOdata.js +5 -2
- package/lib/transform/localized.js +1 -1
- package/lib/transform/parseExpr.js +73 -21
- package/lib/transform/translateAssocsToJoins.js +24 -16
- package/lib/utils/term.js +2 -2
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@
|
|
|
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 4.4.0 - 2023-11-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- compiler: International letters such as `ä` can now be used in CDS identifiers without quoting.
|
|
15
|
+
Unicode letters similar to JavaScript are allowed.
|
|
16
|
+
- to.edm(x):
|
|
17
|
+
+ Allow to render all complex types within a requested service as `OpenType=true` with option `--odata-open-type`.
|
|
18
|
+
Explicit `@open: false` annotations are not overwritten.
|
|
19
|
+
+ Allow to annotate the generated draft artifacts but not generated foreign keys (as with any other managed association).
|
|
20
|
+
- to.sql|hdi|hdbcds: Allow annotating the generated `.drafts` tables.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- CDL parser: improve error recovery inside structured annotation values
|
|
25
|
+
- Update OData vocabularies: 'Aggregation', 'Common', 'Core', 'Hierarchy', 'ODM', 'UI'.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- parser:
|
|
30
|
+
+ `/**/` was incorrectly parsed as an unterminated doc-comment, leading to parse errors.
|
|
31
|
+
+ Doc-comments consisting only of `*` were not correctly parsed.
|
|
32
|
+
- compiler: do not propagate `default`s in a CSN of flavor `xtended`/`gensrc`.
|
|
33
|
+
- to.hana: Fix various bugs in association to join translation. Support `$self` references
|
|
34
|
+
in filter expressions.
|
|
35
|
+
- to.edm(x): Omit `EntitySet` attribute on `Edm.FunctionImport` and `Edm.ActionImport` that return
|
|
36
|
+
a singleton.
|
|
37
|
+
- to.sql|hdi.migration: Improve handling of primary key changes - detect them and render corresponding drop-create.
|
|
38
|
+
|
|
39
|
+
## Version 4.3.2 - 2023-10-25
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- compiler: Fix auto-exposure of composition target entities inside anonymous composition target aspects.
|
|
44
|
+
- to.hana: Fix a bug in association to join translation, expect ON condition operand to be a function without arguments.
|
|
45
|
+
|
|
10
46
|
## Version 4.3.0 - 2023-09-29
|
|
11
47
|
|
|
12
48
|
### Added
|
package/lib/api/main.js
CHANGED
|
@@ -37,6 +37,7 @@ const { ModelError } = require('../base/error');
|
|
|
37
37
|
const { forEach, forEachKey } = require('../utils/objectUtils');
|
|
38
38
|
const { checkRemovedDeprecatedFlags } = require('../base/model');
|
|
39
39
|
const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
|
|
40
|
+
const { traceApi } = require('./trace');
|
|
40
41
|
|
|
41
42
|
const relevantGeneralOptions = [ /* for future generic options */ ];
|
|
42
43
|
const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
|
|
@@ -146,7 +147,7 @@ function odataInternal( csn, internalOptions ) {
|
|
|
146
147
|
* @returns {oDataCSN} Return an oData-pre-processed CSN
|
|
147
148
|
*/
|
|
148
149
|
function odata( csn, options = {} ) {
|
|
149
|
-
traceApi(
|
|
150
|
+
traceApi('for.odata', options);
|
|
150
151
|
const internalOptions = prepareOptions.for.odata(options);
|
|
151
152
|
return odataInternal(csn, internalOptions);
|
|
152
153
|
}
|
|
@@ -159,7 +160,7 @@ function odata( csn, options = {} ) {
|
|
|
159
160
|
* @returns {object} { model: string, namespace: string }
|
|
160
161
|
*/
|
|
161
162
|
function cdl( csn, options = {} ) {
|
|
162
|
-
traceApi(
|
|
163
|
+
traceApi('to.cdl', options);
|
|
163
164
|
const internalOptions = prepareOptions.to.cdl(options);
|
|
164
165
|
return toCdl.csnToCdl(csn, internalOptions);
|
|
165
166
|
}
|
|
@@ -251,7 +252,7 @@ function forEffective( csn, options = {} ) {
|
|
|
251
252
|
* @returns {SQL[]} Array of SQL statements, tables first, views second
|
|
252
253
|
*/
|
|
253
254
|
function sql( csn, options = {} ) {
|
|
254
|
-
traceApi(
|
|
255
|
+
traceApi('to.sql', options);
|
|
255
256
|
const internalOptions = prepareOptions.to.sql(options);
|
|
256
257
|
internalOptions.transformation = 'sql';
|
|
257
258
|
|
|
@@ -274,7 +275,7 @@ function sql( csn, options = {} ) {
|
|
|
274
275
|
* @returns {HDIArtifacts} { <filename>:<content>, ...}
|
|
275
276
|
*/
|
|
276
277
|
function hdi( csn, options = {} ) {
|
|
277
|
-
traceApi(
|
|
278
|
+
traceApi('to.hdi', options);
|
|
278
279
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
279
280
|
|
|
280
281
|
// we need the CSN for view sorting
|
|
@@ -377,7 +378,7 @@ function remapName( key, csn, filter = () => true ) {
|
|
|
377
378
|
* - createsAndAlters: An array of SQL statements to ALTER/CREATE tables/views
|
|
378
379
|
*/
|
|
379
380
|
function sqlMigration( csn, options, beforeImage ) {
|
|
380
|
-
traceApi(
|
|
381
|
+
traceApi('to.sql.migration', options);
|
|
381
382
|
const internalOptions = prepareOptions.to.sql(options);
|
|
382
383
|
const {
|
|
383
384
|
error, warning, info, throwWithError, message,
|
|
@@ -398,6 +399,8 @@ function sqlMigration( csn, options, beforeImage ) {
|
|
|
398
399
|
error, warning, info, throwWithError, message,
|
|
399
400
|
}));
|
|
400
401
|
Object.entries(diff.deletions).forEach(entry => diffFilterObj.deletion(entry, error));
|
|
402
|
+
diff.changedPrimaryKeys = diff.changedPrimaryKeys
|
|
403
|
+
.filter(an => diffFilterObj.changedPrimaryKeys(an));
|
|
401
404
|
}
|
|
402
405
|
|
|
403
406
|
const identifierUtils = sqlUtils.getIdentifierUtils(csn, internalOptions);
|
|
@@ -530,7 +533,7 @@ function sqlMigration( csn, options, beforeImage ) {
|
|
|
530
533
|
* @returns {migration} The migration result
|
|
531
534
|
*/
|
|
532
535
|
function hdiMigration( csn, options, beforeImage ) {
|
|
533
|
-
traceApi(
|
|
536
|
+
traceApi('to.hdi.migration', options);
|
|
534
537
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
535
538
|
|
|
536
539
|
// Prepare after-image.
|
|
@@ -615,7 +618,7 @@ sql.migration = sqlMigration;
|
|
|
615
618
|
* @returns {HDBCDS} { <filename>:<content>, ...}
|
|
616
619
|
*/
|
|
617
620
|
function hdbcds( csn, options = {} ) {
|
|
618
|
-
traceApi(
|
|
621
|
+
traceApi('to.hdbcds', options);
|
|
619
622
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
620
623
|
internalOptions.transformation = 'hdbcds';
|
|
621
624
|
|
|
@@ -632,7 +635,7 @@ function hdbcds( csn, options = {} ) {
|
|
|
632
635
|
* @returns {edm} The JSON representation of the service
|
|
633
636
|
*/
|
|
634
637
|
function edm( csn, options = {} ) {
|
|
635
|
-
traceApi(
|
|
638
|
+
traceApi('to.edm', options);
|
|
636
639
|
// If not provided at all, set service to undefined to trigger validation
|
|
637
640
|
const internalOptions = prepareOptions.to.edm(
|
|
638
641
|
// eslint-disable-next-line comma-dangle
|
|
@@ -663,7 +666,7 @@ edm.all = edmall;
|
|
|
663
666
|
* @returns {edms} { <service>:<JSON representation>, ...}
|
|
664
667
|
*/
|
|
665
668
|
function edmall( csn, options = {} ) {
|
|
666
|
-
traceApi(
|
|
669
|
+
traceApi('to.edm.all', options);
|
|
667
670
|
const internalOptions = prepareOptions.to.edm(options);
|
|
668
671
|
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
669
672
|
|
|
@@ -694,7 +697,7 @@ function edmall( csn, options = {} ) {
|
|
|
694
697
|
* @returns {edmx} The XML representation of the service
|
|
695
698
|
*/
|
|
696
699
|
function edmx( csn, options = {} ) {
|
|
697
|
-
traceApi(
|
|
700
|
+
traceApi('to.edmx', options);
|
|
698
701
|
// If not provided at all, set service to undefined to trigger validation
|
|
699
702
|
const internalOptions = prepareOptions.to.edmx(
|
|
700
703
|
// eslint-disable-next-line comma-dangle
|
|
@@ -726,7 +729,7 @@ edmx.all = edmxall;
|
|
|
726
729
|
* @returns {edmxs} { <service>:<XML representation>, ...}
|
|
727
730
|
*/
|
|
728
731
|
function edmxall( csn, options = {} ) {
|
|
729
|
-
traceApi(
|
|
732
|
+
traceApi('to.edmx.all', options);
|
|
730
733
|
const internalOptions = prepareOptions.to.edmx(options);
|
|
731
734
|
|
|
732
735
|
const result = {};
|
|
@@ -843,19 +846,6 @@ function flattenResultStructure( toProcess ) {
|
|
|
843
846
|
return result;
|
|
844
847
|
}
|
|
845
848
|
|
|
846
|
-
/**
|
|
847
|
-
* Print args to stderr if CDSC_TRACE_API is set
|
|
848
|
-
*
|
|
849
|
-
* @param {...any} args to be logged to stderr
|
|
850
|
-
*/
|
|
851
|
-
function traceApi( ...args ) {
|
|
852
|
-
if (process?.env?.CDSC_TRACE_API !== undefined) {
|
|
853
|
-
for (const arg of args) {
|
|
854
|
-
// eslint-disable-next-line no-console
|
|
855
|
-
console.error( `${ typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg }`);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
849
|
|
|
860
850
|
module.exports = {
|
|
861
851
|
odata: publishCsnProcessor(odata, 'for.odata'),
|
package/lib/api/options.js
CHANGED
package/lib/api/trace.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const shouldTraceApi = process?.env?.CDSC_TRACE_API;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Placeholder for disabled tracing (no-op).
|
|
8
|
+
*
|
|
9
|
+
* @param {string} apiName API name
|
|
10
|
+
* @param {object} options Options passed to the API.
|
|
11
|
+
* @param {...any} [args] Arguments to be logged to stderr
|
|
12
|
+
*/
|
|
13
|
+
// eslint-disable-next-line no-unused-vars
|
|
14
|
+
function noOp( apiName, options, ...args ) {
|
|
15
|
+
// no-op
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Print args to stderr if CDSC_TRACE_API is set
|
|
20
|
+
*
|
|
21
|
+
* @param {string} apiName API name
|
|
22
|
+
* @param {object} options Options passed to the API.
|
|
23
|
+
* @param {...any} [args] Arguments to be logged to stderr
|
|
24
|
+
*/
|
|
25
|
+
function traceApi( apiName, options, ...args ) {
|
|
26
|
+
const optStr = typeof options === 'object' ? JSON.stringify(options, null, 2) : options;
|
|
27
|
+
const argsStr = args.map(val => JSON.stringify(val)).join(', ');
|
|
28
|
+
const rest = args.length > 0 ? ` | ${ argsStr }` : '';
|
|
29
|
+
// Local require: Only load on-demand, not when tracing is disabled.
|
|
30
|
+
// eslint-disable-next-line global-require
|
|
31
|
+
const { version } = require('../../package.json');
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.error( `CDSC_TRACE_API | ${ version } | ${ apiName }(…) | options: ${ optStr }${ rest }`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
traceApi: shouldTraceApi ? traceApi : noOp,
|
|
38
|
+
};
|
package/lib/base/location.js
CHANGED
|
@@ -76,7 +76,35 @@ function emptyWeakLocation( filename ) {
|
|
|
76
76
|
* @returns {CsnLocation}
|
|
77
77
|
*/
|
|
78
78
|
function weakLocation( loc ) {
|
|
79
|
-
return {
|
|
79
|
+
return (!loc?.endLine) ? loc : {
|
|
80
|
+
__proto__: CsnLocation.prototype,
|
|
81
|
+
file: loc.file,
|
|
82
|
+
line: loc.line,
|
|
83
|
+
col: loc.col,
|
|
84
|
+
endLine: undefined,
|
|
85
|
+
endCol: undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Return a location to be used for compiler-generated artifacts whose location is
|
|
91
|
+
* best derived from a reference (`type`, `includes`, `target`, `value`) or a name.
|
|
92
|
+
* Omit the end position to indicate that this is just an approximate location.
|
|
93
|
+
*
|
|
94
|
+
* If represented by a `path` (not always the case for a `name`), use the location
|
|
95
|
+
* of its last item. Reason: think of an IDE functionality “Go to Definition” – only
|
|
96
|
+
* a double-click on the _last_ identifier token of the reference jumps to the artifact
|
|
97
|
+
* represented by the complete reference.
|
|
98
|
+
*
|
|
99
|
+
* @param {CsnLocation} loc
|
|
100
|
+
* @returns {CsnLocation}
|
|
101
|
+
*/
|
|
102
|
+
function weakRefLocation( ref ) {
|
|
103
|
+
if (!ref)
|
|
104
|
+
return ref;
|
|
105
|
+
const { path } = ref;
|
|
106
|
+
const loc = path?.length ? path[path.length - 1].location : ref.location;
|
|
107
|
+
return (!loc?.endLine) ? loc : {
|
|
80
108
|
__proto__: CsnLocation.prototype,
|
|
81
109
|
file: loc.file,
|
|
82
110
|
line: loc.line,
|
|
@@ -86,6 +114,21 @@ function weakLocation( loc ) {
|
|
|
86
114
|
};
|
|
87
115
|
}
|
|
88
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @param {CsnLocation} loc
|
|
119
|
+
* @returns {CsnLocation}
|
|
120
|
+
*/
|
|
121
|
+
function weakEndLocation( loc ) {
|
|
122
|
+
return loc && {
|
|
123
|
+
__proto__: CsnLocation.prototype,
|
|
124
|
+
file: loc.file,
|
|
125
|
+
line: loc.endLine,
|
|
126
|
+
col: loc.endCol && loc.endCol - 1,
|
|
127
|
+
endline: undefined,
|
|
128
|
+
endCol: undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
89
132
|
/**
|
|
90
133
|
* Returns a dummy location for built-in definitions.
|
|
91
134
|
*
|
|
@@ -172,6 +215,8 @@ module.exports = {
|
|
|
172
215
|
emptyLocation,
|
|
173
216
|
emptyWeakLocation,
|
|
174
217
|
weakLocation,
|
|
218
|
+
weakRefLocation,
|
|
219
|
+
weakEndLocation,
|
|
175
220
|
builtinLocation,
|
|
176
221
|
dictLocation,
|
|
177
222
|
locationString,
|
|
@@ -57,11 +57,11 @@ const centralMessages = {
|
|
|
57
57
|
'anno-invalid-sql-kind': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
58
58
|
'anno-invalid-sql-view': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
59
59
|
'anno-invalid-sql-view-element': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
60
|
-
'
|
|
61
|
-
'
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
'
|
|
60
|
+
'ext-undefined-action': { severity: 'Warning' },
|
|
61
|
+
'ext-undefined-art': { severity: 'Warning' }, // for annotate statement (for CDL path root)
|
|
62
|
+
'ext-undefined-def': { severity: 'Warning' }, // for annotate statement (for CSN or CDL path cont)
|
|
63
|
+
'ext-undefined-element': { severity: 'Warning' },
|
|
64
|
+
'ext-undefined-param': { severity: 'Warning' },
|
|
65
65
|
'anno-unexpected-ellipsis': { severity: 'Error', configurableFor: 'deprecated' },
|
|
66
66
|
'anno-unexpected-localized-skip': { severity: 'Error', configurableFor: true },
|
|
67
67
|
|
|
@@ -105,7 +105,6 @@ const centralMessages = {
|
|
|
105
105
|
|
|
106
106
|
'param-default': { severity: 'Error' }, // not supported yet
|
|
107
107
|
|
|
108
|
-
'query-undefined-element': { severity: 'Error' },
|
|
109
108
|
'query-unexpected-assoc-hdbcds': { severity: 'Error' },
|
|
110
109
|
'query-unexpected-structure-hdbcds': { severity: 'Error' },
|
|
111
110
|
'query-ignoring-param-nullability': { severity: 'Info' },
|
|
@@ -122,10 +121,12 @@ const centralMessages = {
|
|
|
122
121
|
'ref-undefined-def': { severity: 'Error' },
|
|
123
122
|
'ref-undefined-var': { severity: 'Error' },
|
|
124
123
|
'ref-undefined-element': { severity: 'Error' },
|
|
124
|
+
'anno-undefined-element': { severity: 'Warning' },
|
|
125
125
|
'ref-unknown-var': { severity: 'Info', errorFor: [ 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename' ] },
|
|
126
126
|
'ref-obsolete-parameters': { severity: 'Error', configurableFor: 'v4' },
|
|
127
127
|
// does not hurt us, but makes it tedious to detect parameter refs
|
|
128
128
|
'ref-undefined-param': { severity: 'Error' },
|
|
129
|
+
'anno-undefined-param': { severity: 'Warning' },
|
|
129
130
|
'ref-rejected-on': { severity: 'Error' },
|
|
130
131
|
'ref-expected-element': { severity: 'Error' },
|
|
131
132
|
|
|
@@ -159,7 +160,6 @@ const centralMessages = {
|
|
|
159
160
|
'syntax-unsupported-masked': { severity: 'Error', configurableFor: 'deprecated' },
|
|
160
161
|
'syntax-unexpected-sql-clause': { severity: 'Error' }, // TODO: configurableFor:'tests'?
|
|
161
162
|
|
|
162
|
-
'type-managed-composition': { severity: 'Error' },
|
|
163
163
|
'type-unsupported-precision-change': { severity: 'Error' },
|
|
164
164
|
'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
|
|
165
165
|
|
|
@@ -537,6 +537,8 @@ const centralMessageTexts = {
|
|
|
537
537
|
on: 'Unexpected $(ID) reference; is valid only if compared to be equal to an association of the target side',
|
|
538
538
|
subQuery: 'Unexpected $(ID) reference in a sub query',
|
|
539
539
|
setQuery: 'Unexpected $(ID) reference in a query on the right side of $(OP)',
|
|
540
|
+
exists: 'With $(NAME), path steps must not start with $(ID)',
|
|
541
|
+
'exists-filter': 'Unexpected $(ID) reference in filter of path $(ELEMREF) following “EXISTS” predicate',
|
|
540
542
|
},
|
|
541
543
|
'ref-undefined-def': {
|
|
542
544
|
std: 'Artifact $(ART) has not been found',
|
|
@@ -544,16 +546,29 @@ const centralMessageTexts = {
|
|
|
544
546
|
element: 'Artifact $(ART) has no element $(MEMBER)',
|
|
545
547
|
},
|
|
546
548
|
'ref-undefined-param': 'Entity $(ART) has no parameter $(ID)',
|
|
549
|
+
'anno-undefined-param': {
|
|
550
|
+
std: 'Entity $(ART) has no parameter $(ID)',
|
|
551
|
+
entity: 'Entity $(ART) has no parameter $(ID)',
|
|
552
|
+
action: 'Action $(ART) has no parameter $(ID)',
|
|
553
|
+
},
|
|
547
554
|
'ref-undefined-art': 'No artifact has been found with name $(ART)',
|
|
548
555
|
// TODO: proposal 'No definition found for $(NAME)',
|
|
549
556
|
'ref-undefined-element': {
|
|
550
557
|
std: 'Element $(ART) has not been found',
|
|
551
558
|
element: 'Artifact $(ART) has no element $(MEMBER)',
|
|
552
559
|
target: 'Target entity $(ART) has no element $(ID)',
|
|
553
|
-
aspect: 'Element $(ID) has not been found in the anonymous target aspect',
|
|
560
|
+
aspect: 'Element $(ID) has not been found in the anonymous target aspect', // TODO: still?
|
|
554
561
|
query: 'The current query has no element $(ART)',
|
|
562
|
+
alias: 'Element $(ART) has not been found in the sub query for alias $(ALIAS)',
|
|
555
563
|
virtual: 'Element $(ART) has not been found. Use $(CODE) to add virtual elements in queries'
|
|
556
564
|
},
|
|
565
|
+
'anno-undefined-element': {
|
|
566
|
+
std: 'Element $(ART) has not been found',
|
|
567
|
+
element: 'Artifact $(ART) has no element $(MEMBER)',
|
|
568
|
+
target: 'Target entity $(ART) has no element $(ID)',
|
|
569
|
+
query: 'The current query has no element $(ART)',
|
|
570
|
+
alias: 'Element $(ART) has not been found in the sub query for alias $(ALIAS)',
|
|
571
|
+
},
|
|
557
572
|
'ref-undefined-var': {
|
|
558
573
|
std: 'Variable $(ID) has not been found',
|
|
559
574
|
alias: 'Variable $(ID) has not been found. Use table alias $(ALIAS) to refer an element with the same name',
|
|
@@ -642,6 +657,9 @@ const centralMessageTexts = {
|
|
|
642
657
|
select: 'Unexpected $(KEYWORD) for type references in queries',
|
|
643
658
|
annotation: '$(KEYWORD) can\'t be used in annotation definitions',
|
|
644
659
|
},
|
|
660
|
+
'type-unexpected-assoc': {
|
|
661
|
+
std: 'An unmanaged association can\'t be used as type',
|
|
662
|
+
},
|
|
645
663
|
|
|
646
664
|
'type-missing-argument': 'Missing value for argument $(NAME) in reference to type $(ID)',
|
|
647
665
|
'type-ignoring-argument': 'Too many arguments for type $(ART)',
|
|
@@ -672,20 +690,20 @@ const centralMessageTexts = {
|
|
|
672
690
|
},
|
|
673
691
|
|
|
674
692
|
'anno-builtin': 'Builtin types should not be annotated. Use custom type instead',
|
|
675
|
-
'
|
|
676
|
-
'
|
|
677
|
-
'
|
|
693
|
+
'ext-undefined-def': 'Artifact $(ART) has not been found',
|
|
694
|
+
'ext-undefined-art': 'No artifact has been found with name $(ART)',
|
|
695
|
+
'ext-undefined-element': {
|
|
678
696
|
std: 'Element $(NAME) has not been found',
|
|
679
697
|
element: 'Artifact $(ART) has no element $(NAME)',
|
|
680
698
|
enum: 'Artifact $(ART) has no enum $(NAME)',
|
|
681
699
|
returns: 'Return value of $(ART) has no element $(NAME)',
|
|
682
700
|
'enum-returns': 'Return value of $(ART) has no enum $(NAME)',
|
|
683
701
|
},
|
|
684
|
-
'
|
|
702
|
+
'ext-undefined-action': {
|
|
685
703
|
std: 'Action $(ART) has not been found',
|
|
686
704
|
action: 'Artifact $(ART) has no action $(NAME)'
|
|
687
705
|
},
|
|
688
|
-
'
|
|
706
|
+
'ext-undefined-param': {
|
|
689
707
|
std: 'Parameter $(ART) has not been found',
|
|
690
708
|
param: 'Artifact $(ART) has no parameter $(NAME)'
|
|
691
709
|
},
|
|
@@ -793,10 +811,20 @@ const centralMessageTexts = {
|
|
|
793
811
|
'ref-expecting-const': 'A constant expression or variable is expected here',
|
|
794
812
|
'ref-expecting-foreign-key': 'Expecting foreign key access after managed association $(NAME) in filter expression of $(ID), but found $(ALIAS)',
|
|
795
813
|
'ref-invalid-target': {
|
|
796
|
-
std: '
|
|
814
|
+
std: 'Expecting an entity as target',
|
|
797
815
|
composition: 'Expecting an entity or aspect as composition target',
|
|
798
816
|
bare: 'Expecting the target aspect to have elements',
|
|
799
|
-
aspect: 'Expecting
|
|
817
|
+
aspect: 'Expecting an aspect in property $(PROP)', // `targetAspect` in CSN input
|
|
818
|
+
redirected: 'Expecting an entity as target; a target aspect can\'t be specified for redirections',
|
|
819
|
+
// a `target aspect alone` would be more correct, but confusing if in `target`
|
|
820
|
+
// property, which is the standard (extra text variants would be too much):
|
|
821
|
+
entity: 'Expecting an entity as target; a target aspect can\'t be specified for projection elements',
|
|
822
|
+
event: 'Expecting an entity as target; a target aspect can\'t be specified in an event',
|
|
823
|
+
select: 'Expecting an entity as target; a target aspect can\'t be specified in a query',
|
|
824
|
+
type: 'Expecting an entity as target; a target aspect can\'t be specified for a type',
|
|
825
|
+
param: 'Expecting an entity as target; a target aspect can\'t be specified for a parameter',
|
|
826
|
+
annotation: 'Expecting an entity as target; a target aspect can\'t be specified for an annotation',
|
|
827
|
+
sub: 'Expecting an entity as target; a target aspect can\'t be specified for a sub element',
|
|
800
828
|
},
|
|
801
829
|
'ref-invalid-include': {
|
|
802
830
|
std: 'A type, entity, aspect or event with direct elements is expected here',
|
|
@@ -965,9 +993,12 @@ const centralMessageTexts = {
|
|
|
965
993
|
'odata-spec-violation-type': {
|
|
966
994
|
std: 'Expected element to have a type',
|
|
967
995
|
incompatible: 'Unexpected EDM Type $(TYPE) for OData $(VERSION)',
|
|
996
|
+
incompatible_anno: 'Unexpected EDM Type $(TYPE) for OData $(VERSION) in $(ANNO)',
|
|
968
997
|
facet: 'Unexpected EDM Type facet $(NAME) of type $(TYPE) for OData $(VERSION)',
|
|
998
|
+
facet_anno: 'Unexpected EDM Type facet $(NAME) of type $(TYPE) for OData $(VERSION) in $(ANNO)',
|
|
969
999
|
external: 'Referenced type $(TYPE) marked as $(ANNO) can\'t be rendered as $(CODE) in service $(NAME) for OData $(VERSION)',
|
|
970
|
-
scale: 'Expected scale $(NUMBER) to be less than or equal to precision $(RAWVALUE)'
|
|
1000
|
+
scale: 'Expected scale $(NUMBER) to be less than or equal to precision $(RAWVALUE)',
|
|
1001
|
+
scale_anno: 'Expected scale $(NUMBER) to be less than or equal to precision $(RAWVALUE) in $(ANNO)'
|
|
971
1002
|
},
|
|
972
1003
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(META)',
|
|
973
1004
|
'odata-spec-violation-namespace': {
|
|
@@ -1050,6 +1081,27 @@ const centralMessageTexts = {
|
|
|
1050
1081
|
'deprecated': '$(ANNO) is deprecated. $(DEPR)',
|
|
1051
1082
|
'notapplied': '$(ANNO) is not applied (AppliesTo: $(RAWVALUES))',
|
|
1052
1083
|
},
|
|
1084
|
+
'odata-anno-xpr': {
|
|
1085
|
+
'std': 'unused',
|
|
1086
|
+
'notadynexpr': '$(OP) is not a renderable dynamic expression in $(ANNO)',
|
|
1087
|
+
'use': 'Function $(OP) is not a renderable dynamic expression in $(ANNO), please use $(CODE) instead',
|
|
1088
|
+
'canonfuncalias': 'Expected function name $(CODE) to be of the form $(META).$(OTHERMETA) for $(OP) in $(ANNO)',
|
|
1089
|
+
}
|
|
1090
|
+
,
|
|
1091
|
+
'odata-anno-xpr-type': {
|
|
1092
|
+
'std': 'Expected one qualified type name for $(OP) in $(ANNO)'
|
|
1093
|
+
},
|
|
1094
|
+
'odata-anno-xpr-args': {
|
|
1095
|
+
'std': 'Unexpected arguments for $(OP) in $(ANNO)',
|
|
1096
|
+
'exactly': 'Expected exactly $(COUNT) argument(s) for $(OP) in $(ANNO)',
|
|
1097
|
+
'atleast': 'Expected at least $(COUNT) argument(s) for $(OP) in $(ANNO)',
|
|
1098
|
+
'atmost': 'Expected at most $(COUNT) argument(s) for $(OP) in $(ANNO)',
|
|
1099
|
+
'wrongcount': 'Expected exactly one $(PROP) for $(OP) in $(ANNO)',
|
|
1100
|
+
'wrongref': 'Unexpected arguments or filters in $(ELEMREF) in $(ANNO)',
|
|
1101
|
+
'wrongval': 'Unexpected value for $(OP) in $(ANNO)',
|
|
1102
|
+
'wrongval_meta': 'Expected value for $(OP) to be a $(META) in $(ANNO)',
|
|
1103
|
+
'wrongval_meta_list': 'Expected value for $(OP) to be a $(META) or $(RAWVALUES) in $(ANNO)',
|
|
1104
|
+
}
|
|
1053
1105
|
// -----------------------------------------------------------------------------------
|
|
1054
1106
|
// OData Message section ends here, no messages below this line
|
|
1055
1107
|
// -----------------------------------------------------------------------------------
|
package/lib/base/messages.js
CHANGED
|
@@ -428,8 +428,9 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
428
428
|
}
|
|
429
429
|
|
|
430
430
|
let semanticLocation = location[1] ? homeName( location[1], false ) : null;
|
|
431
|
-
if (location[2]) // optional suffix
|
|
432
|
-
semanticLocation += `/${ location[2] }`;
|
|
431
|
+
if (location[2]) { // optional suffix, e.g. annotation
|
|
432
|
+
semanticLocation += `/${ (typeof location[2] === 'string') ? location[2]: homeName(location[2]) }`;
|
|
433
|
+
}
|
|
433
434
|
|
|
434
435
|
const definition = location[1] ? homeName( location[1], true ) : null;
|
|
435
436
|
|
|
@@ -676,6 +677,7 @@ const paramsTransform = {
|
|
|
676
677
|
newcode: quote.single,
|
|
677
678
|
kind: quote.single,
|
|
678
679
|
meta: quote.angle,
|
|
680
|
+
othermeta: quote.angle,
|
|
679
681
|
keyword,
|
|
680
682
|
// more complex convenience:
|
|
681
683
|
names: transformManyWith( quoted ),
|
|
@@ -1573,6 +1575,9 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1573
1575
|
result += '/target';
|
|
1574
1576
|
break;
|
|
1575
1577
|
}
|
|
1578
|
+
else if (step === 'targetAspect') {
|
|
1579
|
+
// skip
|
|
1580
|
+
}
|
|
1576
1581
|
else if (step === 'xpr' || step === 'ref' || step === 'as' || step === 'value') {
|
|
1577
1582
|
break; // don't go into xprs, refs, aliases, values, etc.
|
|
1578
1583
|
}
|
|
@@ -1608,7 +1613,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1608
1613
|
}
|
|
1609
1614
|
else {
|
|
1610
1615
|
if (options.testMode)
|
|
1611
|
-
throw new CompilerAssertion(`semantic location:
|
|
1616
|
+
throw new CompilerAssertion(`semantic location: Unhandled segment: ${ csnPath[index] } for path ${ JSON.stringify( csnPath) }`);
|
|
1612
1617
|
break;
|
|
1613
1618
|
}
|
|
1614
1619
|
next();
|
|
@@ -137,7 +137,7 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
137
137
|
// if (!(isMultiSchema && serviceOfType)) {
|
|
138
138
|
this.error(null, currPath,
|
|
139
139
|
{ type: typeName, kind: type.kind, service: serviceName },
|
|
140
|
-
'$(
|
|
140
|
+
'Referenced $(KIND) $(TYPE) can\'t be used in service $(SERVICE) because it is not defined in $(SERVICE)');
|
|
141
141
|
// }
|
|
142
142
|
}
|
|
143
143
|
}
|
|
@@ -24,8 +24,8 @@ function checkCoreMediaTypeAllowance( member ) {
|
|
|
24
24
|
'cds.hana.BINARY': 1,
|
|
25
25
|
};
|
|
26
26
|
if (member['@Core.MediaType'] && member.type && !(this.csnUtils.getFinalTypeInfo(member.type)?.type in allowedCoreMediaTypes)) {
|
|
27
|
-
this.warning(null, member.$path, { names: [ 'Edm.String', 'Edm.Binary' ] },
|
|
28
|
-
'Element annotated with
|
|
27
|
+
this.warning(null, member.$path, { anno: '@Core.MediaType', names: [ 'Edm.String', 'Edm.Binary' ] },
|
|
28
|
+
'Element annotated with $(ANNO) should be of a type mapped to $(NAMES)');
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -45,11 +45,14 @@ function validateSelectItems( query ) {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
const transformers = {
|
|
48
|
+
/*
|
|
48
49
|
columns: aTCB,
|
|
49
50
|
groupBy: aTCB,
|
|
50
|
-
orderBy: aTCB,
|
|
51
51
|
having: aTCB,
|
|
52
52
|
where: aTCB,
|
|
53
|
+
*/
|
|
54
|
+
orderBy: aTCB, // filters in order by imply a join, not allowed
|
|
55
|
+
from: aTCB, // $self refs in from clause filters are not allowed
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
if (this.options.transformation === 'hdbcds') {
|
|
@@ -473,7 +473,7 @@ function assertConsistency( model, stage ) {
|
|
|
473
473
|
optional: [
|
|
474
474
|
'literal', 'val', 'sym', 'struct', 'variant', 'path', 'name', '$duplicates', 'upTo',
|
|
475
475
|
// expressions as annotation values
|
|
476
|
-
'$tokenTexts', 'op', 'args', 'func',
|
|
476
|
+
'$tokenTexts', 'op', 'args', 'func', '_artifact', 'type',
|
|
477
477
|
],
|
|
478
478
|
// TODO: restrict path to #simplePath
|
|
479
479
|
},
|
|
@@ -679,6 +679,7 @@ function assertConsistency( model, stage ) {
|
|
|
679
679
|
'keys',
|
|
680
680
|
'localized', // e.g. compiler-generated elements for localized: `text` assoc, etc.
|
|
681
681
|
'localized-entity', // `.texts` entity
|
|
682
|
+
'localized-origin', // `.texts` entity
|
|
682
683
|
'nav', // only used for MASKED, TODO(v5): Remove
|
|
683
684
|
'none', // only used in ensureColumnName(): Used in object representing empty alias
|
|
684
685
|
'query', // inferred query properties, e.g. `key`
|
|
@@ -966,7 +967,7 @@ function assertConsistency( model, stage ) {
|
|
|
966
967
|
|
|
967
968
|
function isOneOf( values ) {
|
|
968
969
|
return function isOneOfInner( node, parent, prop ) {
|
|
969
|
-
if (!values.includes( node ))
|
|
970
|
+
if (!values.includes( node ) && node !== undefined)
|
|
970
971
|
throw new InternalConsistencyError( `Unexpected value '${ node }', expected ${ JSON.stringify( values ) }${ at( [ node, parent ], prop ) }` );
|
|
971
972
|
};
|
|
972
973
|
}
|
package/lib/compiler/base.js
CHANGED
|
@@ -82,7 +82,7 @@ function getArtifactName( art ) {
|
|
|
82
82
|
const { name } = art;
|
|
83
83
|
if (!name) // no name
|
|
84
84
|
return name;
|
|
85
|
-
if (!art.kind) // annotation assignments
|
|
85
|
+
if (!art.kind) // annotation assignments, $self param type
|
|
86
86
|
return { ...art.name, absolute: art.name.id };
|
|
87
87
|
if (art.kind === 'using')
|
|
88
88
|
return { ...art.name, absolute: art.extern.id };
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -62,6 +62,19 @@ const typeParameters = {
|
|
|
62
62
|
// a.k.a "typeProperties"
|
|
63
63
|
typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Properties that are required next to `=` to make an annotation value an actual expression
|
|
67
|
+
* and not some foreign structure.
|
|
68
|
+
*
|
|
69
|
+
* @type {string[]}
|
|
70
|
+
*/
|
|
71
|
+
const xprInAnnoProperties = [
|
|
72
|
+
'ref', 'xpr', 'list', 'literal', 'val',
|
|
73
|
+
'#', 'func', 'args', 'SELECT', 'SET',
|
|
74
|
+
'cast',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
|
|
65
78
|
// const hana = {
|
|
66
79
|
// BinaryFloat: {},
|
|
67
80
|
// LocalDate: {},
|
|
@@ -178,7 +191,7 @@ function compileArg( src ) {
|
|
|
178
191
|
const magicVariables = {
|
|
179
192
|
$user: {
|
|
180
193
|
// id and locale are always available
|
|
181
|
-
elements: { id: {}, locale: {} },
|
|
194
|
+
elements: { id: {}, locale: {}, tenant: {} },
|
|
182
195
|
// Allow $user.<any>
|
|
183
196
|
$uncheckedElements: true,
|
|
184
197
|
// Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
|
|
@@ -301,6 +314,15 @@ function checkDate( year, month, day ) {
|
|
|
301
314
|
return !Number.isNaN( year ) && month > 0 && month < 13 && day > 0 && day < 32;
|
|
302
315
|
}
|
|
303
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Return whether JSON object `val` is a representation for an annotation expression
|
|
319
|
+
*/
|
|
320
|
+
function isAnnotationExpression( val ) {
|
|
321
|
+
// TODO: we might allow `'=': true`, not just a string, for expressions, to be
|
|
322
|
+
// decided → just check truthy at the moment
|
|
323
|
+
return val['='] && xprInAnnoProperties.some( prop => val[prop] !== undefined );
|
|
324
|
+
}
|
|
325
|
+
|
|
304
326
|
/**
|
|
305
327
|
* Check that the given time is within boundaries.
|
|
306
328
|
* Checks according to ISO 8601.
|
|
@@ -535,10 +557,12 @@ function initBuiltins( model ) {
|
|
|
535
557
|
|
|
536
558
|
module.exports = {
|
|
537
559
|
typeParameters,
|
|
560
|
+
xprInAnnoProperties,
|
|
538
561
|
functionsWithoutParens,
|
|
539
562
|
specialFunctions,
|
|
540
563
|
quotedLiteralPatterns,
|
|
541
564
|
initBuiltins,
|
|
565
|
+
isAnnotationExpression,
|
|
542
566
|
isInReservedNamespace,
|
|
543
567
|
isBuiltinType,
|
|
544
568
|
isIntegerTypeName,
|