@sap/cds-compiler 4.2.4 → 4.3.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 +26 -0
- package/bin/cdsc.js +8 -0
- package/bin/cdshi.js +3 -3
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +19 -0
- package/lib/base/location.js +16 -0
- package/lib/base/message-registry.js +47 -16
- package/lib/base/messages.js +49 -38
- package/lib/base/model.js +1 -1
- package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
- package/lib/checks/existsMustEndInAssoc.js +27 -0
- package/lib/checks/onConditions.js +47 -1
- package/lib/checks/validator.js +10 -1
- package/lib/compiler/assert-consistency.js +23 -15
- package/lib/compiler/base.js +31 -14
- package/lib/compiler/builtins.js +21 -20
- package/lib/compiler/checks.js +36 -49
- package/lib/compiler/define.js +71 -91
- package/lib/compiler/extend.js +27 -25
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +67 -87
- package/lib/compiler/kick-start.js +7 -5
- package/lib/compiler/populate.js +32 -30
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +29 -25
- package/lib/compiler/shared.js +57 -31
- package/lib/compiler/tweak-assocs.js +203 -22
- package/lib/compiler/utils.js +0 -18
- package/lib/gen/Dictionary.json +10 -4
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/languageParser.js +3 -3
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +63 -28
- package/lib/json/to-csn.js +23 -13
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/errorStrategy.js +5 -1
- package/lib/language/genericAntlrParser.js +67 -61
- package/lib/main.d.ts +26 -1
- package/lib/main.js +2 -1
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +28 -0
- package/lib/model/revealInternalProperties.js +3 -9
- package/lib/optionProcessor.js +17 -1
- package/lib/render/toCdl.js +1 -1
- package/lib/transform/db/associations.js +3 -4
- package/lib/transform/db/backlinks.js +293 -0
- package/lib/transform/db/expansion.js +9 -7
- package/lib/transform/db/flattening.js +3 -2
- package/lib/transform/db/rewriteCalculatedElements.js +1 -67
- package/lib/transform/db/transformExists.js +3 -58
- package/lib/transform/db/views.js +8 -14
- package/lib/transform/effective/.eslintrc.json +4 -0
- package/lib/transform/effective/associations.js +101 -0
- package/lib/transform/effective/main.js +88 -0
- package/lib/transform/effective/misc.js +61 -0
- package/lib/transform/effective/queries.js +42 -0
- package/lib/transform/effective/types.js +121 -0
- package/lib/transform/forRelationalDB.js +12 -235
- package/lib/transform/localized.js +22 -3
- package/lib/transform/parseExpr.js +7 -3
- package/lib/transform/transformUtils.js +5 -22
- package/lib/transform/translateAssocsToJoins.js +42 -38
- package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
- package/package.json +1 -2
- package/lib/language/language.g4 +0 -3260
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@
|
|
|
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.3.0 - 2023-09-29
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- compiler: it is possible to publish associations with filters in views.
|
|
15
|
+
Managed associations become unmanaged ones. For example:
|
|
16
|
+
```cds
|
|
17
|
+
entity Proj as projection on Base {
|
|
18
|
+
assoc[id = 1],
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Update OData vocabularies: 'Aggregation', 'Capabilities', 'Common', 'PDF', 'PersonalData', 'UI'.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- parser: Chained methods without arguments such as `b` in `a().b.c()` were lost.
|
|
29
|
+
- compiler:
|
|
30
|
+
+ Type arguments in `cast()` functions, whose column also has an explicit type set, were not
|
|
31
|
+
properly checked. Now the `cast()`s type and type arguments are checked.
|
|
32
|
+
+ SQL function `STDDEV(*)` was not parsable.
|
|
33
|
+
+ In views, published association's ON-conditions containing `$projection` are now rewritten
|
|
34
|
+
to `$self` in the CSN `elements` property. This ensures recompilability of the CSN.
|
|
35
|
+
|
|
10
36
|
## Version 4.2.4 - 2023-09-14
|
|
11
37
|
|
|
12
38
|
### Fixed
|
package/bin/cdsc.js
CHANGED
|
@@ -278,6 +278,7 @@ function executeCommandLine( command, options, args ) {
|
|
|
278
278
|
manageConstraints,
|
|
279
279
|
toSql,
|
|
280
280
|
inspect,
|
|
281
|
+
toEffectiveCsn,
|
|
281
282
|
};
|
|
282
283
|
const commandsWithoutCompilation = {
|
|
283
284
|
explain,
|
|
@@ -317,6 +318,13 @@ function executeCommandLine( command, options, args ) {
|
|
|
317
318
|
return model;
|
|
318
319
|
}
|
|
319
320
|
|
|
321
|
+
function toEffectiveCsn( model ) {
|
|
322
|
+
const csn = options.directBackend ? model : compactModel(model, options);
|
|
323
|
+
displayNamedCsn(main.for.effective(csn, options), 'effective');
|
|
324
|
+
|
|
325
|
+
return model;
|
|
326
|
+
}
|
|
327
|
+
|
|
320
328
|
// Execute the command line option 'toCsn' and display the results.
|
|
321
329
|
// Return the original model (for chaining)
|
|
322
330
|
function toCsn( model ) {
|
package/bin/cdshi.js
CHANGED
|
@@ -41,11 +41,11 @@ function highlight( err, buf ) {
|
|
|
41
41
|
continue;
|
|
42
42
|
if (tok.$isSkipped) {
|
|
43
43
|
if (tok.stop > tok.start) {
|
|
44
|
-
chars[tok.start] = (tok.$isSkipped === true ? '\x0f' : '\x16');
|
|
45
|
-
chars[tok.stop] = '\x17';
|
|
44
|
+
chars[tok.start] = (tok.$isSkipped === true ? '\x0f' : '\x16'); // ^O / ^V
|
|
45
|
+
chars[tok.stop] = '\x17'; // ^W
|
|
46
46
|
}
|
|
47
47
|
else {
|
|
48
|
-
chars[tok.start] = (tok.$isSkipped === true ? '\x0e' : '\x15');
|
|
48
|
+
chars[tok.start] = (tok.$isSkipped === true ? '\x0e' : '\x15'); // ^N / ^U
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
else {
|
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,13 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 4.3.0 - 2023-09-29
|
|
12
|
+
|
|
13
|
+
### Removed `associationDefault`
|
|
14
|
+
|
|
15
|
+
This flag is now the default. Managed associations with exactly one foreign key can now
|
|
16
|
+
have a default value.
|
|
17
|
+
|
|
11
18
|
## Version 4.1.0 - 2023-07-28
|
|
12
19
|
|
|
13
20
|
### Added `associationDefault`
|
package/lib/api/main.js
CHANGED
|
@@ -18,6 +18,7 @@ const csnUtils = lazyload('../model/csnUtils');
|
|
|
18
18
|
const timetrace = lazyload('../utils/timetrace');
|
|
19
19
|
const forRelationalDB = lazyload('../transform/forRelationalDB');
|
|
20
20
|
const sqlUtils = lazyload('../render/utils/sql');
|
|
21
|
+
const effective = lazyload('../transform/effective/main');
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Return the artifact name for use for the hdbresult object
|
|
@@ -225,6 +226,23 @@ function forHdbcds( csn, options = {} ) {
|
|
|
225
226
|
return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
226
227
|
}
|
|
227
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Effective CSN transformation
|
|
231
|
+
*
|
|
232
|
+
* @param {CSN.Model} csn Plain input CSN
|
|
233
|
+
* @param {EffectiveCsnOptions} [options={}] Options
|
|
234
|
+
* @returns {CSN.Model} CSN transformed
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
function forEffective( csn, options = {} ) {
|
|
238
|
+
const internalOptions = prepareOptions.to.sql(options);
|
|
239
|
+
internalOptions.transformation = 'effective';
|
|
240
|
+
|
|
241
|
+
const eCsn = effective.effectiveCsn(csn, internalOptions);
|
|
242
|
+
|
|
243
|
+
return internalOptions.testMode ? toCsn.sortCsn(eCsn, internalOptions) : eCsn;
|
|
244
|
+
}
|
|
245
|
+
|
|
228
246
|
/**
|
|
229
247
|
* Process the given CSN into SQL.
|
|
230
248
|
*
|
|
@@ -851,6 +869,7 @@ module.exports = {
|
|
|
851
869
|
for_sql: publishCsnProcessor(forSql, 'for.sql'),
|
|
852
870
|
for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
|
|
853
871
|
for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
|
|
872
|
+
for_effective: publishCsnProcessor(forEffective, 'for.effective'),
|
|
854
873
|
/** Deprecated, will be removed in cds-compiler@v4 */
|
|
855
874
|
preparedCsnToEdmx,
|
|
856
875
|
preparedCsnToEdm,
|
package/lib/base/location.js
CHANGED
|
@@ -71,6 +71,21 @@ function emptyWeakLocation( filename ) {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @param {CsnLocation} loc
|
|
76
|
+
* @returns {CsnLocation}
|
|
77
|
+
*/
|
|
78
|
+
function weakLocation( loc ) {
|
|
79
|
+
return {
|
|
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
|
+
|
|
74
89
|
/**
|
|
75
90
|
* Returns a dummy location for built-in definitions.
|
|
76
91
|
*
|
|
@@ -156,6 +171,7 @@ module.exports = {
|
|
|
156
171
|
combinedLocation,
|
|
157
172
|
emptyLocation,
|
|
158
173
|
emptyWeakLocation,
|
|
174
|
+
weakLocation,
|
|
159
175
|
builtinLocation,
|
|
160
176
|
dictLocation,
|
|
161
177
|
locationString,
|
|
@@ -74,7 +74,7 @@ const centralMessages = {
|
|
|
74
74
|
'def-unexpected-calcview-assoc': { severity: 'Error' },
|
|
75
75
|
'chained-array-of': { severity: 'Error' },
|
|
76
76
|
'def-missing-type': { severity: 'Error', configurableFor: [ 'compile' ] },
|
|
77
|
-
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename' ] },
|
|
77
|
+
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename', 'for.effective' ] },
|
|
78
78
|
|
|
79
79
|
'def-duplicate-autoexposed': { severity: 'Error' },
|
|
80
80
|
'def-unexpected-default': { severity: 'Error', configurableFor: 'test' },
|
|
@@ -92,6 +92,7 @@ const centralMessages = {
|
|
|
92
92
|
'type-expected-builtin': { severity: 'Error', configurableFor: true },
|
|
93
93
|
'type-expecting-service-target': { severity: 'Error', configurableFor: true },
|
|
94
94
|
'ref-expecting-const': { severity: 'Error' },
|
|
95
|
+
'ref-expecting-foreign-key': { severity: 'Error' },
|
|
95
96
|
'ref-invalid-source': { severity: 'Error' },
|
|
96
97
|
'ref-invalid-target': { severity: 'Error' },
|
|
97
98
|
'ref-sloppy-target': { severity: 'Error', configurableFor: 'v4' },
|
|
@@ -186,13 +187,13 @@ const centralMessages = {
|
|
|
186
187
|
'odata-spec-violation-key-null': { severity: 'Error', configurableFor: true }, // more than 30 chars
|
|
187
188
|
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
188
189
|
'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
|
|
189
|
-
'odata-anno-preproc': { severity: 'Warning'
|
|
190
|
-
'odata-anno-dict': { severity: 'Warning'
|
|
191
|
-
'odata-anno-vocref': { severity: 'Warning'
|
|
190
|
+
'odata-anno-preproc': { severity: 'Warning' },
|
|
191
|
+
'odata-anno-dict': { severity: 'Warning' },
|
|
192
|
+
'odata-anno-vocref': { severity: 'Warning' },
|
|
192
193
|
'odata-anno-dict-enum': { severity: 'Error' },
|
|
193
|
-
'odata-anno-value': { severity: 'Warning'
|
|
194
|
-
'odata-anno-type': { severity: 'Warning'
|
|
195
|
-
'odata-anno-def': { severity: 'Info'
|
|
194
|
+
'odata-anno-value': { severity: 'Warning' },
|
|
195
|
+
'odata-anno-type': { severity: 'Warning' },
|
|
196
|
+
'odata-anno-def': { severity: 'Info' },
|
|
196
197
|
'query-ignoring-assoc-in-union': { severity: 'Info' }
|
|
197
198
|
};
|
|
198
199
|
|
|
@@ -274,6 +275,12 @@ const centralMessageTexts = {
|
|
|
274
275
|
'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO)',
|
|
275
276
|
'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
|
|
276
277
|
'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
|
|
278
|
+
|
|
279
|
+
'anno-unexpected-localized-skip': {
|
|
280
|
+
std: 'Compiler generated entity $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped',
|
|
281
|
+
view: 'Compiler generated view $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped',
|
|
282
|
+
},
|
|
283
|
+
|
|
277
284
|
'chained-array-of': '"Array of"/"many" must not be chained with another "array of"/"many" inside a service',
|
|
278
285
|
|
|
279
286
|
'check-proper-type-of': {
|
|
@@ -463,12 +470,15 @@ const centralMessageTexts = {
|
|
|
463
470
|
'syntax-missing-property': { // location at sibling or '}' otherwise
|
|
464
471
|
std: 'Object in $(PARENTPROP) must have the property $(PROP)',
|
|
465
472
|
sibling: 'Object with property $(SIBLINGPROP) must also have a property $(PROP)',
|
|
473
|
+
bothTargets: 'Object with properties $(SIBLINGPROP) and $(OTHERPROP) must also have a property $(PROP)',
|
|
466
474
|
columns: 'Object in $(PARENTPROP) must have an expression property like $(PROP)',
|
|
467
475
|
extensions: 'Object in $(PARENTPROP) must have the property $(PROP) or $(OTHERPROP)',
|
|
468
476
|
},
|
|
469
477
|
'syntax-unexpected-property': {
|
|
470
478
|
std: 'Unexpected CSN property $(PROP)',
|
|
471
479
|
sibling: 'CSN property $(PROP) is not expected in an object with property $(SIBLINGPROP)',
|
|
480
|
+
target: 'CSN property $(PROP) with sub property $(SUBPROP) is not expected in an object with property $(SIBLINGPROP)',
|
|
481
|
+
targetAspect: 'CSN property $(PROP) is not expected in an object with property $(SIBLINGPROP) having sub property $(SUBPROP)',
|
|
472
482
|
prop: 'CSN property $(PROP) is not expected in $(PARENTPROP)',
|
|
473
483
|
top: 'CSN property $(PROP) is not expected top-level',
|
|
474
484
|
kind: 'CSN property $(PROP) is not expected by a definition of kind $(KIND)',
|
|
@@ -590,6 +600,9 @@ const centralMessageTexts = {
|
|
|
590
600
|
expr: 'Associations can\'t be used as values in expressions',
|
|
591
601
|
'expr-comp': 'Compositions can\'t be used as values in expressions',
|
|
592
602
|
cast: 'Casting to an association is not supported',
|
|
603
|
+
|
|
604
|
+
'managed-filter': 'Unexpected managed association $(NAME) in filter expression of $(ID)',
|
|
605
|
+
'unmanaged-filter': 'Unexpected unmanaged association $(NAME) in filter expression of $(ID)'
|
|
593
606
|
},
|
|
594
607
|
'ref-unexpected-calculated': {
|
|
595
608
|
std: 'Unexpected reference to calculated element',
|
|
@@ -619,6 +632,9 @@ const centralMessageTexts = {
|
|
|
619
632
|
calc: 'Unexpected arguments in path $(ELEMREF) of stored calculated element; only simple paths can be used here'
|
|
620
633
|
},
|
|
621
634
|
|
|
635
|
+
// TODO: Better text ?
|
|
636
|
+
'rewrite-not-supported': 'The ON-condition is not rewritten here - provide an explicit ON-condition',
|
|
637
|
+
|
|
622
638
|
'type-unexpected-typeof': {
|
|
623
639
|
std: 'Unexpected $(KEYWORD) for the type reference here',
|
|
624
640
|
type: 'Unexpected $(KEYWORD) for the type of a type definition',
|
|
@@ -703,7 +719,7 @@ const centralMessageTexts = {
|
|
|
703
719
|
},
|
|
704
720
|
'def-unexpected-key': {
|
|
705
721
|
std: '$(ART) can\'t have additional keys',
|
|
706
|
-
virtual: 'Unexpected $(PROP) for virtual element
|
|
722
|
+
virtual: 'Unexpected $(PROP) for virtual element',
|
|
707
723
|
// TODO: Better message?
|
|
708
724
|
include: '$(ART) can\'t have additional keys (through include)',
|
|
709
725
|
},
|
|
@@ -754,7 +770,7 @@ const centralMessageTexts = {
|
|
|
754
770
|
'include-elements': 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
755
771
|
'include-actions': 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
756
772
|
},
|
|
757
|
-
// TODO: Remove in
|
|
773
|
+
// TODO: Remove in v5, use duplicate-definition (or another ID?)
|
|
758
774
|
'ref-duplicate-include-member': {
|
|
759
775
|
std: 'Duplicate member $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
760
776
|
elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
@@ -773,7 +789,9 @@ const centralMessageTexts = {
|
|
|
773
789
|
missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
|
|
774
790
|
},
|
|
775
791
|
|
|
792
|
+
'ref-expecting-assoc': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
|
|
776
793
|
'ref-expecting-const': 'A constant expression or variable is expected here',
|
|
794
|
+
'ref-expecting-foreign-key': 'Expecting foreign key access after managed association $(NAME) in filter expression of $(ID), but found $(ALIAS)',
|
|
777
795
|
'ref-invalid-target': {
|
|
778
796
|
std: 'An entity, projection or view is expected here', // TODO: change text
|
|
779
797
|
composition: 'Expecting an entity or aspect as composition target',
|
|
@@ -826,6 +844,11 @@ const centralMessageTexts = {
|
|
|
826
844
|
std: 'Ignoring nullability constraint on parameter when generating SAP HANA CDS view',
|
|
827
845
|
sql: 'Ignoring nullability constraint on parameter when generating SQL view'
|
|
828
846
|
},
|
|
847
|
+
'query-ignoring-filter': {
|
|
848
|
+
std: 'Ignoring filter on published association due to explicit redirection', // unused
|
|
849
|
+
onCond: 'Ignoring filter on published association due to explicit redirection with ON-condition',
|
|
850
|
+
fKey: 'Ignoring filter on published association due to explicit redirection with explicit foreign keys',
|
|
851
|
+
},
|
|
829
852
|
'query-expected-identifier': {
|
|
830
853
|
std: 'Expected identifier for select item',
|
|
831
854
|
assoc: 'Expected identifier as the association\'s name',
|
|
@@ -842,15 +865,19 @@ const centralMessageTexts = {
|
|
|
842
865
|
extra: 'Specified element $(NAME) differs from inferred element: it has an additional property $(PROP)',
|
|
843
866
|
target: 'Expected target $(TARGET) of specified element $(NAME) to be the same as the inferred element\'s target $(ART)',
|
|
844
867
|
foreignKeys: 'Expected foreign keys of specified element $(NAME) to be the same as the inferred element\'s foreign keys',
|
|
868
|
+
unmanagedToManaged: 'Unexpected foreign keys in specified element $(NAME); inferred element is a managed association',
|
|
845
869
|
prop: 'Value for $(PROP) of the specified element $(NAME) does not match the inferred element\'s value',
|
|
846
870
|
enumExtra: 'Specified element $(NAME) differs from inferred element: it has an additional enum element $(ID)',
|
|
847
871
|
enumVal: 'Specified element $(NAME) differs from inferred element: it has a different value for enum element $(ID)',
|
|
848
872
|
},
|
|
849
|
-
|
|
850
873
|
'query-unexpected-property': {
|
|
851
874
|
std: 'Unexpected property $(PROP) in the specified element $(NAME)',
|
|
852
875
|
calculatedElement: 'Unexpected property $(PROP) in the specified element $(NAME); calculated elements are not supported in queries',
|
|
853
876
|
},
|
|
877
|
+
'query-ignoring-assoc-in-union': {
|
|
878
|
+
'managed': 'Ignoring managed association $(NAME) that is published in a UNION',
|
|
879
|
+
'std': 'Ignoring association $(NAME) that is published in a UNION'
|
|
880
|
+
},
|
|
854
881
|
|
|
855
882
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
856
883
|
|
|
@@ -900,8 +927,14 @@ const centralMessageTexts = {
|
|
|
900
927
|
|
|
901
928
|
'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
|
|
902
929
|
|
|
903
|
-
'expr-missing-foreign-key':
|
|
930
|
+
'expr-missing-foreign-key': {
|
|
931
|
+
std: 'Path step $(ID) of $(ELEMREF) has no valid foreign keys',
|
|
932
|
+
publishingFilter: 'Can\'t publish managed association $(ID) with filter, as it must have at least one foreign key',
|
|
933
|
+
},
|
|
904
934
|
|
|
935
|
+
// -----------------------------------------------------------------------------------
|
|
936
|
+
// OData Message section starts here
|
|
937
|
+
// -----------------------------------------------------------------------------------
|
|
905
938
|
// OData version dependent messages
|
|
906
939
|
'odata-spec-violation-array': 'Unexpected array type for OData $(VERSION)',
|
|
907
940
|
'odata-spec-violation-param' : 'Expected parameter to be typed with either scalar or structured type for OData $(VERSION)',
|
|
@@ -1017,11 +1050,9 @@ const centralMessageTexts = {
|
|
|
1017
1050
|
'deprecated': '$(ANNO) is deprecated. $(DEPR)',
|
|
1018
1051
|
'notapplied': '$(ANNO) is not applied (AppliesTo: $(RAWVALUES))',
|
|
1019
1052
|
},
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1053
|
+
// -----------------------------------------------------------------------------------
|
|
1054
|
+
// OData Message section ends here, no messages below this line
|
|
1055
|
+
// -----------------------------------------------------------------------------------
|
|
1025
1056
|
}
|
|
1026
1057
|
|
|
1027
1058
|
/**
|
package/lib/base/messages.js
CHANGED
|
@@ -670,6 +670,7 @@ const paramsTransform = {
|
|
|
670
670
|
prop: quote.single,
|
|
671
671
|
siblingprop: quote.single,
|
|
672
672
|
parentprop: quote.single,
|
|
673
|
+
subprop: quote.single,
|
|
673
674
|
otherprop: quote.single,
|
|
674
675
|
code: quote.single,
|
|
675
676
|
newcode: quote.single,
|
|
@@ -820,10 +821,11 @@ function transformArg( arg, r, args, texts ) {
|
|
|
820
821
|
return quoted(`${ arg.ref[0] }:${ arg.ref.slice(1).join('.') }`);
|
|
821
822
|
return quoted(arg.ref);
|
|
822
823
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
824
|
+
if (!arg.name)
|
|
825
|
+
return quoted( arg.name );
|
|
826
|
+
const name = getArtifactName( arg );
|
|
826
827
|
const prop = [ 'element', 'param', 'action', 'alias' ].find( p => name[p] );
|
|
828
|
+
//if (!prop) throw Error()
|
|
827
829
|
if (!prop || !texts[prop] )
|
|
828
830
|
return shortArtName( arg );
|
|
829
831
|
r['#'] = texts[name.$variant] && name.$variant || prop; // text variant (set by searchName)
|
|
@@ -831,12 +833,6 @@ function transformArg( arg, r, args, texts ) {
|
|
|
831
833
|
return artName( arg, prop );
|
|
832
834
|
}
|
|
833
835
|
|
|
834
|
-
const nameProp = {
|
|
835
|
-
enum: 'element',
|
|
836
|
-
key: 'element',
|
|
837
|
-
function: 'action',
|
|
838
|
-
};
|
|
839
|
-
|
|
840
836
|
// TODO: very likely delete this function
|
|
841
837
|
function searchName( art, id, variant ) {
|
|
842
838
|
if (!variant) {
|
|
@@ -844,17 +840,30 @@ function searchName( art, id, variant ) {
|
|
|
844
840
|
// originally provided one (TODO: mention that in the message text)
|
|
845
841
|
const type = art._effectiveType && art._effectiveType.kind !== 'undefined' ? art._effectiveType : art;
|
|
846
842
|
if (type.elements) { // only mentioned elements
|
|
847
|
-
art = type.target
|
|
843
|
+
art = type.target?._artifact || type;
|
|
848
844
|
variant = 'element';
|
|
849
845
|
}
|
|
850
846
|
else {
|
|
851
847
|
variant = 'absolute';
|
|
852
848
|
}
|
|
853
849
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
850
|
+
if (variant === 'absolute') {
|
|
851
|
+
const absolute = `${ art.name.id }.${ id }`;
|
|
852
|
+
return {
|
|
853
|
+
kind: art.kind,
|
|
854
|
+
name: { id: absolute, $variant: variant },
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
const undef = {
|
|
858
|
+
kind: variant || art.kind,
|
|
859
|
+
name: { id, $variant: variant },
|
|
860
|
+
};
|
|
861
|
+
Object.defineProperty( undef, '_parent',
|
|
862
|
+
{ value: art, configurable: true, writable: true } );
|
|
863
|
+
Object.defineProperty( undef, '_main',
|
|
864
|
+
{ value: art._main || art, configurable: true, writable: true } );
|
|
865
|
+
// console.log('SN:',undef)
|
|
866
|
+
return undef;
|
|
858
867
|
}
|
|
859
868
|
|
|
860
869
|
function messageText( texts, params, transform ) {
|
|
@@ -892,17 +901,6 @@ function replaceInString( text, params ) {
|
|
|
892
901
|
return parts.join('');
|
|
893
902
|
}
|
|
894
903
|
|
|
895
|
-
/**
|
|
896
|
-
* @param {CsnLocation} loc
|
|
897
|
-
* @returns {CsnLocation}
|
|
898
|
-
*/
|
|
899
|
-
function weakLocation( loc ) {
|
|
900
|
-
if (!loc)
|
|
901
|
-
return new CsnLocation();
|
|
902
|
-
// no endLine/endCol
|
|
903
|
-
return new CsnLocation( loc.file, loc.line, loc.col, undefined, undefined );
|
|
904
|
-
}
|
|
905
|
-
|
|
906
904
|
/**
|
|
907
905
|
* Return message string with location if present in compact form (i.e. one line).
|
|
908
906
|
*
|
|
@@ -1249,7 +1247,7 @@ function compareMessageSeverityAware( a, b ) {
|
|
|
1249
1247
|
/**
|
|
1250
1248
|
* Return sort-relevant part of semantic location (after the ':').
|
|
1251
1249
|
* Messages without semantic locations are considered smaller (for syntax errors)
|
|
1252
|
-
* and (currently - should not happen in
|
|
1250
|
+
* and (currently - should not happen in v5) larger for other messages.
|
|
1253
1251
|
*
|
|
1254
1252
|
* @param {CompileMessage} msg
|
|
1255
1253
|
*/
|
|
@@ -1299,14 +1297,9 @@ function deduplicateMessages( messages ) {
|
|
|
1299
1297
|
}
|
|
1300
1298
|
|
|
1301
1299
|
function shortArtName( art ) {
|
|
1300
|
+
if (!art.name)
|
|
1301
|
+
return artName( art );
|
|
1302
1302
|
const name = getArtifactName( art );
|
|
1303
|
-
if (!name) {
|
|
1304
|
-
const loc = art.location ? ` at ${ locationString( art.location ) }` : '';
|
|
1305
|
-
throw new CompilerAssertion(
|
|
1306
|
-
art.path
|
|
1307
|
-
? `No artifact for ${ art.path.map( i => i.id ).join( '.' )}${ loc }`
|
|
1308
|
-
: `No name found in ${ Object.keys( art ).join( '+' )}${ loc }` );
|
|
1309
|
-
}
|
|
1310
1303
|
if ([ 'select', 'action', 'alias', 'param' ].every( n => name[n] == null || name[n] === 1 ) &&
|
|
1311
1304
|
!name.absolute.includes(':'))
|
|
1312
1305
|
return quote.double( name.element ? `${ name.absolute }:${ name.element }` : name.absolute );
|
|
@@ -1314,7 +1307,21 @@ function shortArtName( art ) {
|
|
|
1314
1307
|
}
|
|
1315
1308
|
|
|
1316
1309
|
function artName( art, omit ) {
|
|
1310
|
+
let suffix = 0;
|
|
1311
|
+
while (!art.name && art._outer) {
|
|
1312
|
+
++suffix;
|
|
1313
|
+
art = art._outer;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1317
1316
|
const name = getArtifactName( art );
|
|
1317
|
+
if (!name) {
|
|
1318
|
+
const loc = art.location ? ` at ${ locationString( art.location ) }` : '';
|
|
1319
|
+
throw new CompilerAssertion(
|
|
1320
|
+
art.path
|
|
1321
|
+
? `No artifact for ${ art.path.map( i => i.id ).join( '.' )}${ loc }`
|
|
1322
|
+
: `No name found in ${ Object.keys( art ).join( '+' )}${ loc }` );
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1318
1325
|
const r = (name.absolute) ? [ quoted( name.absolute ) ] : [];
|
|
1319
1326
|
if (name.select && name.select > 1 || name.select != null && art.kind !== 'element') // Yes, omit select:1 for element - TODO: re-check
|
|
1320
1327
|
r.push( (art.kind === 'extend' ? 'block:' : 'query:') + name.select ); // TODO: rename to 'select:1' and consider whether there are more selects
|
|
@@ -1328,6 +1335,8 @@ function artName( art, omit ) {
|
|
|
1328
1335
|
if (name.element != null && omit !== 'element') {
|
|
1329
1336
|
if (name.select != null && !art.$inferred)
|
|
1330
1337
|
r.push( 'column:' + quoted( name.element ) );
|
|
1338
|
+
else if (art.kind === 'builtin')
|
|
1339
|
+
return `$var:${ quoted( name.element ) }`;
|
|
1331
1340
|
else
|
|
1332
1341
|
// r.push( `${ art.kind }: ${ quoted( name.element )}` ); or even better element:"assoc"/key:"i" same with enum
|
|
1333
1342
|
r.push( (art.kind === 'enum' ? 'enum:' : 'element:') + quoted( name.element ) );
|
|
@@ -1335,6 +1344,9 @@ function artName( art, omit ) {
|
|
|
1335
1344
|
|
|
1336
1345
|
if (art.kind === '$self')
|
|
1337
1346
|
r.push( `alias:${ quoted( name.alias ) }` ); // should be late due to $self in anonymous aspect
|
|
1347
|
+
if (suffix)
|
|
1348
|
+
r.push( art.targetAspect ? 'target' : art.items?.items ? `items:${ suffix }` : 'items');
|
|
1349
|
+
|
|
1338
1350
|
return r.join('/');
|
|
1339
1351
|
}
|
|
1340
1352
|
|
|
@@ -1361,21 +1373,21 @@ function homeName( art, absoluteOnly ) {
|
|
|
1361
1373
|
return `using:${ quoted( art.name.id ) }`;
|
|
1362
1374
|
else if (art.kind === 'extend' || art.kind === 'annotate')
|
|
1363
1375
|
return !absoluteOnly && homeNameForExtend( art );
|
|
1376
|
+
else if (!art.kind) // annotation assignments are not really supported
|
|
1377
|
+
return (absoluteOnly) ? art.name.id : quoted( `@${ art.name.id }` );
|
|
1364
1378
|
else if (art.name._artifact) // block, extend, annotate
|
|
1365
1379
|
return homeName( art.name._artifact, absoluteOnly ); // use corresponding definition
|
|
1366
|
-
else if (absoluteOnly)
|
|
1367
|
-
return art.name.absolute;
|
|
1368
1380
|
let main = art._main || art;
|
|
1369
1381
|
while (main._outer) // anonymous aspect
|
|
1370
1382
|
main = main._outer._main;
|
|
1371
|
-
return `${ main.kind }:${ artName( art ) }`;
|
|
1383
|
+
return (absoluteOnly) ? main.name.id : `${ main.kind }:${ artName( art ) }`;
|
|
1372
1384
|
}
|
|
1373
1385
|
|
|
1374
1386
|
// The "home" for extensions is handled differently because `_artifact` is not
|
|
1375
1387
|
// set for unknown extensions, and we could have nested extensions.
|
|
1376
1388
|
// TODO: delete this function, just set correct name/_parent for extensions
|
|
1377
1389
|
function homeNameForExtend( art ) {
|
|
1378
|
-
if (
|
|
1390
|
+
if (art._main) // new-style member name
|
|
1379
1391
|
return `${ art._main.kind }:${ artName( art ) }`;
|
|
1380
1392
|
const kind = art.kind || 'extend';
|
|
1381
1393
|
// TODO: fix the following - do like in collectArtifactExtensions() or
|
|
@@ -1776,7 +1788,6 @@ function messageIdsWithExplanation() {
|
|
|
1776
1788
|
|
|
1777
1789
|
module.exports = {
|
|
1778
1790
|
hasErrors,
|
|
1779
|
-
weakLocation,
|
|
1780
1791
|
locationString,
|
|
1781
1792
|
messageString,
|
|
1782
1793
|
messageStringMultiline,
|
package/lib/base/model.js
CHANGED
|
@@ -33,8 +33,8 @@ const availableBetaFlags = {
|
|
|
33
33
|
enableUniversalCsn: true,
|
|
34
34
|
odataTerms: true,
|
|
35
35
|
optionalActionFunctionParameters: true, // not supported by runtime, yet.
|
|
36
|
-
associationDefault: true,
|
|
37
36
|
annotateForeignKeys: true,
|
|
37
|
+
effectiveCsn: true,
|
|
38
38
|
// disabled by --beta-mode
|
|
39
39
|
nestedServices: false,
|
|
40
40
|
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { requireForeignKeyAccess } = require('../checks/onConditions');
|
|
4
|
+
const { applyTransformationsOnNonDictionary } = require('../model/csnUtils');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check that all paths in calculated elements-on write either access normal fields
|
|
8
|
+
* (structures are already rejected by the compiler) or access the foreign keys of
|
|
9
|
+
* associations. Filters and parameters are not allowed.
|
|
10
|
+
*
|
|
11
|
+
* @param {CSN.Element} calculatedElement
|
|
12
|
+
* @param {string} propOnParent
|
|
13
|
+
* @param {object} valueOfCalculatedElement
|
|
14
|
+
* @param {CSN.Path} path
|
|
15
|
+
*/
|
|
16
|
+
function checkPathsInStoredCalcElement( calculatedElement, propOnParent, valueOfCalculatedElement, path ) {
|
|
17
|
+
if (calculatedElement.value.stored) {
|
|
18
|
+
applyTransformationsOnNonDictionary(calculatedElement, 'value', {
|
|
19
|
+
ref: (parent, prop, value, csnPath) => {
|
|
20
|
+
_checkPathsInStoredCalcElement.call(this, parent, value, csnPath);
|
|
21
|
+
},
|
|
22
|
+
}, {}, path);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* See comment for calling function above.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} parent
|
|
31
|
+
* @param {(string|object)[]} value
|
|
32
|
+
* @param {CSN.Path} csnPath
|
|
33
|
+
*/
|
|
34
|
+
function _checkPathsInStoredCalcElement( parent, value, csnPath ) {
|
|
35
|
+
const { _links } = parent;
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < value.length; ++i) {
|
|
38
|
+
let hasPathError = false;
|
|
39
|
+
const step = value[i];
|
|
40
|
+
const stepArt = _links[i].art;
|
|
41
|
+
|
|
42
|
+
if (stepArt.value) {
|
|
43
|
+
applyTransformationsOnNonDictionary(stepArt, 'value', {
|
|
44
|
+
ref: (nestedParent, prop, nestedValue, path) => {
|
|
45
|
+
_checkPathsInStoredCalcElement.call(this, nestedParent, nestedValue, path);
|
|
46
|
+
},
|
|
47
|
+
}, {}, csnPath);
|
|
48
|
+
}
|
|
49
|
+
else if (stepArt.target) {
|
|
50
|
+
const id = step.id || step;
|
|
51
|
+
if (stepArt.on) {
|
|
52
|
+
// It's an unmanaged association - traversal is always forbidden
|
|
53
|
+
this.error('ref-unexpected-navigation', csnPath, { '#': 'calc-unmanaged', id, elemref: parent });
|
|
54
|
+
hasPathError = true;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// It's a managed association - access of the foreign keys is allowed
|
|
58
|
+
requireForeignKeyAccess(parent, i, (errorIndex) => {
|
|
59
|
+
this.error('ref-unexpected-navigation', csnPath, {
|
|
60
|
+
'#': 'calc-non-fk', id, elemref: parent, name: value[errorIndex].id || value[errorIndex],
|
|
61
|
+
});
|
|
62
|
+
hasPathError = true;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (typeof step === 'object') {
|
|
67
|
+
if (step.where) {
|
|
68
|
+
this.error('ref-unexpected-filter', csnPath, { '#': 'calc', elemref: parent });
|
|
69
|
+
hasPathError = true;
|
|
70
|
+
}
|
|
71
|
+
if (step.args) {
|
|
72
|
+
this.error('ref-unexpected-args', csnPath, { '#': 'calc', elemref: parent });
|
|
73
|
+
hasPathError = true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (hasPathError)
|
|
77
|
+
break; // avoid too many consequent errors
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
value: checkPathsInStoredCalcElement,
|
|
83
|
+
};
|