@sap/cds-compiler 2.7.0 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/lib/api/main.js +8 -10
- package/lib/api/options.js +13 -9
- package/lib/api/validate.js +11 -8
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +16 -0
- package/lib/base/messages.js +2 -0
- package/lib/base/model.js +1 -0
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +3 -1
- package/lib/compiler/definer.js +87 -29
- package/lib/compiler/resolver.js +75 -16
- package/lib/compiler/shared.js +29 -9
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +93 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +274 -83
- package/lib/edm/edmUtils.js +29 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4727 -4323
- package/lib/json/from-csn.js +52 -23
- package/lib/json/to-csn.js +185 -71
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +9 -0
- package/lib/language/language.g4 +90 -31
- package/lib/main.js +4 -0
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +7 -1
- package/lib/model/csnUtils.js +5 -4
- package/lib/optionProcessor.js +7 -1
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/toCdl.js +45 -9
- package/lib/render/toHdbcds.js +100 -34
- package/lib/render/toSql.js +12 -4
- package/lib/render/utils/common.js +5 -9
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +14 -4
- package/lib/transform/db/flattening.js +13 -5
- package/lib/transform/db/transformExists.js +252 -58
- package/lib/transform/forHanaNew.js +7 -1
- package/lib/transform/forOdataNew.js +12 -8
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +33 -1
- package/lib/transform/translateAssocsToJoins.js +6 -4
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,109 @@
|
|
|
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.10.2 - 2021-10-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- to.sql/hdi/hdbcds: Correctly handle `exists` in conjunction with mixin-associations
|
|
15
|
+
|
|
16
|
+
## Version 2.10.0 - 2021-10-28
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Support arbitrary paths after `$user` - similar to `$session`.
|
|
21
|
+
- Support scale `floating` and `variable` for `cds.Decimal` in CDL and CSN. Backend specific handling is descibed in their sections.
|
|
22
|
+
- Allow select item wildcard (`*`) in a `select`/`projection` at any position, not just the first.
|
|
23
|
+
|
|
24
|
+
- to.edm(x):
|
|
25
|
+
+ In Odata V4 generate transitive navigation property binding paths along containment hierarchies and terminate on the
|
|
26
|
+
first non-containment association. The association target is either an explicit Edm.EntitySet in the same EntityContainer
|
|
27
|
+
or in a referred EntityContainer (cross service references) or an implicit EntitySet identified by the containment path
|
|
28
|
+
originating from an explicit EntitySet. This enhancement has an observable effect only in structured format with containment
|
|
29
|
+
turned on.
|
|
30
|
+
+ Support for scales `variable` and `floating`:
|
|
31
|
+
+ V4: `variable` and `floating` are rendered as `Scale="variable"`. Since V4 does not support `floating`, it is aproximated as `variable`.
|
|
32
|
+
+ V2: `variable` and `floating` are announced via property annotation `sap:variable-scale="true"`
|
|
33
|
+
|
|
34
|
+
- to.sql/hdi/hdbcds:
|
|
35
|
+
+ Reject scale `floating` and `variable`.
|
|
36
|
+
+ Reject arbitrary `$user` or `$session` paths that cannot be translated to valid SQL.
|
|
37
|
+
+ Following a valid `exists`, further `exists` can be used inside of the filter-expression: `exists assoc[exists another[1=1]]`
|
|
38
|
+
+ `exists` can now be followed by more than one association step.
|
|
39
|
+
`exists assoc.anotherassoc.moreassoc` is semantically equivalent to `exists assoc[exists anotherassoc[exists moreassoc]]`
|
|
40
|
+
|
|
41
|
+
### Removed
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- to.odata: Inform when overwriting draft action annotations like @Common.DraftRoot.ActivationAction.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
## Version 2.9.0 - 2021-10-15
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- to.edm(x): Raise `odata-spec-violation-type` to a downgradable error.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- to.edm(x):
|
|
58
|
+
+ Fix a bug in annotation propagation to foreign keys.
|
|
59
|
+
+ Don't render annotations for not rendered stream element in V2.
|
|
60
|
+
- to.hdi:
|
|
61
|
+
+ for naming mode "hdbcds" and "quoted" parameter definitions are not quoted anymore.
|
|
62
|
+
- to.hdi/sql/hdbcds:
|
|
63
|
+
+ Correctly handle explicit and implicit alias during flattening.
|
|
64
|
+
+ Raise an error for `@odata.draft.enabled` artifacts with elements without types - instead of crashing with internal assertions.
|
|
65
|
+
|
|
66
|
+
## Version 2.8.0 - 2021-10-07
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- Allow defining unmanaged associations in anonymous aspects of compositions.
|
|
71
|
+
- Enable extensions of anonymous aspects for managed compositions of aspects.
|
|
72
|
+
- When the option `addTextsLanguageAssoc` is set to true and
|
|
73
|
+
the model contains an entity `sap.common.Languages` with an element `code`,
|
|
74
|
+
all generated texts entities additionally contain an element `language`
|
|
75
|
+
which is an association to `sap.common.Languages` using element `local`.
|
|
76
|
+
- for.odata:
|
|
77
|
+
+ In `--odata-format=flat`, structured view parameters are flattened like elements.
|
|
78
|
+
- to.hdbcds
|
|
79
|
+
+ Use "smart quotes" for naming mode "plain" - automatically quote identifier which are reserved keywords or non-regular.
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- for.odata:
|
|
84
|
+
+ In `--data-format=structured`, anonymous sub elements of primary keys and parameters are set to `notNull:true`,
|
|
85
|
+
an existing `notNull` attribute is _not_ overwritten. Referred named types are _not_ modified.
|
|
86
|
+
- to.edm(x):
|
|
87
|
+
+ Improve specification violation checks of (nested) keys:
|
|
88
|
+
+ All (sub-)elements must be `Nullable: false` (error).
|
|
89
|
+
+ Must represent a single value (error).
|
|
90
|
+
+ In V4 must be a specification compliant Edm.PrimitiveType (warning).
|
|
91
|
+
- to.hdi/hdbcds/sql: $user.\<xy\> now has \<xy\> added as alias - "$user.\<xy\> as \<xy\>"
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
|
|
95
|
+
- Properly generate auto-exposed entities for associations in parameters.
|
|
96
|
+
- Correctly apply extensions to anonymous array item types.
|
|
97
|
+
- Correctly apply/render annotations to anonymous action return types.
|
|
98
|
+
- With CSN flavor `plain` (`gensrc`), correctly render annotations on elements
|
|
99
|
+
of referred structure types as `annotate` statements in the CSN's `extensions` property.
|
|
100
|
+
- to.cdl:
|
|
101
|
+
+ Correctly render extensions on array item types
|
|
102
|
+
+ Correctly render annotations on action return types
|
|
103
|
+
- to/for: Correctly handle CSN input where the prototype of objects is not the "default"
|
|
104
|
+
- to.hdi:
|
|
105
|
+
+ for naming mode "hdbcds" and "quoted" parameter definitions are now quoted.
|
|
106
|
+
+ for naming mode "plain", smart quotation is applied to parameter definitions if they are reserved words.
|
|
107
|
+
- to.hdi/hdbcds/sql:
|
|
108
|
+
+ Ensure that cdl-style casts to localized types do not lose their localized property
|
|
109
|
+
+ Fix a small memory leak during rendering of SQL/HDBCDS.
|
|
110
|
+
- to.edm(x): Remove ambiguous `Partner` attribute from `NavigationProperty`. A forward association referred
|
|
111
|
+
to by multiple backlinks (`$self` comparisons) is no longer partner to an arbitrary backlink.
|
|
112
|
+
|
|
10
113
|
## Version 2.7.0 - 2021-09-22
|
|
11
114
|
|
|
12
115
|
### Added
|
package/lib/api/main.js
CHANGED
|
@@ -494,19 +494,17 @@ function edmall(csn, options = {}) {
|
|
|
494
494
|
if (internalOptions.version === 'v2')
|
|
495
495
|
error(null, null, 'OData JSON output is not available for OData V2');
|
|
496
496
|
|
|
497
|
-
let servicesAll;
|
|
498
|
-
|
|
499
497
|
const result = {};
|
|
498
|
+
let oDataCsn = csn;
|
|
500
499
|
|
|
501
|
-
if (isPreTransformed(csn, 'odata'))
|
|
500
|
+
if (isPreTransformed(csn, 'odata'))
|
|
502
501
|
checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const services = servicesAll.edmj;
|
|
502
|
+
|
|
503
|
+
else
|
|
504
|
+
oDataCsn = odataInternal(csn, internalOptions);
|
|
505
|
+
|
|
506
|
+
const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
|
|
507
|
+
const services = servicesJson.edmj;
|
|
510
508
|
for (const serviceName in services) {
|
|
511
509
|
const lEdm = services[serviceName];
|
|
512
510
|
// FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
|
package/lib/api/options.js
CHANGED
|
@@ -10,11 +10,13 @@ const publicOptionsNewAPI = [
|
|
|
10
10
|
// GENERAL
|
|
11
11
|
'beta',
|
|
12
12
|
'deprecated',
|
|
13
|
+
'addTextsLanguageAssoc',
|
|
13
14
|
'localizedLanguageFallback', // why can't I define the option type here?
|
|
14
15
|
'severities',
|
|
15
16
|
'messages',
|
|
16
17
|
'withLocations',
|
|
17
18
|
'defaultStringLength',
|
|
19
|
+
'csnFlavor',
|
|
18
20
|
// DB
|
|
19
21
|
'sqlDialect',
|
|
20
22
|
'sqlMapping',
|
|
@@ -68,10 +70,11 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
|
|
|
68
70
|
* @param {FlatOptions} [hardRequire={}] Hard requirements to enforce
|
|
69
71
|
* @param {object} [customValidators] Custom validations to run instead of defaults
|
|
70
72
|
* @param {string[]} [combinationValidators] Option combinations to validate
|
|
73
|
+
* @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
|
|
71
74
|
* @returns {TranslatedOptions} General cds options
|
|
72
75
|
*/
|
|
73
76
|
function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
74
|
-
customValidators = {}, combinationValidators = []) {
|
|
77
|
+
customValidators = {}, combinationValidators = [], moduleName = '') {
|
|
75
78
|
const options = Object.assign({}, defaults);
|
|
76
79
|
const inputOptionNames = Object.keys(input);
|
|
77
80
|
for (const name of overallOptions) {
|
|
@@ -90,6 +93,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
90
93
|
// Validate the filtered input options
|
|
91
94
|
// only "new-style" options are here
|
|
92
95
|
validate(options,
|
|
96
|
+
moduleName,
|
|
93
97
|
// TODO: is there a better place to specify the type of option values?
|
|
94
98
|
Object.assign( {
|
|
95
99
|
localizedLanguageFallback: generateStringValidator([ 'none', 'coalesce' ]),
|
|
@@ -141,11 +145,11 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
141
145
|
|
|
142
146
|
module.exports = {
|
|
143
147
|
to: {
|
|
144
|
-
cdl: options => translateOptions(options),
|
|
148
|
+
cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
|
|
145
149
|
sql: (options) => {
|
|
146
150
|
const hardOptions = { src: 'sql' };
|
|
147
151
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
|
|
148
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ]);
|
|
152
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
|
|
149
153
|
|
|
150
154
|
const result = Object.assign({}, processed);
|
|
151
155
|
result.toSql = Object.assign({}, processed);
|
|
@@ -155,7 +159,7 @@ module.exports = {
|
|
|
155
159
|
hdi: (options) => {
|
|
156
160
|
const hardOptions = { src: 'hdi' };
|
|
157
161
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
158
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) });
|
|
162
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
|
|
159
163
|
|
|
160
164
|
const result = Object.assign({}, processed);
|
|
161
165
|
result.toSql = Object.assign({}, processed);
|
|
@@ -164,7 +168,7 @@ module.exports = {
|
|
|
164
168
|
},
|
|
165
169
|
hdbcds: (options) => {
|
|
166
170
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
167
|
-
const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) });
|
|
171
|
+
const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdbcds');
|
|
168
172
|
|
|
169
173
|
const result = Object.assign({}, processed);
|
|
170
174
|
result.forHana = Object.assign({}, processed);
|
|
@@ -174,7 +178,7 @@ module.exports = {
|
|
|
174
178
|
edm: (options) => {
|
|
175
179
|
const hardOptions = { json: true, combined: true };
|
|
176
180
|
const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
|
|
177
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ]);
|
|
181
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ], 'to.edm');
|
|
178
182
|
|
|
179
183
|
const result = Object.assign({}, processed);
|
|
180
184
|
result.toOdata = Object.assign({}, processed);
|
|
@@ -186,7 +190,7 @@ module.exports = {
|
|
|
186
190
|
const defaultOptions = {
|
|
187
191
|
odataVersion: 'v4', odataFormat: 'flat',
|
|
188
192
|
};
|
|
189
|
-
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ]);
|
|
193
|
+
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ], 'to.edmx');
|
|
190
194
|
|
|
191
195
|
const result = Object.assign({}, processed);
|
|
192
196
|
result.toOdata = Object.assign({}, processed);
|
|
@@ -198,7 +202,7 @@ module.exports = {
|
|
|
198
202
|
|
|
199
203
|
odata: (options) => {
|
|
200
204
|
const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
|
|
201
|
-
const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ]);
|
|
205
|
+
const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ], 'for.odata');
|
|
202
206
|
|
|
203
207
|
const result = Object.assign({}, processed);
|
|
204
208
|
result.toOdata = Object.assign({}, processed);
|
|
@@ -208,7 +212,7 @@ module.exports = {
|
|
|
208
212
|
},
|
|
209
213
|
hana: (options) => {
|
|
210
214
|
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
211
|
-
const processed = translateOptions(options, defaultOptions);
|
|
215
|
+
const processed = translateOptions(options, defaultOptions, undefined, undefined, undefined, 'for.hana');
|
|
212
216
|
|
|
213
217
|
const result = Object.assign({}, processed);
|
|
214
218
|
result.forHana = Object.assign({}, processed);
|
package/lib/api/validate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { makeMessageFunction
|
|
3
|
+
const { makeMessageFunction } = require('../base/messages');
|
|
4
4
|
|
|
5
5
|
/* eslint-disable arrow-body-style */
|
|
6
6
|
const booleanValidator = {
|
|
@@ -94,6 +94,11 @@ const validators = {
|
|
|
94
94
|
expected: () => 'Integer literal',
|
|
95
95
|
found: val => `type ${ typeof val }`,
|
|
96
96
|
},
|
|
97
|
+
csnFlavor: {
|
|
98
|
+
validate: val => typeof val === 'string',
|
|
99
|
+
expected: () => 'type string',
|
|
100
|
+
found: val => `type ${ typeof val }`,
|
|
101
|
+
},
|
|
97
102
|
dictionaryPrototype: {
|
|
98
103
|
validate: () => true,
|
|
99
104
|
},
|
|
@@ -122,16 +127,17 @@ const allCombinationValidators = {
|
|
|
122
127
|
* Use a custom validator or "default" custom validator, fallback to Boolean validator.
|
|
123
128
|
*
|
|
124
129
|
* @param {object} options Flat options object to validate
|
|
130
|
+
* @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
|
|
125
131
|
* @param {object} [customValidators] Map of custom validators to use
|
|
126
132
|
* @param {string[]} [combinationValidators] Validate option combinations
|
|
127
133
|
* @returns {void}
|
|
128
134
|
* @throws {CompilationError} Throws in case of invalid option usage
|
|
129
135
|
*/
|
|
130
|
-
function validate(options, customValidators = {}, combinationValidators = []) {
|
|
136
|
+
function validate(options, moduleName, customValidators = {}, combinationValidators = []) {
|
|
131
137
|
// TODO: issuing messages in this function looks very strange...
|
|
132
138
|
{
|
|
133
139
|
const messageCollector = { messages: [] };
|
|
134
|
-
const { error, throwWithError } = makeMessageFunction(null, messageCollector);
|
|
140
|
+
const { error, throwWithError } = makeMessageFunction(null, messageCollector, moduleName);
|
|
135
141
|
|
|
136
142
|
for (const optionName of Object.keys(options)) {
|
|
137
143
|
const optionValue = options[optionName];
|
|
@@ -143,7 +149,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
|
|
|
143
149
|
throwWithError();
|
|
144
150
|
}
|
|
145
151
|
|
|
146
|
-
const message = makeMessageFunction(null, options);
|
|
152
|
+
const message = makeMessageFunction(null, options, moduleName);
|
|
147
153
|
|
|
148
154
|
for (const combinationValidatorName of combinationValidators.concat([ 'beta-no-test' ])) {
|
|
149
155
|
const combinationValidator = allCombinationValidators[combinationValidatorName];
|
|
@@ -151,10 +157,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
|
|
|
151
157
|
message[combinationValidator.severity]('invalid-option-combination', null, {}, combinationValidator.getMessage(options));
|
|
152
158
|
}
|
|
153
159
|
|
|
154
|
-
|
|
155
|
-
// But be aware that it only throws with non-configurable errors and that this
|
|
156
|
-
// will lead to issues in test3. See #6037
|
|
157
|
-
handleMessages(undefined, options);
|
|
160
|
+
message.throwWithError();
|
|
158
161
|
}
|
|
159
162
|
/* eslint-enable jsdoc/no-undefined-types */
|
|
160
163
|
|
package/lib/base/keywords.js
CHANGED
|
@@ -194,7 +194,7 @@ module.exports = {
|
|
|
194
194
|
'OTHERS',
|
|
195
195
|
'TIES',
|
|
196
196
|
],
|
|
197
|
-
// HANA keywords, used
|
|
197
|
+
// HANA keywords, used for smart quoting in to-hdi.plain
|
|
198
198
|
// Taken from https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/28bcd6af3eb6437892719f7c27a8a285.html
|
|
199
199
|
// Better use keywords in ptime/query/parser/syntax/qp_keyword.cc minus those
|
|
200
200
|
// in rule unreserved_keyword_column (=…_common - "CONSTRAINT") in
|
|
@@ -673,5 +673,35 @@ module.exports = {
|
|
|
673
673
|
'WITHIN',
|
|
674
674
|
'XMLTABLE',
|
|
675
675
|
'YEAR'
|
|
676
|
-
]
|
|
676
|
+
],
|
|
677
|
+
// HANA CDS keywords, used for smart quoting in to-hdbcds.plain
|
|
678
|
+
hdbcds: [
|
|
679
|
+
'ALL', 'ALTER', 'AS',
|
|
680
|
+
'BEFORE', 'BEGIN', 'BOTH',
|
|
681
|
+
'CASE', 'CHAR', 'CONDITION',
|
|
682
|
+
'CONNECT', 'CROSS', 'CUBE',
|
|
683
|
+
'CURRENT_CONNECTION', 'CURRENT_DATE', 'CURRENT_SCHEMA',
|
|
684
|
+
'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER',
|
|
685
|
+
'CURRENT_UTCDATE', 'CURRENT_UTCTIME', 'CURRENT_UTCTIMESTAMP',
|
|
686
|
+
'CURRVAL', 'CURSOR', 'DECLARE',
|
|
687
|
+
'DISTINCT', 'ELSE', 'ELSEIF',
|
|
688
|
+
'ELSIF', 'END', 'EXCEPT',
|
|
689
|
+
'EXCEPTION', 'EXEC', 'FOR',
|
|
690
|
+
'FROM', 'FULL', 'GROUP',
|
|
691
|
+
'HAVING', 'IF', 'IN',
|
|
692
|
+
'INNER', 'INOUT', 'INTERSECT',
|
|
693
|
+
'INTO', 'IS', 'JOIN',
|
|
694
|
+
'LEADING', 'LEFT', 'LIMIT',
|
|
695
|
+
'LOOP', 'MINUS', 'NATURAL',
|
|
696
|
+
'NEXTVAL', 'NULL', 'ON',
|
|
697
|
+
'ORDER', 'OUT', 'OUTER',
|
|
698
|
+
'PRIOR', 'RETURN', 'RETURNS',
|
|
699
|
+
'REVERSE', 'RIGHT', 'ROLLUP',
|
|
700
|
+
'ROWID', 'SELECT', 'SET',
|
|
701
|
+
'SQL', 'START', 'SYSDATE',
|
|
702
|
+
'SYSTIME', 'SYSTIMESTAMP', 'SYSUUID',
|
|
703
|
+
'TOP', 'TRAILING', 'UNION',
|
|
704
|
+
'USING', 'VALUES', 'WHEN',
|
|
705
|
+
'WHERE', 'WHILE', 'WITH'
|
|
706
|
+
]
|
|
677
707
|
}
|
|
@@ -132,6 +132,10 @@ const centralMessages = {
|
|
|
132
132
|
'composition-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing and not supported
|
|
133
133
|
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
134
134
|
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
135
|
+
'odata-spec-violation-type': { severity: 'Error', configurableFor: [ 'to.edmx' ] },
|
|
136
|
+
'odata-spec-violation-key-array': { severity: 'Error' }, // more than 30 chars
|
|
137
|
+
'odata-spec-violation-key-null': { severity: 'Error' }, // more than 30 chars
|
|
138
|
+
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
135
139
|
'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
|
|
136
140
|
'odata-spec-violation-namespace-name': { severity: 'Warning' }, // more than 30 chars
|
|
137
141
|
};
|
|
@@ -246,6 +250,18 @@ const centralMessageTexts = {
|
|
|
246
250
|
'odata-spec-violation-assoc': 'Unexpected association in structured type for $(API)',
|
|
247
251
|
'odata-spec-violation-constraints': 'Partial referential constraints produced for $(API)',
|
|
248
252
|
// version independent messages
|
|
253
|
+
'odata-spec-violation-key-array': {
|
|
254
|
+
std: 'Unexpected array type for element $(NAME)',
|
|
255
|
+
scalar: 'Unexpected array type'
|
|
256
|
+
},
|
|
257
|
+
'odata-spec-violation-key-null': {
|
|
258
|
+
std: 'Expected key element $(NAME) to be not nullable', // structured
|
|
259
|
+
scalar: 'Expected key element to be not nullable' // flat
|
|
260
|
+
},
|
|
261
|
+
'odata-spec-violation-key-type': {
|
|
262
|
+
std: 'Unexpected $(TYPE) mapped to $(ID) as type for key element $(NAME)', // structured
|
|
263
|
+
scalar: 'Unexpected $(TYPE) mapped to $(ID) as type for key element' // flat
|
|
264
|
+
},
|
|
249
265
|
'odata-spec-violation-type': 'Expected element to have a type',
|
|
250
266
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(KIND)',
|
|
251
267
|
'odata-spec-violation-namespace': 'Expected service name not to be one of the reserved names $(NAMES)',
|
package/lib/base/messages.js
CHANGED
package/lib/base/model.js
CHANGED
|
@@ -59,6 +59,11 @@ function otherSideIsValidDollarSelf(on, startIndex) {
|
|
|
59
59
|
*/
|
|
60
60
|
function validateOnCondition(member, memberName, property, path) {
|
|
61
61
|
if (member && member.on) {
|
|
62
|
+
// complain about nullability constraint on managed composition
|
|
63
|
+
if (member.targetAspect && {}.hasOwnProperty.call(member, 'notNull')) {
|
|
64
|
+
this.warning(null, path.concat([ 'on' ]),
|
|
65
|
+
'Unexpected nullability constraint defined on managed composition');
|
|
66
|
+
}
|
|
62
67
|
for (let i = 0; i < member.on.length; i++) {
|
|
63
68
|
if (member.on[i].ref) {
|
|
64
69
|
const { ref } = member.on[i];
|
package/lib/checks/types.js
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
3
|
+
const { getUtils, isBuiltinType, hasAnnotationValue } = require('../model/csnUtils');
|
|
4
4
|
|
|
5
5
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Scale must not be 'variable' or 'floating'
|
|
9
|
+
*
|
|
10
|
+
* scale property is always propagated
|
|
11
|
+
*
|
|
12
|
+
* @param {CSN.Element} member the element to be checked
|
|
13
|
+
* @param {string} memberName the elements name
|
|
14
|
+
* @param {string} prop which kind of member are we looking at -> only prop "elements"
|
|
15
|
+
* @param {CSN.Path} path the path to the member
|
|
16
|
+
*/
|
|
17
|
+
function checkDecimalScale(member, memberName, prop, path) {
|
|
18
|
+
if (hasAnnotationValue(this.artifact, '@cds.persistence.exists') ||
|
|
19
|
+
// skip is already filtered in validator, here for completeness
|
|
20
|
+
hasAnnotationValue(this.artifact, '@cds.persistence.skip'))
|
|
21
|
+
return;
|
|
22
|
+
if (member.scale && [ 'variable', 'floating' ].includes(member.scale))
|
|
23
|
+
this.error(null, path, { name: member.scale }, 'Unexpected scale $(NAME)');
|
|
24
|
+
}
|
|
25
|
+
|
|
7
26
|
/**
|
|
8
27
|
* View parameter for hana must be of scalar type
|
|
9
28
|
*
|
|
@@ -165,4 +184,9 @@ function hasArtifactTypeInformation(artifact) {
|
|
|
165
184
|
artifact.type; // => `type A : [type of] Integer`
|
|
166
185
|
}
|
|
167
186
|
|
|
168
|
-
module.exports = {
|
|
187
|
+
module.exports = {
|
|
188
|
+
checkTypeDefinitionHasType,
|
|
189
|
+
checkElementTypeDefinitionHasType,
|
|
190
|
+
checkTypeIsScalar,
|
|
191
|
+
checkDecimalScale,
|
|
192
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// We only care about the "wild" ones - $at is validated by the compiler
|
|
4
|
+
const magicVariables = {
|
|
5
|
+
$user: [
|
|
6
|
+
'id', // $user.id
|
|
7
|
+
'locale', // $user.locale
|
|
8
|
+
],
|
|
9
|
+
$session: [
|
|
10
|
+
// no valid ways for this
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check that the given ref does not use magic variables for which we don't have
|
|
16
|
+
* a valid way of rendering.
|
|
17
|
+
*
|
|
18
|
+
* Valid ways:
|
|
19
|
+
* - We know what to do -> $user.id on HANA
|
|
20
|
+
* - The user tells us what to do -> options.magicVars
|
|
21
|
+
*
|
|
22
|
+
* @param {object} parent Object with the ref as a property
|
|
23
|
+
* @param {string} name Name of the ref property on parent
|
|
24
|
+
* @param {Array} ref to check
|
|
25
|
+
*/
|
|
26
|
+
function unknownMagicVariable(parent, name, ref) {
|
|
27
|
+
if (parent.$scope && parent.$scope === '$magic') {
|
|
28
|
+
const [ head, ...rest ] = ref;
|
|
29
|
+
const tail = rest.join('.');
|
|
30
|
+
const magicVariable = magicVariables[head];
|
|
31
|
+
if (magicVariable && magicVariable.indexOf(tail) === -1)
|
|
32
|
+
this.error(null, parent.$location, { id: tail, elemref: parent }, 'Magic variable is not supported - path $(ELEMREF), step $(ID)');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
ref: unknownMagicVariable,
|
|
38
|
+
};
|
package/lib/checks/validator.js
CHANGED
|
@@ -23,13 +23,17 @@ const {
|
|
|
23
23
|
// both
|
|
24
24
|
const { validateOnCondition, validateMixinOnCondition } = require('./onConditions');
|
|
25
25
|
const validateForeignKeys = require('./foreignKeys');
|
|
26
|
-
const {
|
|
26
|
+
const {
|
|
27
|
+
checkTypeDefinitionHasType, checkElementTypeDefinitionHasType,
|
|
28
|
+
checkTypeIsScalar, checkDecimalScale,
|
|
29
|
+
} = require('./types');
|
|
27
30
|
const { checkPrimaryKey, checkVirtualElement, checkManagedAssoc } = require('./elements');
|
|
28
31
|
const checkForInvalidTarget = require('./invalidTarget');
|
|
29
32
|
const { validateAssociationsInItems } = require('./arrayOfs');
|
|
30
33
|
const checkQueryForNoDBArtifacts = require('./queryNoDbArtifacts');
|
|
31
34
|
const checkExplicitlyNullableKeys = require('./nullableKeys');
|
|
32
35
|
const nonexpandableStructuredInExpression = require('./nonexpandableStructured');
|
|
36
|
+
const unknownMagic = require('./unknownMagic');
|
|
33
37
|
const managedWithoutKeys = require('./managedWithoutKeys');
|
|
34
38
|
|
|
35
39
|
const forHanaMemberValidators
|
|
@@ -37,6 +41,7 @@ const forHanaMemberValidators
|
|
|
37
41
|
// For HANA CDS specifically, reject any default parameter values, as these are not supported.
|
|
38
42
|
rejectParamDefaultsInHanaCds,
|
|
39
43
|
checkTypeIsScalar,
|
|
44
|
+
checkDecimalScale,
|
|
40
45
|
checkExplicitlyNullableKeys,
|
|
41
46
|
managedWithoutKeys,
|
|
42
47
|
warnAboutDefaultOnAssociationForHanaCds,
|
|
@@ -50,7 +55,7 @@ const forHanaArtifactValidators
|
|
|
50
55
|
checkForEmptyOrOnlyVirtual,
|
|
51
56
|
];
|
|
52
57
|
|
|
53
|
-
const forHanaCsnValidators = [ nonexpandableStructuredInExpression ];
|
|
58
|
+
const forHanaCsnValidators = [ nonexpandableStructuredInExpression, unknownMagic ];
|
|
54
59
|
/**
|
|
55
60
|
* @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
|
|
56
61
|
*/
|
|
@@ -246,7 +246,7 @@ function assertConsistency( model, stage ) {
|
|
|
246
246
|
optional: [
|
|
247
247
|
'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
|
|
248
248
|
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
|
|
249
|
-
'_projections', '_block', '_parent', '_main', '_effectiveType',
|
|
249
|
+
'_projections', '_block', '_parent', '_main', '_effectiveType', '$expand',
|
|
250
250
|
'$tableAliases', 'kind', '_$next', '_combined', '$inlines',
|
|
251
251
|
],
|
|
252
252
|
},
|
|
@@ -324,7 +324,7 @@ function assertConsistency( model, stage ) {
|
|
|
324
324
|
requires: [ 'location' ],
|
|
325
325
|
optional: [
|
|
326
326
|
'path', 'elements', '_outer',
|
|
327
|
-
'scope', '_artifact', '$inferred',
|
|
327
|
+
'scope', '_artifact', '$inferred', '$expand',
|
|
328
328
|
'_effectiveType', // by propagation
|
|
329
329
|
],
|
|
330
330
|
},
|
|
@@ -351,6 +351,7 @@ function assertConsistency( model, stage ) {
|
|
|
351
351
|
$delimited: { parser: true, test: isBoolean },
|
|
352
352
|
scope: { test: isScope },
|
|
353
353
|
func: { test: TODO },
|
|
354
|
+
suffix: { test: TODO },
|
|
354
355
|
kind: {
|
|
355
356
|
isRequired: !stageParser && (() => true),
|
|
356
357
|
// required to be set by Core Compiler even with parse errors
|
|
@@ -398,6 +399,7 @@ function assertConsistency( model, stage ) {
|
|
|
398
399
|
optional: [
|
|
399
400
|
'args',
|
|
400
401
|
'func',
|
|
402
|
+
'suffix',
|
|
401
403
|
'quantifier',
|
|
402
404
|
'$inferred',
|
|
403
405
|
'$parens',
|
|
@@ -426,7 +428,7 @@ function assertConsistency( model, stage ) {
|
|
|
426
428
|
struct: { inherits: 'val', test: isDictionary( definition ) }, // def because double @
|
|
427
429
|
args: {
|
|
428
430
|
inherits: 'value',
|
|
429
|
-
optional: [ 'name', '$duplicate', '$expected' ],
|
|
431
|
+
optional: [ 'name', '$duplicate', '$expected', 'args', 'suffix' ],
|
|
430
432
|
test: args,
|
|
431
433
|
},
|
|
432
434
|
on: { kind: true, inherits: 'value', test: expression },
|
|
@@ -533,7 +535,7 @@ function assertConsistency( model, stage ) {
|
|
|
533
535
|
],
|
|
534
536
|
optional: [
|
|
535
537
|
'_effectiveType', '$parens',
|
|
536
|
-
'_deps',
|
|
538
|
+
'_deps', '$expand',
|
|
537
539
|
// query specific
|
|
538
540
|
'where', 'columns', 'mixin', 'quantifier', 'offset',
|
|
539
541
|
'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
|
|
@@ -574,7 +576,11 @@ function assertConsistency( model, stage ) {
|
|
|
574
576
|
$duplicates: { parser: true, kind: true, test: TODO }, // array of arts or true
|
|
575
577
|
$extension: { kind: true, test: TODO }, // TODO: introduce $applied instead or $status
|
|
576
578
|
$inferred: { parser: true, kind: true, test: isString },
|
|
577
|
-
|
|
579
|
+
|
|
580
|
+
// Helper property for the XSN-to-CSN transformation, see function setExpandStatus():
|
|
581
|
+
// client, universal: render expanded elements? gensrc: produce annotate statements?
|
|
582
|
+
$expand: { kind: true, test: isString }, // TODO: rename it to $elementsExpand ?
|
|
583
|
+
|
|
578
584
|
$autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
|
|
579
585
|
$a2j: { kind: true, enumerable: true, test: TODO },
|
|
580
586
|
$extra: { parser: true, test: TODO }, // for unexpected properties in CSN
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -83,6 +83,8 @@ const specialFunctions = {
|
|
|
83
83
|
const magicVariables = {
|
|
84
84
|
$user: {
|
|
85
85
|
elements: { id: {}, locale: {} },
|
|
86
|
+
// Allow $user.<any>
|
|
87
|
+
$uncheckedElements: true,
|
|
86
88
|
// Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
|
|
87
89
|
$autoElement: 'id',
|
|
88
90
|
}, // CDS-specific, not part of SQL
|
package/lib/compiler/checks.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
// const { hasArtifactTypeInformation } = require('../model/csnUtils')
|
|
17
17
|
const builtins = require('../compiler/builtins');
|
|
18
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
forEachGeneric, forEachDefinition, forEachMember,
|
|
20
|
+
} = require('../base/model');
|
|
19
21
|
|
|
20
22
|
function check( model ) { // = XSN
|
|
21
23
|
const {
|