@sap/cds-compiler 3.6.0 → 3.7.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 +58 -0
- package/README.md +3 -0
- package/bin/cdsc.js +9 -5
- package/doc/CHANGELOG_BETA.md +20 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +2 -1
- package/lib/api/options.js +3 -2
- package/lib/base/dictionaries.js +10 -0
- package/lib/base/message-registry.js +56 -12
- package/lib/base/messages.js +39 -20
- package/lib/base/model.js +1 -0
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +9 -5
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +8 -5
- package/lib/checks/types.js +6 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +20 -23
- package/lib/compiler/base.js +1 -2
- package/lib/compiler/builtins.js +2 -2
- package/lib/compiler/checks.js +237 -242
- package/lib/compiler/define.js +63 -75
- package/lib/compiler/extend.js +325 -22
- package/lib/compiler/finalize-parse-cdl.js +1 -55
- package/lib/compiler/kick-start.js +6 -7
- package/lib/compiler/populate.js +284 -288
- package/lib/compiler/propagator.js +15 -13
- package/lib/compiler/resolve.js +136 -306
- package/lib/compiler/shared.js +42 -44
- package/lib/compiler/tweak-assocs.js +29 -27
- package/lib/compiler/utils.js +29 -3
- package/lib/edm/annotations/genericTranslation.js +7 -13
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +0 -4
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +1 -5
- package/lib/gen/Dictionary.json +34 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2429 -2401
- package/lib/inspect/inspectPropagation.js +2 -0
- package/lib/json/from-csn.js +87 -41
- package/lib/json/to-csn.js +47 -16
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +109 -28
- package/lib/language/language.g4 +20 -4
- package/lib/model/csnRefs.js +30 -2
- package/lib/model/csnUtils.js +1 -0
- package/lib/model/revealInternalProperties.js +1 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +20 -7
- package/lib/render/toHdbcds.js +2 -8
- package/lib/render/toSql.js +6 -5
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +559 -0
- package/lib/transform/db/transformExists.js +15 -6
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forRelationalDB.js +44 -30
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +14 -8
- package/lib/transform/transformUtilsNew.js +6 -5
- package/lib/transform/translateAssocsToJoins.js +49 -33
- 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
|
+
|
|
11
|
+
## Version 3.7.2 - 2023-02-24
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- CSN parser: Structured annotations containing `=` were accidentally interpreted as expressions,
|
|
16
|
+
even though the corresponding beta flag was not set.
|
|
17
|
+
|
|
18
|
+
## Version 3.7.0 - 2023-02-22
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Several `annotate` statement can append/prepend values
|
|
23
|
+
to the same array-valued annotation without an `anno-duplicate` error,
|
|
24
|
+
even if there is no `using from` dependency between the involved sources
|
|
25
|
+
- SQL methods such as `point.ST_X()` can be used in views.
|
|
26
|
+
- The SQL `new` keyword can be used for `ST_*` types such as `new ST_POINT('Point(0.5 0.5)') )`
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Update OData vocabularies 'Common', 'Core', 'Measures', 'PDF', 'UI'.
|
|
31
|
+
- to.edm(x): Empty complex types are no longer warned about as they are allowed.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- `parse.cql` and `parse.expr` no longer ignore type arguments such as `cast(field as String(12))`.
|
|
36
|
+
One argument is interpreted as `length` and two are interpreted as `precision` and `scale`, similar to
|
|
37
|
+
how custom types and their arguments are interpreted.
|
|
38
|
+
- Previously, the compiler could not always find a unique redirection target if there were
|
|
39
|
+
one direct projection on the model target and two or more projections on that projection.
|
|
40
|
+
- The performance of compiler-checks for deeply nested expressions/queries has been improved
|
|
41
|
+
- Fix various bugs in Association to Join translation:
|
|
42
|
+
+ Recursive `$self` dereferencing
|
|
43
|
+
+ Correct resolution of table alias in non-bijective `$self` backlink associations in combination with
|
|
44
|
+
explicit redirection.
|
|
45
|
+
- to.edm(x): Process value help list convenience annotations on unbound action parameters.
|
|
46
|
+
|
|
47
|
+
### Removed
|
|
48
|
+
|
|
49
|
+
- tbd
|
|
50
|
+
|
|
51
|
+
## Version 3.6.2 - 2023-02-06
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- to.hdi(.migration): Don't render `-- generated by cds-compiler version` comment at the top of the HDI-based
|
|
56
|
+
artifacts, as this caused HDI to detect the file as `changed`
|
|
57
|
+
and redeploy, causing way longer deployment times. Old behavior can be enabled with option `generatedByComment: true`.
|
|
58
|
+
- to.sql/hdi/hdbcds: Correctly handle variables like `$user` in `exists` filters.
|
|
59
|
+
|
|
10
60
|
## Version 3.6.0 - 2023-01-25
|
|
11
61
|
|
|
12
62
|
### Added
|
|
@@ -356,6 +406,14 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
356
406
|
it is now only allowed for `count`, `min`, `max`, `sum`, `avg`, `stddev`, `var`.
|
|
357
407
|
- All non-SNAPI options.
|
|
358
408
|
|
|
409
|
+
## Version 2.15.10 - 2023-01-26
|
|
410
|
+
|
|
411
|
+
### Fixed
|
|
412
|
+
|
|
413
|
+
- If an entity with parameters is auto-exposed, the generated projection now has
|
|
414
|
+
the same formal parameters and its query forwards these parameters to the origin entity.
|
|
415
|
+
- to.edm(x): Respect record type hint `$Type` in EDM JSON as full qualified `@type` URI property.
|
|
416
|
+
|
|
359
417
|
## Version 2.15.8 - 2022-08-02
|
|
360
418
|
|
|
361
419
|
### Fixed
|
package/README.md
CHANGED
|
@@ -29,6 +29,9 @@ Or maintain your package.json dependencies as follows:
|
|
|
29
29
|
|
|
30
30
|
Please refer to the [official CDS documentation](https://cap.cloud.sap/docs/cds/).
|
|
31
31
|
|
|
32
|
+
## How to Obtain Support
|
|
33
|
+
|
|
34
|
+
In case you find a bug, please report an [incident](https://cap.cloud.sap/docs/resources/#reporting-incidents) on SAP Support Portal.
|
|
32
35
|
|
|
33
36
|
## License
|
|
34
37
|
|
package/bin/cdsc.js
CHANGED
|
@@ -239,10 +239,13 @@ function displayUsage( error, helpText, code ) {
|
|
|
239
239
|
// Display help text first, error at the end (more readable, no scrolling)
|
|
240
240
|
out.write(`${helpText}\n`);
|
|
241
241
|
if (error) {
|
|
242
|
-
if (error instanceof Array)
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
if (error instanceof Array) {
|
|
243
|
+
const errors = error.map(err => `cdsc: ERROR: ${err}`).join('\n');
|
|
244
|
+
out.write(`${errors}\n`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
245
247
|
out.write(`cdsc: ERROR: ${error}\n`);
|
|
248
|
+
}
|
|
246
249
|
}
|
|
247
250
|
throw new ProcessExitError(code);
|
|
248
251
|
}
|
|
@@ -389,8 +392,8 @@ function executeCommandLine( command, options, args ) {
|
|
|
389
392
|
// Execute the command line option 'manageConstraints' and display the results.
|
|
390
393
|
function manageConstraints( model ) {
|
|
391
394
|
const csn = options.directBackend ? model : compactModel(model, options);
|
|
392
|
-
const alterConstraintsResult = alterConstraintsWithCsn(csn, options);
|
|
393
395
|
const { src } = options || {};
|
|
396
|
+
const alterConstraintsResult = alterConstraintsWithCsn(csn, options);
|
|
394
397
|
Object.keys(alterConstraintsResult).forEach((id) => {
|
|
395
398
|
const renderedConstraintStatement = alterConstraintsResult[id];
|
|
396
399
|
if (src === 'hdi')
|
|
@@ -499,8 +502,9 @@ function executeCommandLine( command, options, args ) {
|
|
|
499
502
|
.forEach(msg => log(msg));
|
|
500
503
|
}
|
|
501
504
|
else if (options.noMessageContext) {
|
|
505
|
+
// TODO: currently, ‹↓› = "downgradable" is only shown here
|
|
502
506
|
messages.filter(msg => (messageLevels[msg.severity] <= options.warning))
|
|
503
|
-
.forEach(msg => log(main.messageString(msg, normalizeFilename, !!options.noMessageId)));
|
|
507
|
+
.forEach(msg => log(main.messageString(msg, normalizeFilename, !!options.noMessageId, false, options.testMode && 'compile'))); // TODO: use module name
|
|
504
508
|
}
|
|
505
509
|
else {
|
|
506
510
|
// Contains file-contents that are split at '\n'. Try to avoid multiple `.split()` calls.
|
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,24 @@ 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
|
+
|
|
12
|
+
## Version 3.7.0 - 2023-02-22
|
|
13
|
+
|
|
14
|
+
### Added `calculatedElements`
|
|
15
|
+
|
|
16
|
+
Allows to define calculated elements in entities and aspects. When used in views, they
|
|
17
|
+
are replaced by their value, for example:
|
|
18
|
+
|
|
19
|
+
```cds
|
|
20
|
+
entity E { one: Integer; two = one + 1; };
|
|
21
|
+
entity P as projection on E { two };
|
|
22
|
+
// P is the same as:
|
|
23
|
+
entity P as projection on E { one + 1 as two };
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This allows to define calculations centrally at the entity, which can be used by
|
|
27
|
+
other views.
|
|
28
|
+
|
|
11
29
|
## Version 3.5.0 - 2022-12-07
|
|
12
30
|
|
|
13
31
|
### Added `odataTerms`
|
|
@@ -250,7 +268,7 @@ Render JOIN cardinality in native HANA association if provided. If no cardinalit
|
|
|
250
268
|
|
|
251
269
|
### Removed `aspectCompositions`
|
|
252
270
|
|
|
253
|
-
Aspect compositions aka managed compositions are now
|
|
271
|
+
Aspect compositions aka managed compositions are now available without beta option.
|
|
254
272
|
_Warning_: the CSN representation can still change.
|
|
255
273
|
|
|
256
274
|
## Version 1.31.0 - 2020-06-26
|
|
@@ -286,5 +304,5 @@ This option does not enable:
|
|
|
286
304
|
### Added `keyRefError`
|
|
287
305
|
|
|
288
306
|
Always signal an error (instead of just a warning in some cases),
|
|
289
|
-
if not all references in the `keys` of
|
|
307
|
+
if not all references in the `keys` of a managed associations
|
|
290
308
|
are projected in the new target.
|
|
@@ -15,7 +15,7 @@ and several new features are not available.**
|
|
|
15
15
|
|
|
16
16
|
### Added `autoCorrectOrderBySourceRefs`
|
|
17
17
|
|
|
18
|
-
When this option is set, calling `compile`
|
|
18
|
+
When this option is set, calling `compile` autocorrects direct `order by`
|
|
19
19
|
source element references without table alias for SELECT queries by adding the
|
|
20
20
|
table alias to the `ref`.
|
|
21
21
|
|
|
@@ -153,7 +153,7 @@ Render old and broken temporal EDM API.
|
|
|
153
153
|
### Added `noElementsExpansion`
|
|
154
154
|
|
|
155
155
|
When setting it, association in sub elements are not automatically redirected,
|
|
156
|
-
and the sub elements cannot be annotated
|
|
156
|
+
and the sub elements cannot be annotated individually.
|
|
157
157
|
|
|
158
158
|
Do not use this. Setting it might avoid some compile errors,
|
|
159
159
|
but in most cases the reported errors are rightly reported.
|
package/lib/api/main.js
CHANGED
|
@@ -63,6 +63,7 @@ function attachTransformerCharacteristics( csn, transformation, options,
|
|
|
63
63
|
relevant[name] = options[name];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// eslint-disable-next-line sonarjs/no-empty-collection
|
|
66
67
|
for (const name of relevantGeneralOptions ) {
|
|
67
68
|
if (options[name] !== undefined)
|
|
68
69
|
relevant[name] = options[name];
|
|
@@ -763,7 +764,7 @@ function flattenResultStructure( toProcess ) {
|
|
|
763
764
|
/**
|
|
764
765
|
* Print args to stderr if CDSC_TRACE_API is set
|
|
765
766
|
*
|
|
766
|
-
* @param {...any} args
|
|
767
|
+
* @param {...any} args to be logged to stderr
|
|
767
768
|
*/
|
|
768
769
|
function traceApi( ...args ) {
|
|
769
770
|
if (process?.env?.CDSC_TRACE_API !== undefined) {
|
package/lib/api/options.js
CHANGED
|
@@ -28,6 +28,7 @@ const publicOptionsNewAPI = [
|
|
|
28
28
|
'magicVars', // deprecated, not removed in v3 as we have specific error messages for it
|
|
29
29
|
'variableReplacements',
|
|
30
30
|
'pre2134ReferentialConstraintNames',
|
|
31
|
+
'generatedByComment',
|
|
31
32
|
// ODATA
|
|
32
33
|
'odataVersion',
|
|
33
34
|
'odataFormat',
|
|
@@ -120,14 +121,14 @@ module.exports = {
|
|
|
120
121
|
cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
|
|
121
122
|
sql: (options) => {
|
|
122
123
|
const hardOptions = { src: 'sql', toSql: true, forHana: true };
|
|
123
|
-
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
|
|
124
|
+
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain', generatedByComment: true };
|
|
124
125
|
const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
|
|
125
126
|
|
|
126
127
|
return Object.assign({}, processed);
|
|
127
128
|
},
|
|
128
129
|
hdi: (options) => {
|
|
129
130
|
const hardOptions = { src: 'hdi', toSql: true, forHana: true };
|
|
130
|
-
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
|
|
131
|
+
const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana', generatedByComment: false };
|
|
131
132
|
return translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
|
|
132
133
|
},
|
|
133
134
|
hdbcds: (options) => {
|
package/lib/base/dictionaries.js
CHANGED
|
@@ -81,6 +81,15 @@ function dictAddArray( dict, name, entry, messageCallback ) {
|
|
|
81
81
|
return entry;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function dictFirst( dict ) {
|
|
85
|
+
if (!dict)
|
|
86
|
+
return dict;
|
|
87
|
+
for (const name in dict) {
|
|
88
|
+
return dict[name];
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
// Push `entry` to the array value with key `name` in the dictionary `dict`.
|
|
85
94
|
function pushToDict( dict, name, entry ) {
|
|
86
95
|
if (dict[name])
|
|
@@ -93,5 +102,6 @@ module.exports = {
|
|
|
93
102
|
dictAdd,
|
|
94
103
|
dictForEach,
|
|
95
104
|
dictAddArray,
|
|
105
|
+
dictFirst,
|
|
96
106
|
pushToDict,
|
|
97
107
|
};
|
|
@@ -159,6 +159,8 @@ const centralMessages = {
|
|
|
159
159
|
|
|
160
160
|
'def-missing-element': { severity: 'Error' },
|
|
161
161
|
|
|
162
|
+
'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
|
|
163
|
+
|
|
162
164
|
'unexpected-keys-for-composition': { severity: 'Error' }, // TODO: more than 30 chars
|
|
163
165
|
'unmanaged-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing
|
|
164
166
|
'composition-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing and not supported
|
|
@@ -231,11 +233,15 @@ const centralMessageTexts = {
|
|
|
231
233
|
std: 'Duplicate assignment with $(ANNO)',
|
|
232
234
|
doc: 'Duplicate assignment with a doc comment',
|
|
233
235
|
},
|
|
236
|
+
'anno-duplicate-same-file': {
|
|
237
|
+
std: 'Duplicate assignment with $(ANNO), using last',
|
|
238
|
+
doc: 'Duplicate assignment with a doc comment, using last',
|
|
239
|
+
},
|
|
234
240
|
'anno-duplicate-unrelated-layer': {
|
|
235
241
|
std: 'Duplicate assignment with $(ANNO)',
|
|
236
242
|
doc: 'Duplicate assignment with a doc comment',
|
|
237
243
|
},
|
|
238
|
-
'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO)
|
|
244
|
+
'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO)',
|
|
239
245
|
'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
|
|
240
246
|
'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
|
|
241
247
|
'anno-unexpected-ellipsis-layers': 'No base annotation available to apply $(CODE)',
|
|
@@ -360,7 +366,6 @@ const centralMessageTexts = {
|
|
|
360
366
|
dynamic: 'Dynamic parameter $(CODE) is not supported',
|
|
361
367
|
positional: 'Positional parameter $(CODE) is not supported',
|
|
362
368
|
},
|
|
363
|
-
// 'syntax-unsupported-method', 'syntax-unsupported-new'
|
|
364
369
|
|
|
365
370
|
// Syntax messages, CSN parser - default: Error ------------------------------
|
|
366
371
|
'syntax-deprecated-dollar-syntax': {
|
|
@@ -420,7 +425,7 @@ const centralMessageTexts = {
|
|
|
420
425
|
extend: 'CSN property $(PROP) is not expected by an extend in $(PARENTPROP)',
|
|
421
426
|
annotate: 'CSN property $(PROP) is not expected by an annotate in $(PARENTPROP)',
|
|
422
427
|
},
|
|
423
|
-
'
|
|
428
|
+
'def-invalid-calc-elem': {
|
|
424
429
|
std: 'Invalid calculated element',
|
|
425
430
|
key: 'A primary key element can\'t be calculated',
|
|
426
431
|
virtual: 'A virtual element can\'t be calculated',
|
|
@@ -430,7 +435,12 @@ const centralMessageTexts = {
|
|
|
430
435
|
type: 'A type can\'t have calculated elements',
|
|
431
436
|
action: 'An action can\'t have calculated elements',
|
|
432
437
|
function: 'A function can\'t have calculated elements',
|
|
433
|
-
annotation: 'Annotation definitions can\'t have calculated elements'
|
|
438
|
+
annotation: 'Annotation definitions can\'t have calculated elements',
|
|
439
|
+
param: 'Parameters can\'t have calculated elements',
|
|
440
|
+
},
|
|
441
|
+
'def-unsupported-calc-elem': {
|
|
442
|
+
std: 'Calculated elements are not supported',
|
|
443
|
+
nested: 'Calculated elements in structures are not supported, yet'
|
|
434
444
|
},
|
|
435
445
|
// 'syntax-unknown-property' (Warning? Better configurable Error)
|
|
436
446
|
|
|
@@ -489,6 +499,10 @@ const centralMessageTexts = {
|
|
|
489
499
|
std: '$(ART) can\'t be extended because it originates from an include',
|
|
490
500
|
elements: '$(ART) can\'t be extended by elements/enums because it originates from an include',
|
|
491
501
|
},
|
|
502
|
+
'ref-unexpected-scope': {
|
|
503
|
+
std: 'Unexpected parameter reference',
|
|
504
|
+
calc: 'Calculated elements can\'t use parameter references',
|
|
505
|
+
},
|
|
492
506
|
'ref-unexpected-structured': {
|
|
493
507
|
std: 'Unexpected usage of structured type $(ELEMREF)',
|
|
494
508
|
expr: 'Structured elements can\'t be used in expressions',
|
|
@@ -503,6 +517,16 @@ const centralMessageTexts = {
|
|
|
503
517
|
expr: 'Associations can\'t be used as values in expressions',
|
|
504
518
|
cast: 'Casting to an association is not supported',
|
|
505
519
|
},
|
|
520
|
+
'ref-unexpected-calculated': {
|
|
521
|
+
std: 'Unexpected reference to calculated element',
|
|
522
|
+
on: 'Calculated elements can\'t be used in ON-conditions of unmanaged associations',
|
|
523
|
+
fkey: 'Calculated elements can\'t be used as foreign keys for managed associations',
|
|
524
|
+
},
|
|
525
|
+
|
|
526
|
+
'ref-unexpected-navigation': {
|
|
527
|
+
std: 'Can\'t follow association $(ID) of path $(ELEMREF) in an ON-condition; only foreign keys can be referred to, but not $(NAME)',
|
|
528
|
+
unmanaged: 'Can\'t follow unmanaged association $(ID) of path $(ELEMREF) in an ON-condition; only foreign keys can be referred to',
|
|
529
|
+
},
|
|
506
530
|
|
|
507
531
|
'type-unexpected-typeof': {
|
|
508
532
|
std: 'Unexpected $(KEYWORD) for the type reference here',
|
|
@@ -545,14 +569,14 @@ const centralMessageTexts = {
|
|
|
545
569
|
},
|
|
546
570
|
|
|
547
571
|
'def-unexpected-paramview-assoc': {
|
|
548
|
-
std: 'SAP HANA
|
|
549
|
-
view: 'SAP HANA
|
|
550
|
-
target: 'SAP HANA
|
|
572
|
+
std: 'SAP HANA doesn\'t support associations in/to parameterized entities',
|
|
573
|
+
view: 'SAP HANA doesn\'t support associations in parameterized entities',
|
|
574
|
+
target: 'SAP HANA doesn\'t support associations to parameterized entities',
|
|
551
575
|
},
|
|
552
576
|
'def-unexpected-calcview-assoc': {
|
|
553
|
-
std: 'SAP HANA
|
|
554
|
-
'entity-persistence': 'SAP HANA
|
|
555
|
-
'target-persistence': 'SAP HANA
|
|
577
|
+
std: 'SAP HANA doesn\'t allow associations in/to entities annotated with $(ANNO)',
|
|
578
|
+
'entity-persistence': 'SAP HANA doesn\'t allow associations in entities annotated with $(ANNO)',
|
|
579
|
+
'target-persistence': 'SAP HANA doesn\'t allow associations pointing to entities annotated with $(ANNO)',
|
|
556
580
|
},
|
|
557
581
|
'def-unexpected-key': {
|
|
558
582
|
std: '$(ART) can\'t have additional keys',
|
|
@@ -566,9 +590,13 @@ const centralMessageTexts = {
|
|
|
566
590
|
include: '$(ART) can\'t have localized elements (through include)',
|
|
567
591
|
},
|
|
568
592
|
'def-unexpected-localized-anno': 'Annotations can\'t have localized elements',
|
|
593
|
+
'type-unexpected-structure': {
|
|
594
|
+
std: 'Unexpected structured type', // unused variant
|
|
595
|
+
calc: 'A structured type can\'t be used for calculated elements',
|
|
596
|
+
},
|
|
569
597
|
|
|
570
598
|
'def-missing-element': {
|
|
571
|
-
std: 'Expecting entity to have at least one
|
|
599
|
+
std: 'Expecting entity to have at least one element which is neither virtual nor calculated',
|
|
572
600
|
view: 'Expecting view to have at least one non-virtual element'
|
|
573
601
|
},
|
|
574
602
|
|
|
@@ -595,6 +623,11 @@ const centralMessageTexts = {
|
|
|
595
623
|
param: 'Duplicate definition of parameter $(NAME)',
|
|
596
624
|
alias: 'Duplicate definition of table alias or mixin $(NAME)',
|
|
597
625
|
},
|
|
626
|
+
'ref-duplicate-include-member': {
|
|
627
|
+
std: 'Duplicate member $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
628
|
+
elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
629
|
+
actions: 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
630
|
+
},
|
|
598
631
|
|
|
599
632
|
// TODO: rename to ref-expected-XYZ
|
|
600
633
|
'expected-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
@@ -645,7 +678,10 @@ const centralMessageTexts = {
|
|
|
645
678
|
std: 'Expected identifier for select item',
|
|
646
679
|
assoc: 'Expected identifier as the association\'s name',
|
|
647
680
|
},
|
|
648
|
-
|
|
681
|
+
'query-unsupported-calc': {
|
|
682
|
+
std: 'Using nested projections next to calculated elements is not supported, yet',
|
|
683
|
+
inside: 'Using calculated elements in nested projections is not supported, yet'
|
|
684
|
+
},
|
|
649
685
|
'ref-sloppy-type': 'A type or an element is expected here',
|
|
650
686
|
'ref-sloppy-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
651
687
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
@@ -658,6 +694,14 @@ const centralMessageTexts = {
|
|
|
658
694
|
entity: 'Entity $(ART) with managed compositions can\'t be used in types', // yet
|
|
659
695
|
},
|
|
660
696
|
|
|
697
|
+
// -----------------------------------------------------------------------------------
|
|
698
|
+
// Expressions
|
|
699
|
+
// -----------------------------------------------------------------------------------
|
|
700
|
+
'expr-invalid-operator': 'Comparing $(ID) is only allowed with $(OP)',
|
|
701
|
+
'expr-missing-comparison': {
|
|
702
|
+
std: 'Expected a comparison with $(OP) when using $(ID) in an ON-condition',
|
|
703
|
+
},
|
|
704
|
+
|
|
661
705
|
'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
|
|
662
706
|
|
|
663
707
|
// OData version dependent messages
|
package/lib/base/messages.js
CHANGED
|
@@ -766,6 +766,12 @@ function transformManyWith( t, sorted ) {
|
|
|
766
766
|
};
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
+
/**
|
|
770
|
+
* Quote the given string. Performs a type sanity check.
|
|
771
|
+
*
|
|
772
|
+
* @param {string} name
|
|
773
|
+
* @return {string}
|
|
774
|
+
*/
|
|
769
775
|
function quoted( name ) {
|
|
770
776
|
if (typeof name === 'string')
|
|
771
777
|
return quote.double( name );
|
|
@@ -912,16 +918,16 @@ function weakLocation( loc ) {
|
|
|
912
918
|
* @param {boolean} [noHome]
|
|
913
919
|
* @returns {string}
|
|
914
920
|
*/
|
|
915
|
-
function messageString( err, normalizeFilename, noMessageId, noHome ) {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
921
|
+
function messageString( err, normalizeFilename, noMessageId, noHome, moduleName = undefined ) {
|
|
922
|
+
const location = (err.$location?.file ? `${ locationString( err.$location, normalizeFilename ) }: ` : '');
|
|
923
|
+
const severity = err.severity || 'Error';
|
|
924
|
+
const downgradable = severity === 'Error' && moduleName &&
|
|
925
|
+
isDowngradable(err.messageId, moduleName, false) ? '‹↓›' : '';
|
|
926
|
+
// even with noHome, print err.home if the location is weak
|
|
927
|
+
const home = !err.home || noHome && err.$location?.endLine ? '' : ` (in ${ err.home })`;
|
|
928
|
+
// TODO: the plan was with brackets = `Error[ref-undefined-def]`
|
|
929
|
+
const id = err.messageId && !noMessageId ? ` ${ err.messageId }` : '';
|
|
930
|
+
return `${ location }${ severity }${ downgradable }${ id }: ${ err.message }${ home }`;
|
|
925
931
|
}
|
|
926
932
|
|
|
927
933
|
/**
|
|
@@ -1309,6 +1315,12 @@ function homeNameForExtend( art ) {
|
|
|
1309
1315
|
function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
1310
1316
|
if (!model)
|
|
1311
1317
|
return null;
|
|
1318
|
+
|
|
1319
|
+
if (options.testMode)
|
|
1320
|
+
sanitizeCsnPath(csnPath);
|
|
1321
|
+
|
|
1322
|
+
const _quoted = options.testMode ? quoted : quote.double;
|
|
1323
|
+
|
|
1312
1324
|
let result = '';
|
|
1313
1325
|
const csnDictionaries = [
|
|
1314
1326
|
'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
|
|
@@ -1338,12 +1350,12 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1338
1350
|
if (step === 'definitions') {
|
|
1339
1351
|
next(); // "definitions"
|
|
1340
1352
|
const kind = currentThing?.kind || 'artifact';
|
|
1341
|
-
result += `${ kind }:${
|
|
1353
|
+
result += `${ kind }:${ _quoted(step) }`;
|
|
1342
1354
|
}
|
|
1343
1355
|
else if (step === 'vocabularies') {
|
|
1344
1356
|
next(); // dictionary name
|
|
1345
1357
|
if (index < csnPath.length)
|
|
1346
|
-
result += `annotation:${
|
|
1358
|
+
result += `annotation:${ _quoted(csnPath[index]) }`;
|
|
1347
1359
|
else
|
|
1348
1360
|
result += 'vocabularies';
|
|
1349
1361
|
}
|
|
@@ -1355,7 +1367,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1355
1367
|
else {
|
|
1356
1368
|
const name = currentThing.annotate || currentThing.extend;
|
|
1357
1369
|
const kind = currentThing.annotate ? 'annotate' : 'extend';
|
|
1358
|
-
result += `${ kind }:${
|
|
1370
|
+
result += `${ kind }:${ _quoted(name) }`;
|
|
1359
1371
|
}
|
|
1360
1372
|
}
|
|
1361
1373
|
|
|
@@ -1381,7 +1393,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1381
1393
|
next();
|
|
1382
1394
|
}
|
|
1383
1395
|
if (elementHierarchy.length > 0)
|
|
1384
|
-
result += `/element:${
|
|
1396
|
+
result += `/element:${ _quoted(elementHierarchy.join('.')) }`;
|
|
1385
1397
|
// no trailing /elements or /items
|
|
1386
1398
|
continue;
|
|
1387
1399
|
}
|
|
@@ -1389,7 +1401,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1389
1401
|
next(); // "actions"
|
|
1390
1402
|
if (index < csnPath.length) {
|
|
1391
1403
|
const kind = currentThing?.kind || 'action';
|
|
1392
|
-
result += `/${ kind }:${
|
|
1404
|
+
result += `/${ kind }:${ _quoted(csnPath[index]) }`;
|
|
1393
1405
|
}
|
|
1394
1406
|
else { // actions is last segment
|
|
1395
1407
|
result += '/actions';
|
|
@@ -1419,7 +1431,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1419
1431
|
}
|
|
1420
1432
|
else if (step === 'target') {
|
|
1421
1433
|
if (currentThing)
|
|
1422
|
-
result += '/target:' +
|
|
1434
|
+
result += '/target:' + _quoted(currentThing);
|
|
1423
1435
|
else
|
|
1424
1436
|
result += '/target';
|
|
1425
1437
|
break;
|
|
@@ -1447,14 +1459,14 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1447
1459
|
next(); // "keys"
|
|
1448
1460
|
if (index < csnPath.length) {
|
|
1449
1461
|
const key = aliasOrReference();
|
|
1450
|
-
result += `/key:${ key ?
|
|
1462
|
+
result += `/key:${ key ? _quoted(key) : step }`;
|
|
1451
1463
|
}
|
|
1452
1464
|
break;
|
|
1453
1465
|
}
|
|
1454
1466
|
else if (step[0] === '@') {
|
|
1455
1467
|
// Annotations are always the last step.
|
|
1456
1468
|
// Nothing comes after them, everything is user defined.
|
|
1457
|
-
result += `/${
|
|
1469
|
+
result += `/${ _quoted(csnPath[index]) }`;
|
|
1458
1470
|
break;
|
|
1459
1471
|
}
|
|
1460
1472
|
else {
|
|
@@ -1470,7 +1482,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1470
1482
|
function dictEntry( prefix ) {
|
|
1471
1483
|
next(); // dictionary name
|
|
1472
1484
|
if (index < csnPath.length)
|
|
1473
|
-
result += `/${ prefix }:${
|
|
1485
|
+
result += `/${ prefix }:${ _quoted(csnPath[index]) }`;
|
|
1474
1486
|
}
|
|
1475
1487
|
|
|
1476
1488
|
/**
|
|
@@ -1548,7 +1560,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
|
1548
1560
|
} while (index < csnPath.length);
|
|
1549
1561
|
|
|
1550
1562
|
if (elementHierarchy.length > 0)
|
|
1551
|
-
result += `/column:${
|
|
1563
|
+
result += `/column:${ _quoted(elementHierarchy.join('.')) }`;
|
|
1552
1564
|
}
|
|
1553
1565
|
else if(step === 'args') {
|
|
1554
1566
|
// Should only be reached for cases, where no SELECT in a union is picked.
|
|
@@ -1595,6 +1607,13 @@ function queryDepthForMessage( csnPath, model, view ) {
|
|
|
1595
1607
|
return 0;
|
|
1596
1608
|
}
|
|
1597
1609
|
|
|
1610
|
+
function sanitizeCsnPath(csnPath) {
|
|
1611
|
+
for (const step of csnPath) {
|
|
1612
|
+
if (typeof step !== 'string' && typeof step !== 'number')
|
|
1613
|
+
throw new CompilerAssertion(`Found CSN path step that is neither string nor number: ${step} ${ JSON.stringify(csnPath) }`)
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1598
1617
|
/**
|
|
1599
1618
|
* Get the explanation string for the given message-id.
|
|
1600
1619
|
* Ensure to have called hasMessageExplanation() before.
|
package/lib/base/model.js
CHANGED
package/lib/base/shuffle.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// By <https://github.com/bryc/code/blob/c97a26ad27a9f9d4f48cd3307fd8ee6f1772d4eb/jshash/PRNGs.md>:
|
|
4
4
|
|
|
5
|
-
// The random seed must be an integer between
|
|
5
|
+
// The random seed must be an integer between 1 and 4294967296 (= 2**32),
|
|
6
|
+
// otherwise no shuffling takes place.
|
|
6
7
|
function shuffleGen( seed ) {
|
|
7
8
|
return (Number.isSafeInteger( seed ) && seed > 0)
|
|
8
9
|
? { shuffleArray, shuffleDict }
|
package/lib/checks/elements.js
CHANGED
|
@@ -208,6 +208,34 @@ function checkRecursiveTypeUsage( art ) {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Member validator to check that certain annotations (@cds.valid { from, to, key }) are not
|
|
214
|
+
* assigned to calculated elements in an entity.
|
|
215
|
+
*
|
|
216
|
+
* TODO: Allow @cds.valid on persisted calculated elements (when they become available).
|
|
217
|
+
*
|
|
218
|
+
* @param {CSN.Element} member the element to be checked
|
|
219
|
+
* @param {string} _memberName the elements name
|
|
220
|
+
* @param {string} _prop which kind of member are we looking at -> only prop "elements"
|
|
221
|
+
* @param {CSN.Path} _path the path to the member
|
|
222
|
+
*/
|
|
223
|
+
function rejectAnnotationsOnCalcElement( member, _memberName, _prop, _path ) {
|
|
224
|
+
if (this.artifact.kind === 'entity' && !(this.artifact.query && this.artifact.projection)) {
|
|
225
|
+
if (member.value) {
|
|
226
|
+
for (const anno in member) {
|
|
227
|
+
if (anno.startsWith('@cds.valid.')) {
|
|
228
|
+
this.error('anno-unexpected-temporal', member.$path, { anno },
|
|
229
|
+
'Unexpected $(ANNO) assigned to a calculated element');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
211
235
|
module.exports = {
|
|
212
|
-
checkPrimaryKey,
|
|
236
|
+
checkPrimaryKey,
|
|
237
|
+
checkVirtualElement,
|
|
238
|
+
checkManagedAssoc,
|
|
239
|
+
checkRecursiveTypeUsage,
|
|
240
|
+
rejectAnnotationsOnCalcElement,
|
|
213
241
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isPersistedOnDatabase } = require('../model/csnUtils.js');
|
|
4
3
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
5
4
|
// not relevant for odata - entities need to be checked at the end of the transformation
|
|
5
|
+
|
|
6
|
+
const { isPersistedOnDatabase } = require('../model/csnUtils.js');
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Ensure that empty/only virtual entities do not reach the db.
|
|
8
10
|
*
|
|
@@ -11,22 +13,24 @@ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
|
|
|
11
13
|
* @param {string} prop Property being looped over
|
|
12
14
|
* @param {CSN.Path} path Path to the artifact
|
|
13
15
|
*/
|
|
14
|
-
function
|
|
16
|
+
function validateHasPersistedElements( artifact, artifactName, prop, path ) {
|
|
15
17
|
if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact)) {
|
|
16
18
|
if (!artifact.elements || !hasRealElements(artifact.elements))
|
|
19
|
+
// TODO: Maybe check if there are only calc elements and adapt the message?
|
|
17
20
|
this.error('def-missing-element', path, { '#': artifact.query ? 'view' : 'std' });
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
|
-
* Check if the provided elements contain elements that will be created on the
|
|
25
|
+
* Check if the provided elements contain elements that will be created on the database.
|
|
26
|
+
* This includes virtual and calculated elements.
|
|
23
27
|
*
|
|
24
28
|
* @param {CSN.Elements} elements Elements to look through
|
|
25
29
|
* @returns {boolean} True if something would be created on the db from these elements.
|
|
26
30
|
*/
|
|
27
31
|
function hasRealElements( elements ) {
|
|
28
32
|
for (const element of Object.values(elements)) {
|
|
29
|
-
if (!element.virtual) {
|
|
33
|
+
if (!element.virtual && !element.value) {
|
|
30
34
|
if (element.elements) {
|
|
31
35
|
if (hasRealElements(element.elements))
|
|
32
36
|
return true;
|
|
@@ -41,4 +45,4 @@ function hasRealElements( elements ) {
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
|
|
44
|
-
module.exports =
|
|
48
|
+
module.exports = validateHasPersistedElements;
|