@sap/cds-compiler 5.5.2 → 5.6.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 +12 -1
- package/bin/cdsse.js +3 -0
- package/lib/base/message-registry.js +2 -0
- package/lib/compiler/builtins.js +2 -0
- package/lib/edm/annotations/vocabularyDefinitions.js +6 -0
- package/lib/gen/BaseParser.js +115 -76
- package/lib/gen/CdlParser.js +839 -841
- package/lib/gen/Dictionary.json +185 -6
- package/lib/parsers/AstBuildingParser.js +63 -28
- package/lib/parsers/CdlGrammar.g4 +36 -22
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/forOdata.js +1 -1
- package/lib/transform/odata/createForeignKeys.js +92 -9
- package/package.json +1 -1
package/lib/gen/Dictionary.json
CHANGED
|
@@ -1114,6 +1114,13 @@
|
|
|
1114
1114
|
],
|
|
1115
1115
|
"$experimental": true
|
|
1116
1116
|
},
|
|
1117
|
+
"Common.WebSocketChannel": {
|
|
1118
|
+
"Type": "Edm.String",
|
|
1119
|
+
"AppliesTo": [
|
|
1120
|
+
"EntityContainer"
|
|
1121
|
+
],
|
|
1122
|
+
"$experimental": true
|
|
1123
|
+
},
|
|
1117
1124
|
"Communication.Contact": {
|
|
1118
1125
|
"Type": "Communication.ContactType",
|
|
1119
1126
|
"AppliesTo": [
|
|
@@ -1463,6 +1470,61 @@
|
|
|
1463
1470
|
"EntitySet"
|
|
1464
1471
|
]
|
|
1465
1472
|
},
|
|
1473
|
+
"EntityRelationship.entityType": {
|
|
1474
|
+
"Type": "Edm.String",
|
|
1475
|
+
"AppliesTo": [
|
|
1476
|
+
"EntityType"
|
|
1477
|
+
],
|
|
1478
|
+
"$experimental": true
|
|
1479
|
+
},
|
|
1480
|
+
"EntityRelationship.propertyType": {
|
|
1481
|
+
"Type": "Edm.String",
|
|
1482
|
+
"AppliesTo": [
|
|
1483
|
+
"Property"
|
|
1484
|
+
],
|
|
1485
|
+
"$experimental": true
|
|
1486
|
+
},
|
|
1487
|
+
"EntityRelationship.entityIds": {
|
|
1488
|
+
"Type": "Collection(EntityRelationship.entityId)",
|
|
1489
|
+
"AppliesTo": [
|
|
1490
|
+
"EntityType"
|
|
1491
|
+
],
|
|
1492
|
+
"$experimental": true
|
|
1493
|
+
},
|
|
1494
|
+
"EntityRelationship.reference": {
|
|
1495
|
+
"Type": "EntityRelationship.singleReference",
|
|
1496
|
+
"AppliesTo": [
|
|
1497
|
+
"Property"
|
|
1498
|
+
]
|
|
1499
|
+
},
|
|
1500
|
+
"EntityRelationship.compositeReferences": {
|
|
1501
|
+
"Type": "Collection(EntityRelationship.compositeReference)",
|
|
1502
|
+
"AppliesTo": [
|
|
1503
|
+
"EntityType"
|
|
1504
|
+
],
|
|
1505
|
+
"$experimental": true
|
|
1506
|
+
},
|
|
1507
|
+
"EntityRelationship.temporalIds": {
|
|
1508
|
+
"Type": "Collection(EntityRelationship.temporalId)",
|
|
1509
|
+
"AppliesTo": [
|
|
1510
|
+
"EntityType"
|
|
1511
|
+
],
|
|
1512
|
+
"$experimental": true
|
|
1513
|
+
},
|
|
1514
|
+
"EntityRelationship.temporalReferences": {
|
|
1515
|
+
"Type": "Collection(EntityRelationship.temporalReference)",
|
|
1516
|
+
"AppliesTo": [
|
|
1517
|
+
"EntityType"
|
|
1518
|
+
],
|
|
1519
|
+
"$experimental": true
|
|
1520
|
+
},
|
|
1521
|
+
"EntityRelationship.referencesWithConstantIds": {
|
|
1522
|
+
"Type": "Collection(EntityRelationship.referencesWithConstantId)",
|
|
1523
|
+
"AppliesTo": [
|
|
1524
|
+
"EntityType"
|
|
1525
|
+
],
|
|
1526
|
+
"$experimental": true
|
|
1527
|
+
},
|
|
1466
1528
|
"Graph.traceId": {
|
|
1467
1529
|
"Type": "Edm.String",
|
|
1468
1530
|
"$experimental": true
|
|
@@ -1719,6 +1781,7 @@
|
|
|
1719
1781
|
"EntityType",
|
|
1720
1782
|
"Action",
|
|
1721
1783
|
"Function",
|
|
1784
|
+
"ActionImport",
|
|
1722
1785
|
"FunctionImport"
|
|
1723
1786
|
]
|
|
1724
1787
|
},
|
|
@@ -1783,7 +1846,8 @@
|
|
|
1783
1846
|
"Type": "Collection(UI.CriticalityLabelType)",
|
|
1784
1847
|
"AppliesTo": [
|
|
1785
1848
|
"Property",
|
|
1786
|
-
"EntityType"
|
|
1849
|
+
"EntityType",
|
|
1850
|
+
"TypeDefinition"
|
|
1787
1851
|
],
|
|
1788
1852
|
"$experimental": true
|
|
1789
1853
|
},
|
|
@@ -1823,6 +1887,16 @@
|
|
|
1823
1887
|
"EntityType"
|
|
1824
1888
|
]
|
|
1825
1889
|
},
|
|
1890
|
+
"UI.OperationParameterFacets": {
|
|
1891
|
+
"Type": "Collection(UI.ReferenceFacet)",
|
|
1892
|
+
"AppliesTo": [
|
|
1893
|
+
"Action",
|
|
1894
|
+
"Function",
|
|
1895
|
+
"ActionImport",
|
|
1896
|
+
"FunctionImport"
|
|
1897
|
+
],
|
|
1898
|
+
"$experimental": true
|
|
1899
|
+
},
|
|
1826
1900
|
"UI.SelectionPresentationVariant": {
|
|
1827
1901
|
"Type": "UI.SelectionPresentationVariantType",
|
|
1828
1902
|
"AppliesTo": [
|
|
@@ -1878,14 +1952,16 @@
|
|
|
1878
1952
|
"Type": "Core.Tag",
|
|
1879
1953
|
"AppliesTo": [
|
|
1880
1954
|
"Property",
|
|
1881
|
-
"Term"
|
|
1955
|
+
"Term",
|
|
1956
|
+
"TypeDefinition"
|
|
1882
1957
|
]
|
|
1883
1958
|
},
|
|
1884
1959
|
"UI.IsImage": {
|
|
1885
1960
|
"Type": "Core.Tag",
|
|
1886
1961
|
"AppliesTo": [
|
|
1887
1962
|
"Property",
|
|
1888
|
-
"EntityType"
|
|
1963
|
+
"EntityType",
|
|
1964
|
+
"TypeDefinition"
|
|
1889
1965
|
],
|
|
1890
1966
|
"$experimental": true
|
|
1891
1967
|
},
|
|
@@ -1894,7 +1970,8 @@
|
|
|
1894
1970
|
"AppliesTo": [
|
|
1895
1971
|
"Property",
|
|
1896
1972
|
"PropertyValue",
|
|
1897
|
-
"Parameter"
|
|
1973
|
+
"Parameter",
|
|
1974
|
+
"TypeDefinition"
|
|
1898
1975
|
]
|
|
1899
1976
|
},
|
|
1900
1977
|
"UI.Placeholder": {
|
|
@@ -2070,7 +2147,8 @@
|
|
|
2070
2147
|
"Type": "UI.RecommendationListType",
|
|
2071
2148
|
"AppliesTo": [
|
|
2072
2149
|
"Property",
|
|
2073
|
-
"Parameter"
|
|
2150
|
+
"Parameter",
|
|
2151
|
+
"TypeDefinition"
|
|
2074
2152
|
]
|
|
2075
2153
|
},
|
|
2076
2154
|
"UI.Recommendations": {
|
|
@@ -2089,7 +2167,8 @@
|
|
|
2089
2167
|
"UI.DoNotCheckScaleOfMeasuredQuantity": {
|
|
2090
2168
|
"Type": "Edm.Boolean",
|
|
2091
2169
|
"AppliesTo": [
|
|
2092
|
-
"Property"
|
|
2170
|
+
"Property",
|
|
2171
|
+
"TypeDefinition"
|
|
2093
2172
|
],
|
|
2094
2173
|
"$experimental": true
|
|
2095
2174
|
},
|
|
@@ -3301,6 +3380,7 @@
|
|
|
3301
3380
|
"Properties": {
|
|
3302
3381
|
"SourceProperties": "Collection(Edm.PropertyPath)",
|
|
3303
3382
|
"SourceEntities": "Collection(Edm.NavigationPropertyPath)",
|
|
3383
|
+
"SourceEvents": "Collection(Edm.String)",
|
|
3304
3384
|
"TargetProperties": "Collection(Edm.String)",
|
|
3305
3385
|
"TargetEntities": "Collection(Edm.NavigationPropertyPath)",
|
|
3306
3386
|
"EffectTypes": "Common.EffectType",
|
|
@@ -3777,6 +3857,105 @@
|
|
|
3777
3857
|
"DELETE"
|
|
3778
3858
|
]
|
|
3779
3859
|
},
|
|
3860
|
+
"EntityRelationship.singleReference": {
|
|
3861
|
+
"$kind": "ComplexType",
|
|
3862
|
+
"Properties": {
|
|
3863
|
+
"name": "Edm.String",
|
|
3864
|
+
"referencedEntityType": "EntityRelationship.entityTypeID",
|
|
3865
|
+
"referencedPropertyType": "EntityRelationship.propertyTypeID"
|
|
3866
|
+
}
|
|
3867
|
+
},
|
|
3868
|
+
"EntityRelationship.entityId": {
|
|
3869
|
+
"$kind": "ComplexType",
|
|
3870
|
+
"Properties": {
|
|
3871
|
+
"name": "Edm.String",
|
|
3872
|
+
"description": "Edm.String",
|
|
3873
|
+
"propertyTypes": "Collection(EntityRelationship.propertyTypeID)"
|
|
3874
|
+
}
|
|
3875
|
+
},
|
|
3876
|
+
"EntityRelationship.compositeReference": {
|
|
3877
|
+
"$kind": "ComplexType",
|
|
3878
|
+
"Properties": {
|
|
3879
|
+
"name": "Edm.String",
|
|
3880
|
+
"referencedEntityType": "EntityRelationship.entityTypeID",
|
|
3881
|
+
"referencedPropertyType": "Collection(EntityRelationship.referencedPropertyType)"
|
|
3882
|
+
}
|
|
3883
|
+
},
|
|
3884
|
+
"EntityRelationship.referencedPropertyType": {
|
|
3885
|
+
"$kind": "ComplexType",
|
|
3886
|
+
"Properties": {
|
|
3887
|
+
"referencedPropertyType": "EntityRelationship.propertyTypeID",
|
|
3888
|
+
"localPropertyName": "Edm.PropertyPath"
|
|
3889
|
+
}
|
|
3890
|
+
},
|
|
3891
|
+
"EntityRelationship.temporalId": {
|
|
3892
|
+
"$kind": "ComplexType",
|
|
3893
|
+
"Properties": {
|
|
3894
|
+
"name": "Edm.String",
|
|
3895
|
+
"description": "Edm.String",
|
|
3896
|
+
"propertyTypes": "Collection(EntityRelationship.propertyTypeID)",
|
|
3897
|
+
"temporalIntervalType": "EntityRelationship.temporalIntervalTypeEnum",
|
|
3898
|
+
"temporalType": "EntityRelationship.temporalTypeEnum",
|
|
3899
|
+
"temporalIntervalStartProperty": "Edm.PropertyPath",
|
|
3900
|
+
"temporalIntervalEndProperty": "Edm.PropertyPath"
|
|
3901
|
+
}
|
|
3902
|
+
},
|
|
3903
|
+
"EntityRelationship.temporalReference": {
|
|
3904
|
+
"$kind": "ComplexType",
|
|
3905
|
+
"Properties": {
|
|
3906
|
+
"name": "Edm.String",
|
|
3907
|
+
"referencedEntityType": "EntityRelationship.entityTypeID",
|
|
3908
|
+
"referencedPropertyType": "Collection(EntityRelationship.referencedPropertyType)",
|
|
3909
|
+
"category": "EntityRelationship.temporalCategoryEnum",
|
|
3910
|
+
"selectionDateProperty": "Edm.PropertyPath"
|
|
3911
|
+
}
|
|
3912
|
+
},
|
|
3913
|
+
"EntityRelationship.referenceWithConstantId": {
|
|
3914
|
+
"$kind": "ComplexType",
|
|
3915
|
+
"Properties": {
|
|
3916
|
+
"name": "Edm.String",
|
|
3917
|
+
"referencedEntityType": "EntityRelationship.entityTypeID",
|
|
3918
|
+
"referencedPropertyType": "Collection(EntityRelationship.referencedPropertyTypeWithConstantId)"
|
|
3919
|
+
}
|
|
3920
|
+
},
|
|
3921
|
+
"EntityRelationship.referencedPropertyTypeWithConstantId": {
|
|
3922
|
+
"$kind": "ComplexType",
|
|
3923
|
+
"Properties": {
|
|
3924
|
+
"referencedPropertyType": "EntityRelationship.propertyTypeID",
|
|
3925
|
+
"localPropertyName": "Edm.PropertyPath",
|
|
3926
|
+
"constantValue": "Edm.String"
|
|
3927
|
+
}
|
|
3928
|
+
},
|
|
3929
|
+
"EntityRelationship.temporalIntervalTypeEnum": {
|
|
3930
|
+
"$kind": "EnumType",
|
|
3931
|
+
"Members": [
|
|
3932
|
+
"CLOSED_CLOSED",
|
|
3933
|
+
"OPEN_OPEN",
|
|
3934
|
+
"OPEN_CLOSED",
|
|
3935
|
+
"CLOSED_OPEN"
|
|
3936
|
+
]
|
|
3937
|
+
},
|
|
3938
|
+
"EntityRelationship.temporalTypeEnum": {
|
|
3939
|
+
"$kind": "EnumType",
|
|
3940
|
+
"Members": [
|
|
3941
|
+
"DATE",
|
|
3942
|
+
"DATETIME"
|
|
3943
|
+
]
|
|
3944
|
+
},
|
|
3945
|
+
"EntityRelationship.temporalCategoryEnum": {
|
|
3946
|
+
"$kind": "EnumType",
|
|
3947
|
+
"Members": [
|
|
3948
|
+
"TEMPORAL_DATE"
|
|
3949
|
+
]
|
|
3950
|
+
},
|
|
3951
|
+
"EntityRelationship.propertyTypeID": {
|
|
3952
|
+
"$kind": "TypeDefinition",
|
|
3953
|
+
"UnderlyingType": "Edm.String"
|
|
3954
|
+
},
|
|
3955
|
+
"EntityRelationship.entityTypeID": {
|
|
3956
|
+
"$kind": "TypeDefinition",
|
|
3957
|
+
"UnderlyingType": "Edm.String"
|
|
3958
|
+
},
|
|
3780
3959
|
"Graph.DetailsType": {
|
|
3781
3960
|
"$kind": "ComplexType",
|
|
3782
3961
|
"Properties": {
|
|
@@ -40,6 +40,8 @@ const PRECEDENCE_OF_IN_PREDICATE = 10;
|
|
|
40
40
|
const PRECEDENCE_OF_EQUAL = 10;
|
|
41
41
|
|
|
42
42
|
class AstBuildingParser extends BaseParser {
|
|
43
|
+
leanConditions = { afterBrace: true };
|
|
44
|
+
|
|
43
45
|
constructor( lexer, keywords, table, options, messageFunctions ) {
|
|
44
46
|
super( lexer, keywords, table ); // lexer has file
|
|
45
47
|
this.options = options;
|
|
@@ -212,6 +214,16 @@ class AstBuildingParser extends BaseParser {
|
|
|
212
214
|
return next !== '*' && next !== '{';
|
|
213
215
|
}
|
|
214
216
|
|
|
217
|
+
notAfterEntityArgOrFilter( mode ) {
|
|
218
|
+
if (mode !== 'M')
|
|
219
|
+
return true;
|
|
220
|
+
const { type } = this.lb();
|
|
221
|
+
if (type !== ')' && type !== ']')
|
|
222
|
+
return true;
|
|
223
|
+
const { followState } = this.stack.at( -1 );
|
|
224
|
+
return !this.table[followState][':'];
|
|
225
|
+
}
|
|
226
|
+
|
|
215
227
|
// <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
|
|
216
228
|
// completion for `… default 3 not ~;` → currently just `null` but hey
|
|
217
229
|
isNegatedRelation( _test, prec ) {
|
|
@@ -253,6 +265,23 @@ class AstBuildingParser extends BaseParser {
|
|
|
253
265
|
return !this.dynamic_.inBlock;
|
|
254
266
|
}
|
|
255
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Restrictions according to the expression of a select column.
|
|
270
|
+
* Currently only to restrict it to a single `Id` for published associations.
|
|
271
|
+
*/
|
|
272
|
+
columnExpr( mode, arg ) {
|
|
273
|
+
if (mode)
|
|
274
|
+
return this.columnExpr$;
|
|
275
|
+
// TODO: should we use (text of) syntax-unexpected-assoc somewhere ?
|
|
276
|
+
if (arg)
|
|
277
|
+
this.columnExpr$ = this.tokenIdx;
|
|
278
|
+
else if (this.columnExpr$ !== this.tokenIdx - 1 ||
|
|
279
|
+
this.lb().type !== 'Id' ||
|
|
280
|
+
[ 'true', 'false', 'null' ].includes( this.lb().keyword ) )
|
|
281
|
+
this.columnExpr$ = null;
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
256
285
|
/**
|
|
257
286
|
* Prepare element restrictions and check validility of final anno assignments.
|
|
258
287
|
*
|
|
@@ -321,13 +350,36 @@ class AstBuildingParser extends BaseParser {
|
|
|
321
350
|
return true;
|
|
322
351
|
}
|
|
323
352
|
|
|
353
|
+
noRepeatedCardinality( mode ) {
|
|
354
|
+
if (this.tokens[this.tokenIdx - 2]?.type !== ']')
|
|
355
|
+
return true;
|
|
356
|
+
if (mode === 'M')
|
|
357
|
+
return false;
|
|
358
|
+
// currently just warning if same cardinality provided twice
|
|
359
|
+
const same = { one: '1', many: '*' }[this.la().keyword];
|
|
360
|
+
return this.tokens[this.tokenIdx - 3]?.text === same;
|
|
361
|
+
}
|
|
362
|
+
|
|
324
363
|
/**
|
|
325
364
|
* `;` between statements is optional only after a `}` (ex braces of structure
|
|
326
|
-
* values for annotations).
|
|
365
|
+
* values for annotations and foreign key specifications).
|
|
366
|
+
*
|
|
367
|
+
* Beware: mentioned in leanConditions, i.e. executed in predictions!
|
|
327
368
|
*/
|
|
328
369
|
afterBrace( test ) {
|
|
329
370
|
if (!test)
|
|
330
371
|
this.afterBrace$ = this.tokenIdx;
|
|
372
|
+
// TODO TOOL: the following test belongs to the BaseParser.js:
|
|
373
|
+
if (this.conditionTokenIdx === this.tokenIdx && // tested on same
|
|
374
|
+
this.conditionStackLength == null && // after error recover
|
|
375
|
+
test !== 'M')
|
|
376
|
+
return true;
|
|
377
|
+
// Strange optional `;` after PROJECTION ON source: the rule exit prediction
|
|
378
|
+
// for fromRefWithOptAlias etc now checks C(afterBrace):
|
|
379
|
+
if (test === 'E' && this.afterBrace$ > 0 &&
|
|
380
|
+
this.tokens[this.afterBrace$ - 1]?.keyword === 'projection' &&
|
|
381
|
+
this.tokens[this.afterBrace$].keyword === 'on')
|
|
382
|
+
return true;
|
|
331
383
|
return this.afterBrace$ === this.tokenIdx;
|
|
332
384
|
}
|
|
333
385
|
|
|
@@ -838,7 +890,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
838
890
|
// TODO: `literal` needed?
|
|
839
891
|
if (art.cardinality) {
|
|
840
892
|
this.reportDuplicateClause( 'cardinality', targetMax, art.cardinality.targetMax,
|
|
841
|
-
card.keyword
|
|
893
|
+
card.keyword );
|
|
842
894
|
}
|
|
843
895
|
else {
|
|
844
896
|
art.cardinality = { targetMax, location: targetMax.location };
|
|
@@ -846,9 +898,9 @@ class AstBuildingParser extends BaseParser {
|
|
|
846
898
|
return target;
|
|
847
899
|
}
|
|
848
900
|
|
|
901
|
+
// TODO: as condition
|
|
849
902
|
reportExpandInline( column, isInline ) {
|
|
850
903
|
// called before matching `{`
|
|
851
|
-
const { name } = column;
|
|
852
904
|
if (column.value && !column.value.path) {
|
|
853
905
|
// improve error location when using "inline" `.{…}` after ref (arguments and
|
|
854
906
|
// filters not covered, not worth the effort); after an expression where
|
|
@@ -864,18 +916,9 @@ class AstBuildingParser extends BaseParser {
|
|
|
864
916
|
// - no errors for refs inside expand/inline, but for refs in sibling expr
|
|
865
917
|
// - think about: reference to these (sub) elements from other view
|
|
866
918
|
}
|
|
867
|
-
if (isInline && name) {
|
|
868
|
-
const alias = this.tokens[this.tokenIdx - 2];
|
|
869
|
-
const location = (isInline === true)
|
|
870
|
-
? alias.location
|
|
871
|
-
: this.combineLocation( isInline, alias );
|
|
872
|
-
this.error( 'syntax-unexpected-alias', location, { code: '.{ ‹inline› }' },
|
|
873
|
-
'Unexpected alias name before $(CODE)' );
|
|
874
|
-
// continuation semantics: ignore AS
|
|
875
|
-
}
|
|
876
919
|
}
|
|
877
920
|
|
|
878
|
-
reportDuplicateClause( prop, erroneous, chosen, code
|
|
921
|
+
reportDuplicateClause( prop, erroneous, chosen, code ) {
|
|
879
922
|
// probably easier for message linters not to use (?:) for the message id...?
|
|
880
923
|
const args = {
|
|
881
924
|
'#': prop,
|
|
@@ -887,11 +930,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
887
930
|
// TODO v6: duplicate clause = error, independently whether it is the same
|
|
888
931
|
this.warning( 'syntax-duplicate-equal-clause', erroneous.location, args );
|
|
889
932
|
}
|
|
890
|
-
|
|
891
|
-
if (literalValIfNotEq)
|
|
892
|
-
args.code = chosen.val;
|
|
893
|
-
this.message( 'syntax-duplicate-clause', erroneous.location, args );
|
|
894
|
-
}
|
|
933
|
+
// TODO extra msg text 'syntax-duplicate-clause' for noRepeatedCardinality()
|
|
895
934
|
}
|
|
896
935
|
|
|
897
936
|
setTypeFacet( art, name, value ) {
|
|
@@ -1090,20 +1129,16 @@ class AstBuildingParser extends BaseParser {
|
|
|
1090
1129
|
};
|
|
1091
1130
|
}
|
|
1092
1131
|
|
|
1132
|
+
// TODO: as condition
|
|
1093
1133
|
associationInSelectItem( art ) {
|
|
1134
|
+
if (art.name)
|
|
1135
|
+
return;
|
|
1094
1136
|
this.classifyImplicitName( 'ItemAssoc', art.value );
|
|
1095
1137
|
const path = art.value?.path;
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
if (path.length === 1 && !name.args && !name.cardinality && !name.where) {
|
|
1100
|
-
art.name = name;
|
|
1101
|
-
delete art.value;
|
|
1102
|
-
return;
|
|
1103
|
-
}
|
|
1138
|
+
if (path?.length) {
|
|
1139
|
+
art.name = path.at( -1 ); // usually length 1, but make it also work during error recovery
|
|
1140
|
+
delete art.value;
|
|
1104
1141
|
}
|
|
1105
|
-
this.error( 'syntax-unexpected-assoc', this.la(), {},
|
|
1106
|
-
'Unexpected association definition in select item' );
|
|
1107
1142
|
}
|
|
1108
1143
|
|
|
1109
1144
|
// must be in action directly after having parsed '{', '(`, or a keyword before
|
|
@@ -507,7 +507,8 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
507
507
|
( assoc=ASSOCIATION cardinality[ $art ]? TO
|
|
508
508
|
| assoc=COMPOSITION cardinality[ $art ]? OF
|
|
509
509
|
)
|
|
510
|
-
card=ONE/MANY?
|
|
510
|
+
( <cond=noRepeatedCardinality> card=ONE/MANY )?
|
|
511
|
+
target=simplePath
|
|
511
512
|
{ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
512
513
|
ON expr=condition { $art.on = $expr; }
|
|
513
514
|
;
|
|
@@ -866,11 +867,13 @@ typeExpression[ art ]
|
|
|
866
867
|
typeProperties[ $art ]?
|
|
867
868
|
|
|
|
868
869
|
assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
|
|
869
|
-
cardinality[ $art ]? TO
|
|
870
|
+
cardinality[ $art ]? TO
|
|
871
|
+
( <cond=noRepeatedCardinality> card=ONE/MANY )?
|
|
870
872
|
typeAssocProperties[ $art, $assoc, $card ]
|
|
871
873
|
|
|
|
872
874
|
assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
|
|
873
|
-
cardinality[ $art ]? OF
|
|
875
|
+
cardinality[ $art ]? OF
|
|
876
|
+
( <cond=noRepeatedCardinality> card=ONE/MANY )?
|
|
874
877
|
( typeAssocProperties[ $art, $assoc, $card ]
|
|
875
878
|
| elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
|
|
876
879
|
{ $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
|
|
@@ -989,6 +992,7 @@ typeNamedArgsList[ art ]
|
|
|
989
992
|
|
|
990
993
|
typeNamedArg[ art ]
|
|
991
994
|
:
|
|
995
|
+
// TODO: or keywords with guards for better code completion?
|
|
992
996
|
name=Id['typeparamname']
|
|
993
997
|
':'
|
|
994
998
|
( Number
|
|
@@ -1055,12 +1059,11 @@ projectionSpec returns[ default query = {} ]
|
|
|
1055
1059
|
@finally{ this.attachLocation($query); }
|
|
1056
1060
|
:
|
|
1057
1061
|
// TODO, currently just with simple ref
|
|
1058
|
-
PROJECTION
|
|
1062
|
+
PROJECTION <prepare=afterBrace> // v6: remove <prepare>
|
|
1063
|
+
{ $query = { op: this.valueWithLocation( 'SELECT' ) }; }
|
|
1059
1064
|
ON
|
|
1060
|
-
tab=fromRefWithOptAlias
|
|
1065
|
+
tab=fromRefWithOptAlias <prepare=afterBrace>
|
|
1061
1066
|
// TODO: this <prepare=afterBrace> is extremely strange... v6 forbid.
|
|
1062
|
-
// Deliberately set this via action (→ interpreter will not accept this)
|
|
1063
|
-
{ this.afterBrace(); }<always>
|
|
1064
1067
|
{ $query.from = tab; }
|
|
1065
1068
|
selectItemsList[ $query ]?
|
|
1066
1069
|
excludingClause[ $query ]?
|
|
@@ -1225,8 +1228,7 @@ fromRefWithOptAlias returns[ default expr = {} ]
|
|
|
1225
1228
|
(
|
|
1226
1229
|
AS Id['FromAlias'] { $expr.name = this.identAst(); }
|
|
1227
1230
|
|
|
|
1228
|
-
<cond=tableWithoutAs>
|
|
1229
|
-
// TODO: probably not necessary, TOOL already uses `default: this.giR()`
|
|
1231
|
+
// <cond=tableWithoutAs> not necessary, tool uses `default: this.giR()`
|
|
1230
1232
|
Id_restricted['FromAlias']
|
|
1231
1233
|
{ $expr.name = this.fragileAlias(); }
|
|
1232
1234
|
|
|
|
@@ -1241,6 +1243,7 @@ fromPath[ table, category ] locals[ pathItem ]
|
|
|
1241
1243
|
Id[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
|
|
1242
1244
|
( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
|
|
1243
1245
|
(
|
|
1246
|
+
<cond=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
|
|
1244
1247
|
'.' { if (!$pathItem && !$table.scope) {
|
|
1245
1248
|
$table.scope = $table.path.length; $category = 'ref';
|
|
1246
1249
|
this.warning( 'syntax-invalid-path-separator', this.lb(),
|
|
@@ -1356,24 +1359,24 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
|
|
|
1356
1359
|
{ $columns.push( $art ); } // TODO: probably too early
|
|
1357
1360
|
{ this.docComment( $art ); } annoAssignCol[ $art ]*
|
|
1358
1361
|
( <cond=modifierRestriction> VIRTUAL
|
|
1359
|
-
{ $art.virtual = this.valueWithLocation( true ); }
|
|
1362
|
+
{ $art.virtual = this.valueWithLocation( true ); }
|
|
1363
|
+
)?
|
|
1364
|
+
{;} <prepare=columnExpr, arg=key> // TOOL TODO: disappears without {;}
|
|
1360
1365
|
( <cond=modifierRestriction> KEY
|
|
1361
|
-
{ $art.key = this.valueWithLocation( true ); }
|
|
1366
|
+
{ $art.key = this.valueWithLocation( true ); }
|
|
1367
|
+
)?
|
|
1362
1368
|
(
|
|
1363
1369
|
expr=expression { $art.value = $expr; }
|
|
1364
1370
|
( as=AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1365
1371
|
| Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
|
|
1366
1372
|
| { $alias = this.classifyImplicitName( 'ItemImplicit', $expr ); }
|
|
1367
|
-
)
|
|
1368
|
-
// TODO: <cond> instead `reportExpandInline` for "expand/inline only w/ ref"
|
|
1369
|
-
(
|
|
1370
|
-
{ this.reportExpandInline( $art, false ); }
|
|
1371
|
-
nestedSelectItemsList[ $art, 'expand' ]
|
|
1372
|
-
excludingClause[ $art ]?
|
|
1373
1373
|
|
|
|
1374
|
+
// TODO: guard for ref-only expression can probably replace reportExpandInline
|
|
1374
1375
|
'.'
|
|
1375
1376
|
{ this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
|
|
1376
1377
|
{ this.reportExpandInline( $art, $as || true ); }
|
|
1378
|
+
// no extra 'syntax-unexpected-alias' anymore,
|
|
1379
|
+
// 'syntax-unexpected-anno' reported in define.js
|
|
1377
1380
|
{ if ($alias) $alias.token.parsedAs = $alias.parsedAs; }
|
|
1378
1381
|
(
|
|
1379
1382
|
nestedSelectItemsList[ $art, 'inline' ]
|
|
@@ -1381,13 +1384,21 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
|
|
|
1381
1384
|
|
|
|
1382
1385
|
'*' { $art.inline = [ this.valueWithLocation() ]; }
|
|
1383
1386
|
)
|
|
1387
|
+
<exitRule>
|
|
1388
|
+
)
|
|
1389
|
+
// TODO: <cond> instead `reportExpandInline` for "expand/inline only w/ ref"
|
|
1390
|
+
(
|
|
1391
|
+
// TODO: guard for ref-only expression can probably replace:
|
|
1392
|
+
{ this.reportExpandInline( $art, false ); }
|
|
1393
|
+
nestedSelectItemsList[ $art, 'expand' ]
|
|
1394
|
+
excludingClause[ $art ]?
|
|
1384
1395
|
)?
|
|
1385
1396
|
|
|
|
1386
1397
|
nestedSelectItemsList[ $art, 'expand' ]
|
|
1387
1398
|
excludingClause[ $art ]?
|
|
1388
1399
|
AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1389
1400
|
)
|
|
1390
|
-
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
1401
|
+
{ this.docComment( $art ); } <prepare=columnExpr> annoAssignMid[ $art ]*
|
|
1391
1402
|
(
|
|
1392
1403
|
':'
|
|
1393
1404
|
(
|
|
@@ -1396,18 +1407,21 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
|
|
|
1396
1407
|
typeRefOptArgs[ $art ]
|
|
1397
1408
|
)
|
|
1398
1409
|
|
|
|
1410
|
+
// TODO: guard for ref-only expression ?
|
|
1399
1411
|
REDIRECTED TO target=simplePath { $art.target = $target; }
|
|
1400
1412
|
( ON cond=condition { $art.on = $cond; }
|
|
1401
1413
|
| foreignKeysBlock[ $art ]
|
|
1402
1414
|
)?
|
|
1403
1415
|
|
|
|
1404
|
-
//
|
|
1405
|
-
|
|
1416
|
+
( <cond=columnExpr> // arg=singleId
|
|
1417
|
+
assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
|
|
1406
1418
|
cardinality[ $art ]? TO
|
|
1407
|
-
|
|
|
1419
|
+
| <cond=columnExpr> // arg=singleId
|
|
1420
|
+
assoc=COMPOSITION { this.associationInSelectItem( $art ); }
|
|
1408
1421
|
cardinality[ $art ]? OF
|
|
1409
1422
|
)
|
|
1410
|
-
card=ONE/MANY?
|
|
1423
|
+
( <cond=noRepeatedCardinality> card=ONE/MANY )?
|
|
1424
|
+
target=simplePath
|
|
1411
1425
|
{ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
1412
1426
|
ON expr=condition { $art.on = $expr; }
|
|
1413
1427
|
)
|
|
@@ -652,6 +652,9 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
652
652
|
if (typeof root.$env === 'string' && (isComplexOrNestedQuery || options.transformation !== 'odata' || root.$env === pathId(obj.ref[0])))
|
|
653
653
|
obj.ref = [ root.$env, ...obj.ref ];
|
|
654
654
|
|
|
655
|
+
if (iterateOptions.keepKeysOrigin)
|
|
656
|
+
setProp(obj, '$originalKeyRef', { ref: root.ref, as: root.as });
|
|
657
|
+
|
|
655
658
|
return obj;
|
|
656
659
|
});
|
|
657
660
|
}
|
|
@@ -188,7 +188,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
188
188
|
if (!structuredOData) {
|
|
189
189
|
expansion.expandStructureReferences(csn, options, '_',
|
|
190
190
|
{ error, info, throwWithAnyError }, csnUtils,
|
|
191
|
-
{ skipArtifact: isExternalServiceMember });
|
|
191
|
+
{ skipArtifact: isExternalServiceMember, keepKeysOrigin: true });
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
createForeignKeyElements(csn, options, messageFunctions, csnUtils, { skipArtifact: isExternalServiceMember });
|