@sap/cds-compiler 5.7.4 → 5.8.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 +54 -2
- package/bin/cdsse.js +13 -1
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/options.js +2 -1
- package/lib/api/validate.js +9 -0
- package/lib/base/message-registry.js +55 -20
- package/lib/base/messages.js +5 -2
- package/lib/base/model.js +4 -1
- package/lib/checks/assocOutsideService.js +40 -0
- package/lib/checks/featureFlags.js +4 -1
- package/lib/checks/types.js +7 -4
- package/lib/checks/validator.js +3 -0
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/checks.js +79 -17
- package/lib/compiler/define.js +57 -3
- package/lib/compiler/extend.js +1 -2
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/populate.js +17 -6
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +181 -150
- package/lib/compiler/shared.js +276 -22
- package/lib/compiler/tweak-assocs.js +15 -4
- package/lib/compiler/xpr-rewrite.js +76 -50
- package/lib/edm/annotations/edmJson.js +1 -1
- package/lib/edm/annotations/genericTranslation.js +2 -2
- package/lib/edm/csn2edm.js +2 -2
- package/lib/edm/edmPreprocessor.js +15 -9
- package/lib/edm/edmUtils.js +12 -5
- package/lib/gen/CdlGrammar.checksum +1 -0
- package/lib/gen/CdlParser.js +2234 -2233
- package/lib/gen/Dictionary.json +55 -8
- package/lib/json/from-csn.js +37 -17
- package/lib/json/to-csn.js +4 -0
- package/lib/language/genericAntlrParser.js +7 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +0 -5
- package/lib/modelCompare/utils/filter.js +2 -2
- package/lib/optionProcessor.js +2 -0
- package/lib/parsers/AstBuildingParser.js +47 -17
- package/lib/parsers/CdlGrammar.g4 +10 -12
- package/lib/parsers/XprTree.js +206 -0
- package/lib/render/toCdl.js +61 -89
- package/lib/render/toSql.js +59 -29
- package/lib/render/utils/standardDatabaseFunctions.js +252 -15
- package/lib/transform/addTenantFields.js +9 -3
- package/lib/transform/db/assocsToQueries/transformExists.js +3 -0
- package/lib/transform/db/assocsToQueries/utils.js +10 -3
- package/lib/transform/db/expansion.js +3 -1
- package/lib/transform/db/flattening.js +7 -3
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +70 -17
- package/lib/transform/draft/db.js +8 -3
- package/lib/transform/draft/odata.js +27 -4
- package/lib/transform/effective/main.js +37 -10
- package/lib/transform/effective/misc.js +4 -9
- package/lib/transform/effective/service.js +34 -0
- package/lib/transform/effective/types.js +28 -17
- package/lib/transform/forOdata.js +36 -10
- package/lib/transform/forRelationalDB.js +30 -18
- package/lib/transform/odata/adaptAnnotationRefs.js +37 -21
- package/lib/transform/odata/createForeignKeys.js +120 -116
- package/lib/transform/odata/flattening.js +10 -8
- package/lib/transform/transformUtils.js +58 -25
- package/lib/transform/translateAssocsToJoins.js +10 -6
- package/lib/transform/universalCsn/coreComputed.js +5 -1
- package/package.json +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/rewrite-not-supported.md +5 -0
- package/share/messages/rewrite-undefined-key.md +94 -0
package/lib/gen/Dictionary.json
CHANGED
|
@@ -132,6 +132,13 @@
|
|
|
132
132
|
"Collection"
|
|
133
133
|
]
|
|
134
134
|
},
|
|
135
|
+
"Analytics.LevelInformation": {
|
|
136
|
+
"Type": "Hierarchy.HierarchyType",
|
|
137
|
+
"AppliesTo": [
|
|
138
|
+
"EntityType"
|
|
139
|
+
],
|
|
140
|
+
"$experimental": true
|
|
141
|
+
},
|
|
135
142
|
"Authorization.SecuritySchemes": {
|
|
136
143
|
"Type": "Collection(Authorization.SecurityScheme)",
|
|
137
144
|
"AppliesTo": [
|
|
@@ -1121,6 +1128,13 @@
|
|
|
1121
1128
|
],
|
|
1122
1129
|
"$experimental": true
|
|
1123
1130
|
},
|
|
1131
|
+
"Common.AddressViaNavigationPath": {
|
|
1132
|
+
"Type": "Core.Tag",
|
|
1133
|
+
"AppliesTo": [
|
|
1134
|
+
"EntityContainer"
|
|
1135
|
+
],
|
|
1136
|
+
"$experimental": true
|
|
1137
|
+
},
|
|
1124
1138
|
"Communication.Contact": {
|
|
1125
1139
|
"Type": "Communication.ContactType",
|
|
1126
1140
|
"AppliesTo": [
|
|
@@ -1519,7 +1533,7 @@
|
|
|
1519
1533
|
"$experimental": true
|
|
1520
1534
|
},
|
|
1521
1535
|
"EntityRelationship.referencesWithConstantIds": {
|
|
1522
|
-
"Type": "Collection(EntityRelationship.
|
|
1536
|
+
"Type": "Collection(EntityRelationship.referenceWithConstantId)",
|
|
1523
1537
|
"AppliesTo": [
|
|
1524
1538
|
"EntityType"
|
|
1525
1539
|
],
|
|
@@ -1575,8 +1589,7 @@
|
|
|
1575
1589
|
]
|
|
1576
1590
|
},
|
|
1577
1591
|
"HTML5.LinkTarget": {
|
|
1578
|
-
"Type": "HTML5.LinkTargetType"
|
|
1579
|
-
"$experimental": true
|
|
1592
|
+
"Type": "HTML5.LinkTargetType"
|
|
1580
1593
|
},
|
|
1581
1594
|
"HTML5.RowSpanForDuplicateValues": {
|
|
1582
1595
|
"Type": "Core.Tag",
|
|
@@ -1894,8 +1907,7 @@
|
|
|
1894
1907
|
"Function",
|
|
1895
1908
|
"ActionImport",
|
|
1896
1909
|
"FunctionImport"
|
|
1897
|
-
]
|
|
1898
|
-
"$experimental": true
|
|
1910
|
+
]
|
|
1899
1911
|
},
|
|
1900
1912
|
"UI.SelectionPresentationVariant": {
|
|
1901
1913
|
"Type": "UI.SelectionPresentationVariantType",
|
|
@@ -2525,6 +2537,30 @@
|
|
|
2525
2537
|
"AccumulativeMeasure": "Core.Tag"
|
|
2526
2538
|
}
|
|
2527
2539
|
},
|
|
2540
|
+
"Analytics.MultiLevelExpandLevel": {
|
|
2541
|
+
"$kind": "ComplexType",
|
|
2542
|
+
"Properties": {
|
|
2543
|
+
"DimensionProperties": "Collection(Edm.String)",
|
|
2544
|
+
"AdditionalProperties": "Collection(Edm.String)"
|
|
2545
|
+
},
|
|
2546
|
+
"$experimental": true
|
|
2547
|
+
},
|
|
2548
|
+
"Analytics.MultiLevelExpandSiblingOrder": {
|
|
2549
|
+
"$kind": "ComplexType",
|
|
2550
|
+
"Properties": {
|
|
2551
|
+
"Property": "Edm.String",
|
|
2552
|
+
"Descending": "Edm.Boolean"
|
|
2553
|
+
},
|
|
2554
|
+
"$experimental": true
|
|
2555
|
+
},
|
|
2556
|
+
"Analytics.MultiLevelExpandEntry": {
|
|
2557
|
+
"$kind": "ComplexType",
|
|
2558
|
+
"Properties": {
|
|
2559
|
+
"Entry": "Collection(Edm.String)",
|
|
2560
|
+
"Levels": "Edm.Int64"
|
|
2561
|
+
},
|
|
2562
|
+
"$experimental": true
|
|
2563
|
+
},
|
|
2528
2564
|
"Authorization.SecurityScheme": {
|
|
2529
2565
|
"$kind": "ComplexType",
|
|
2530
2566
|
"Properties": {
|
|
@@ -3964,16 +4000,26 @@
|
|
|
3964
4000
|
},
|
|
3965
4001
|
"$experimental": true
|
|
3966
4002
|
},
|
|
4003
|
+
"Hierarchy.HierarchyType": {
|
|
4004
|
+
"$kind": "ComplexType",
|
|
4005
|
+
"Properties": {
|
|
4006
|
+
"LimitedDescendantCount": "Edm.Int64",
|
|
4007
|
+
"DrillState": "Edm.String",
|
|
4008
|
+
"DistanceFromRoot": "Edm.Int64"
|
|
4009
|
+
},
|
|
4010
|
+
"$experimental": true
|
|
4011
|
+
},
|
|
3967
4012
|
"Hierarchy.RecursiveHierarchyType": {
|
|
3968
4013
|
"$kind": "ComplexType",
|
|
4014
|
+
"BaseType": "Hierarchy.HierarchyType",
|
|
3969
4015
|
"Properties": {
|
|
3970
4016
|
"ExternalKey": "Edm.String",
|
|
3971
4017
|
"NodeType": "Edm.String",
|
|
3972
|
-
"ChildCount": "Edm.Int64",
|
|
3973
|
-
"DescendantCount": "Edm.Int64",
|
|
3974
4018
|
"LimitedDescendantCount": "Edm.Int64",
|
|
3975
4019
|
"DrillState": "Edm.String",
|
|
3976
4020
|
"DistanceFromRoot": "Edm.Int64",
|
|
4021
|
+
"ChildCount": "Edm.Int64",
|
|
4022
|
+
"DescendantCount": "Edm.Int64",
|
|
3977
4023
|
"LimitedRank": "Edm.Int64",
|
|
3978
4024
|
"SiblingRank": "Edm.Int64",
|
|
3979
4025
|
"Matched": "Edm.Boolean",
|
|
@@ -3987,7 +4033,8 @@
|
|
|
3987
4033
|
"ChangeNextSiblingAction": "Common.QualifiedName",
|
|
3988
4034
|
"ChangeSiblingForRootsSupported": "Edm.Boolean",
|
|
3989
4035
|
"CopyAction": "Common.QualifiedName"
|
|
3990
|
-
}
|
|
4036
|
+
},
|
|
4037
|
+
"$experimental": true
|
|
3991
4038
|
},
|
|
3992
4039
|
"Hierarchy.TopLevelsExpandType": {
|
|
3993
4040
|
"$kind": "ComplexType",
|
package/lib/json/from-csn.js
CHANGED
|
@@ -124,6 +124,7 @@ const { isAnnotationExpression } = require('../base/builtins');
|
|
|
124
124
|
const { CompilerAssertion } = require('../base/error');
|
|
125
125
|
const { Location } = require('../base/location');
|
|
126
126
|
const { XsnSource } = require('../compiler/xsn-model');
|
|
127
|
+
const { xprAsTree } = require('../parsers/XprTree');
|
|
127
128
|
|
|
128
129
|
const $location = Symbol.for('cds.$location');
|
|
129
130
|
|
|
@@ -529,7 +530,7 @@ const schema = compileSchema( {
|
|
|
529
530
|
'from', 'all', 'distinct', 'columns', 'excluding', // no 'mixin'
|
|
530
531
|
'where', 'groupBy', 'having', 'orderBy', 'limit',
|
|
531
532
|
],
|
|
532
|
-
inKind: [ 'entity', 'event' ],
|
|
533
|
+
inKind: [ 'entity', 'event', 'type' ],
|
|
533
534
|
},
|
|
534
535
|
SELECT: {
|
|
535
536
|
type: queryTerm,
|
|
@@ -664,7 +665,7 @@ const schema = compileSchema( {
|
|
|
664
665
|
],
|
|
665
666
|
schema: {
|
|
666
667
|
'=': {
|
|
667
|
-
type: renameTo( '$tokenTexts',
|
|
668
|
+
type: renameTo( '$tokenTexts', stringOrBool ),
|
|
668
669
|
xorGroups: null, // reset xorGroup; allow '=' for all :expr
|
|
669
670
|
},
|
|
670
671
|
},
|
|
@@ -698,7 +699,8 @@ const schema = compileSchema( {
|
|
|
698
699
|
// 2. Inside "xpr" => inside expressions
|
|
699
700
|
// Because of (1) we have to set this property to false.
|
|
700
701
|
inValue: false,
|
|
701
|
-
optional: typeProperties,
|
|
702
|
+
optional: typeProperties,
|
|
703
|
+
// TODO v6: only in CDL-style cast, otherwise just length,…
|
|
702
704
|
inKind: [ '$column' ],
|
|
703
705
|
},
|
|
704
706
|
default: {
|
|
@@ -762,6 +764,9 @@ const schema = compileSchema( {
|
|
|
762
764
|
options: { // deprecated top-level property
|
|
763
765
|
type: ignore,
|
|
764
766
|
},
|
|
767
|
+
csnInteropEffective: {
|
|
768
|
+
type: ignore, // by https://github.com/SAP/csn-interop-specification
|
|
769
|
+
},
|
|
765
770
|
indexNo: { // CSN v0.1.0, but ignored without message
|
|
766
771
|
ignore: true, type: ignore,
|
|
767
772
|
},
|
|
@@ -787,6 +792,7 @@ const topLevelSpec = {
|
|
|
787
792
|
optional: [
|
|
788
793
|
'requires', 'definitions', 'vocabularies', 'extensions', 'i18n',
|
|
789
794
|
'namespace', 'version', 'messages', 'meta', 'options', '@', '$location',
|
|
795
|
+
'csnInteropEffective',
|
|
790
796
|
],
|
|
791
797
|
requires: false, // empty object OK
|
|
792
798
|
schema,
|
|
@@ -1316,6 +1322,14 @@ function string( val, spec ) {
|
|
|
1316
1322
|
return ignore( val );
|
|
1317
1323
|
}
|
|
1318
1324
|
|
|
1325
|
+
function stringOrBool( val, spec ) {
|
|
1326
|
+
if (typeof val === 'string' && val || typeof val === 'boolean')
|
|
1327
|
+
return val;
|
|
1328
|
+
error( 'syntax-expecting-string', location(true),
|
|
1329
|
+
{ '#': spec.msgVariant || 'or-bool', prop: spec.msgProp } );
|
|
1330
|
+
return ignore( val );
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1319
1333
|
function stringVal( val, spec ) {
|
|
1320
1334
|
if (typeof val === 'string' && val)
|
|
1321
1335
|
// XSN TODO: do not require literal
|
|
@@ -1440,12 +1454,12 @@ function annoValue( val, spec ) {
|
|
|
1440
1454
|
}
|
|
1441
1455
|
return retval;
|
|
1442
1456
|
}
|
|
1443
|
-
else if (typeof val['='] === 'string') {
|
|
1457
|
+
else if (typeof val['='] === 'string' || val['='] === true) {
|
|
1444
1458
|
// An object with `=` is an expression if and only if:
|
|
1445
1459
|
// - there is exactly one property ('=')
|
|
1446
1460
|
// - there is at least one other expression property (e.g. "xpr")
|
|
1447
1461
|
const valKeys = Object.keys( val );
|
|
1448
|
-
if (valKeys.length === 1) {
|
|
1462
|
+
if (valKeys.length === 1 && typeof val['='] === 'string') {
|
|
1449
1463
|
++virtualLine;
|
|
1450
1464
|
const r = refSplit( val['='], '=' ); // i.e. no extra `variant` stuff
|
|
1451
1465
|
++virtualLine;
|
|
@@ -1566,12 +1580,16 @@ function xpr( exprs, spec, xsn, csn ) {
|
|
|
1566
1580
|
{ prop: 'xpr', siblingprop: 'func', '#': 'suffix' });
|
|
1567
1581
|
}
|
|
1568
1582
|
xsn.suffix = exprArgs( exprs, spec );
|
|
1583
|
+
if (exprs.length > 2)
|
|
1584
|
+
xsn.suffix = xprAsTree( exprs, xsn.suffix, xsn.op.location ).args;
|
|
1569
1585
|
}
|
|
1570
1586
|
else {
|
|
1571
1587
|
// setting $parens here would not always be correct; thus, keep distinction
|
|
1572
1588
|
// between 'xpr' and 'ixpr' (”internal” `xpr` = without implicit parens)
|
|
1573
1589
|
xsn.op = { val: 'xpr', location: location() };
|
|
1574
1590
|
xsn.args = exprArgs( exprs, spec );
|
|
1591
|
+
if (exprs.length > 2)
|
|
1592
|
+
xsn.args = xprAsTree( exprs, xsn.args, xsn.op.location ).args;
|
|
1575
1593
|
}
|
|
1576
1594
|
}
|
|
1577
1595
|
|
|
@@ -1620,11 +1638,10 @@ function expr( e, spec ) {
|
|
|
1620
1638
|
if (Array.isArray( e )) {
|
|
1621
1639
|
if (e.length > 1) { // struct-xpr
|
|
1622
1640
|
const loc = location();
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
};
|
|
1641
|
+
const xsn = exprArgs( e, spec );
|
|
1642
|
+
return (e.length < 3) // optimization
|
|
1643
|
+
? { op: { val: 'ixpr', location: loc }, args: xsn, location: loc }
|
|
1644
|
+
: xprAsTree( e, xsn, loc );
|
|
1628
1645
|
}
|
|
1629
1646
|
else if (e.length === 1) { // CSN v.0.1.0 way for parentheses
|
|
1630
1647
|
const loc = location();
|
|
@@ -1657,14 +1674,14 @@ function exprOrString( val, spec ) {
|
|
|
1657
1674
|
: expr( val, spec );
|
|
1658
1675
|
}
|
|
1659
1676
|
|
|
1660
|
-
// mark path argument of '
|
|
1677
|
+
// mark path argument of 'exists' predicate with $expected:'exists'
|
|
1661
1678
|
function exprArgs( cond, spec ) {
|
|
1662
1679
|
const rxsn = arrayOf( exprOrString )( cond, spec );
|
|
1663
1680
|
// TODO: do that in definer.js, neither here nor in CDL parser
|
|
1664
1681
|
if (Array.isArray( rxsn )) {
|
|
1665
1682
|
for (let i = 0; i < rxsn.length - 1; i++) {
|
|
1666
1683
|
// TODO: disallow param ref - write test
|
|
1667
|
-
if (
|
|
1684
|
+
if (cond[i] === 'exists' && rxsn[i + 1].path)
|
|
1668
1685
|
rxsn[++i].$expected = 'exists';
|
|
1669
1686
|
}
|
|
1670
1687
|
}
|
|
@@ -1673,11 +1690,14 @@ function exprArgs( cond, spec ) {
|
|
|
1673
1690
|
|
|
1674
1691
|
function condition( cond, spec ) {
|
|
1675
1692
|
const loc = location();
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
};
|
|
1693
|
+
const xsn = exprArgs( cond, spec );
|
|
1694
|
+
// TODO sql-like backends: with the commented `return`, test3 generated sql
|
|
1695
|
+
// files will not have the unnecessary `(…)` around `on` anymore → extra PR
|
|
1696
|
+
const tree = (cond.length < 3) ? xsn : xprAsTree( cond, xsn, loc ).args;
|
|
1697
|
+
return { op: { val: 'xpr', location: loc }, args: tree, location: loc };
|
|
1698
|
+
// return (cond.length < 3) // optimization
|
|
1699
|
+
// ? { op: { val: 'ixpr', location: loc }, args: xsn, location: loc }
|
|
1700
|
+
// : xprAsTree( cond, xsn, loc );
|
|
1681
1701
|
}
|
|
1682
1702
|
|
|
1683
1703
|
// Queries (std signature) ---------------------------------------------------
|
package/lib/json/to-csn.js
CHANGED
|
@@ -1147,6 +1147,8 @@ function value( node ) {
|
|
|
1147
1147
|
}
|
|
1148
1148
|
|
|
1149
1149
|
function enumValue( node ) {
|
|
1150
|
+
if (node.val !== undefined) // with `val` via CSN input (e.g. recompilation)
|
|
1151
|
+
return extra( { '#': node.sym.id, val: node.val }, node );
|
|
1150
1152
|
const r = extra( { '#': node.sym.id }, node );
|
|
1151
1153
|
const sym = node.sym._artifact;
|
|
1152
1154
|
// add calculated `val`, but not for chained symbols:
|
|
@@ -1276,6 +1278,8 @@ function exprInternal( node, xprParens ) {
|
|
|
1276
1278
|
function flattenInternalXpr( array, op ) {
|
|
1277
1279
|
if (!structXpr)
|
|
1278
1280
|
return array.flat( Infinity );
|
|
1281
|
+
// TODO: do not rely on 'nary' - this dosn't work with CSN input have an
|
|
1282
|
+
// `xpr: [{val:1},'+',{val:2},'+',{val:3}]`.
|
|
1279
1283
|
if (array.length < 5 || op !== 'nary')
|
|
1280
1284
|
return array;
|
|
1281
1285
|
// nary: [ ‹a›, '+', ‹b›, '+', ‹c› ] → [ [ ‹a›, '+', ‹b› ], '+', ‹c› ]
|
|
@@ -853,6 +853,10 @@ function valuePathAst( ref ) {
|
|
|
853
853
|
const implicit = this.previousTokenAtLocation( location );
|
|
854
854
|
if (implicit && implicit.isIdentifier)
|
|
855
855
|
implicit.isIdentifier = 'func';
|
|
856
|
+
|
|
857
|
+
const filter = path[0].cardinality || path[0].where;
|
|
858
|
+
if (filter)
|
|
859
|
+
this.message( 'syntax-unexpected-filter', filter.location, {} );
|
|
856
860
|
const op = { location, val: 'call' };
|
|
857
861
|
return (args)
|
|
858
862
|
? {
|
|
@@ -881,6 +885,9 @@ function valuePathAst( ref ) {
|
|
|
881
885
|
}, 'References after function calls can\'t be resolved. Use $(CODE) in function arguments');
|
|
882
886
|
break;
|
|
883
887
|
}
|
|
888
|
+
const filter = path[i].cardinality || path[i].where;
|
|
889
|
+
if (filter)
|
|
890
|
+
this.message( 'syntax-unexpected-filter', filter.location, {} );
|
|
884
891
|
}
|
|
885
892
|
|
|
886
893
|
const args = [];
|
package/lib/main.d.ts
CHANGED
|
@@ -933,6 +933,11 @@ declare namespace compiler {
|
|
|
933
933
|
/**
|
|
934
934
|
* Renders the given CSN into a CDL source representation.
|
|
935
935
|
*
|
|
936
|
+
* The CDL string representation may change between minor @sap/cds-compiler
|
|
937
|
+
* versions, but when compiled, will return the same CSN again.
|
|
938
|
+
* Hence, stylistic changes such as additional whitespace is not considered
|
|
939
|
+
* a breaking change.
|
|
940
|
+
*
|
|
936
941
|
* @returns Object containing the rendered model.
|
|
937
942
|
*/
|
|
938
943
|
function cdl(csn: CSN, options?: CdlOptions): CdlResult;
|
package/lib/model/cloneCsn.js
CHANGED
|
@@ -38,6 +38,7 @@ const internalCsnProps = {
|
|
|
38
38
|
$default: shallowCopy, // used for HANA CSN migrations
|
|
39
39
|
$notNull: shallowCopy, // used for HANA CSN migrations
|
|
40
40
|
$sqlService: shallowCopy,
|
|
41
|
+
$dummyService: shallowCopy,
|
|
41
42
|
};
|
|
42
43
|
const internalEnumerableCsnProps = {
|
|
43
44
|
__proto__: null,
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -170,6 +170,7 @@
|
|
|
170
170
|
// any reference in the entity is inspected: with data for the query
|
|
171
171
|
// hierarchy, query number, table aliases and links from a column to its
|
|
172
172
|
// respective inferred element.
|
|
173
|
+
// - TODO: some `name` property would also be useful (set with `initDefinition`)
|
|
173
174
|
|
|
174
175
|
// Properties in cache:
|
|
175
176
|
//
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -1179,10 +1179,6 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1179
1179
|
}
|
|
1180
1180
|
}
|
|
1181
1181
|
|
|
1182
|
-
function isAspect( node ) {
|
|
1183
|
-
return node && node.kind === 'aspect';
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
1182
|
/**
|
|
1187
1183
|
* Return true if the artifact has a valid, truthy persistence.exists/skip annotation
|
|
1188
1184
|
*
|
|
@@ -1446,7 +1442,6 @@ module.exports = {
|
|
|
1446
1442
|
normalizeTypeRef,
|
|
1447
1443
|
copyAnnotations,
|
|
1448
1444
|
copyAnnotationsAndDoc,
|
|
1449
|
-
isAspect,
|
|
1450
1445
|
hasValidSkipOrExists,
|
|
1451
1446
|
getNamespace,
|
|
1452
1447
|
getServiceNames,
|
|
@@ -91,10 +91,10 @@ function getFilterObject( options, dialect, extensionCallback, migrationCallback
|
|
|
91
91
|
if (migration.new.type === migration.old.type && migration.new.length < migration.old.length) {
|
|
92
92
|
raiseErrorOrMarkAsLossy(name, migration, 'migration-unsupported-length-change', id => message(id, loc, { '#': messageVariant, id: name }));
|
|
93
93
|
}
|
|
94
|
-
else if (migration.new.type === migration.old.type && migration.new.scale
|
|
94
|
+
else if (migration.new.type === migration.old.type && migration.new.scale < migration.old.scale || migration.new.precision - migration.old.precision < migration.new.scale - migration.old.scale) {
|
|
95
95
|
raiseErrorOrMarkAsLossy(name, migration, 'migration-unsupported-scale-change', id => message(id, loc, { '#': messageVariant, id: name }));
|
|
96
96
|
}
|
|
97
|
-
else if (migration.new.type === migration.old.type && migration.new.precision
|
|
97
|
+
else if (migration.new.type === migration.old.type && migration.new.precision < migration.old.precision) {
|
|
98
98
|
raiseErrorOrMarkAsLossy(name, migration, 'migration-unsupported-precision-change', id => message(id, loc, { '#': messageVariant, id: name }));
|
|
99
99
|
}
|
|
100
100
|
else if (migration.new.type !== migration.old.type && typeChangeIsNotCompatible(dialect, migration.old.type, migration.new.type)) {
|
package/lib/optionProcessor.js
CHANGED
|
@@ -580,6 +580,7 @@ optionProcessor.command('forEffective')
|
|
|
580
580
|
.option('--resolve-projections <val>', { valid: ['true', 'false'] } )
|
|
581
581
|
.option('--remap-odata-annotations <val>', { valid: ['true', 'false'] } )
|
|
582
582
|
.option('--keep-localized <val>', { valid: ['true', 'false'] } )
|
|
583
|
+
.option('--effective-service-name <name>')
|
|
583
584
|
.positionalArgument('<files...>')
|
|
584
585
|
.help(`
|
|
585
586
|
Usage: cdsc forEffective [options] <files...>
|
|
@@ -602,6 +603,7 @@ optionProcessor.command('forEffective')
|
|
|
602
603
|
--keep-localized <val> Keep '.localized' property in the CSN:
|
|
603
604
|
true: property is kept
|
|
604
605
|
false:(default) property is deleted
|
|
606
|
+
--effective-service-name <name> Filter the output CSN to only contain the given service
|
|
605
607
|
`);
|
|
606
608
|
|
|
607
609
|
optionProcessor.command('forSeal')
|
|
@@ -48,7 +48,7 @@ const PRECEDENCE_OF_IN_PREDICATE = 10;
|
|
|
48
48
|
const PRECEDENCE_OF_EQUAL = 10;
|
|
49
49
|
|
|
50
50
|
class AstBuildingParser extends BaseParser {
|
|
51
|
-
leanConditions = { afterBrace: true, fail: true };
|
|
51
|
+
leanConditions = { afterBrace: true, atRightParen: true, fail: true };
|
|
52
52
|
|
|
53
53
|
constructor( lexer, keywords, table, options, messageFunctions ) {
|
|
54
54
|
super( lexer, keywords, table ); // lexer has file
|
|
@@ -341,7 +341,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
/**
|
|
344
|
-
* Prepare element restrictions and check
|
|
344
|
+
* Prepare element restrictions and check validity of final anno assignments.
|
|
345
345
|
*
|
|
346
346
|
* Called as <prepare=…>:
|
|
347
347
|
*
|
|
@@ -439,27 +439,49 @@ class AstBuildingParser extends BaseParser {
|
|
|
439
439
|
* `;` between statements is optional only after a `}` (ex braces of structure
|
|
440
440
|
* values for annotations and foreign key specifications).
|
|
441
441
|
*
|
|
442
|
+
* Unfortunate exception: always optional after `entity … as projection on`.
|
|
443
|
+
*
|
|
442
444
|
* Beware: mentioned in leanConditions, i.e. executed in predictions!
|
|
443
445
|
*/
|
|
444
446
|
afterBrace( test, arg ) {
|
|
445
447
|
if (!test) {
|
|
446
|
-
this.
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
448
|
+
if (arg === 'normal' && this.lb().type !== '}') {
|
|
449
|
+
const { type, keyword } = this.la();
|
|
450
|
+
if (type !== ';' && type !== '}' && type !== 'EOF' && keyword !== 'actions') {
|
|
451
|
+
const prev = this.lb().location;
|
|
452
|
+
const loc = new Location( prev.file, prev.endLine, prev.endCol );
|
|
453
|
+
this.warning( 'syntax-missing-proj-semicolon', loc,
|
|
454
|
+
{ expecting: [ "';'" ], offending: this.antlrName( this.la() ) },
|
|
455
|
+
'Missing $(EXPECTING) before $(OFFENDING)');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// with arg 'init' (used in rule `start`/`artifactsBlock`), and arg 'sloppy'
|
|
459
|
+
// or 'normal' (used in rule `entityDef`), set marker in dynamic context:
|
|
460
|
+
if (!arg)
|
|
461
|
+
this.afterBrace$ = this.tokenIdx;
|
|
462
|
+
else if (arg === 'init')
|
|
463
|
+
this.dynamic_.sloppySemicolon$ = [ false ];
|
|
464
|
+
else if (arg === 'sloppy' || this.la().keyword === 'actions')
|
|
465
|
+
this.dynamic_.sloppySemicolon$[0] = (arg === 'sloppy');
|
|
466
|
+
return null;
|
|
450
467
|
}
|
|
451
468
|
// TODO TOOL: the following test belongs to the BaseParser.js:
|
|
452
469
|
if (this.conditionTokenIdx === this.tokenIdx && // tested on same
|
|
453
470
|
this.conditionStackLength == null && // after error recover
|
|
454
471
|
test !== 'M')
|
|
455
472
|
return false;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
473
|
+
const { sloppySemicolon$ } = this.dynamic_;
|
|
474
|
+
if (!sloppySemicolon$?.[0])
|
|
475
|
+
return this.afterBrace$ !== this.tokenIdx;
|
|
476
|
+
if (test === true && sloppySemicolon$) // TODO: single-let mode for running parser
|
|
477
|
+
sloppySemicolon$[0] = false;
|
|
478
|
+
return this.afterBrace$ !== this.tokenIdx && test === 'M';
|
|
479
|
+
// TODO: should we always fail for expected set (test === 'M'), at least if
|
|
480
|
+
// token is not on a new line?
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
atRightParen() {
|
|
484
|
+
return this.l() !== ')';
|
|
463
485
|
}
|
|
464
486
|
|
|
465
487
|
/**
|
|
@@ -1104,7 +1126,6 @@ class AstBuildingParser extends BaseParser {
|
|
|
1104
1126
|
}
|
|
1105
1127
|
|
|
1106
1128
|
// make sure that the parens of `IN (…)` do not disappear:
|
|
1107
|
-
// TODO: make this a to-csn thing
|
|
1108
1129
|
secureParens( expr ) {
|
|
1109
1130
|
const op = expr?.op?.val;
|
|
1110
1131
|
const $parens = expr?.$parens;
|
|
@@ -1163,6 +1184,10 @@ class AstBuildingParser extends BaseParser {
|
|
|
1163
1184
|
// TODO: we could have an opt(?) parameter funcToken for speed-up (passing this.lr())
|
|
1164
1185
|
if (funcToken)
|
|
1165
1186
|
funcToken.parsedAs = 'func';
|
|
1187
|
+
|
|
1188
|
+
const filter = path[0].cardinality || path[0].where; // XSN TODO: filter$location
|
|
1189
|
+
if (filter) // TODO v7: make this be reported via guard, as error
|
|
1190
|
+
this.message( 'syntax-unexpected-filter', filter.location, {} );
|
|
1166
1191
|
// TODO: XSN representation of functions is a bit strange - rework
|
|
1167
1192
|
const op = { location, val: 'call' };
|
|
1168
1193
|
return this.attachLocation( { op, func: ref, args } );
|
|
@@ -1179,13 +1204,18 @@ class AstBuildingParser extends BaseParser {
|
|
|
1179
1204
|
// Everything after the first function is also a function, and not a reference.
|
|
1180
1205
|
|
|
1181
1206
|
for (let i = firstFunc; i < path.length; ++i) {
|
|
1182
|
-
|
|
1207
|
+
const item = path[i];
|
|
1208
|
+
if (item.args && item.$syntax === ':') {
|
|
1183
1209
|
// Error for `a(P => 1).b.c(P: 1)`: no ref after function.
|
|
1184
|
-
this
|
|
1210
|
+
// TODO v6: make this be reported via guard
|
|
1211
|
+
this.error( 'syntax-invalid-ref', item.args[$location], {
|
|
1212
|
+
// TODO: msg text - huh? → syntax-invalid-named-arg ?
|
|
1185
1213
|
code: '=>',
|
|
1186
1214
|
}, 'References after function calls can\'t be resolved. Use $(CODE) in function arguments');
|
|
1187
|
-
break;
|
|
1188
1215
|
}
|
|
1216
|
+
const filter = item.cardinality || item.where; // XSN TODO: filter$location
|
|
1217
|
+
if (filter) // TODO v7: make this be reported via guard, as error
|
|
1218
|
+
this.message( 'syntax-unexpected-filter', filter.location, {} );
|
|
1189
1219
|
}
|
|
1190
1220
|
|
|
1191
1221
|
const args = [];
|
|
@@ -45,6 +45,7 @@ tokens{ // reserved words
|
|
|
45
45
|
|
|
46
46
|
start returns[ source = new XsnSource( 'cdl' ) ]
|
|
47
47
|
:
|
|
48
|
+
{ this.afterBrace( null, 'init' ); }<always> // init sloppy semicolon handling
|
|
48
49
|
(
|
|
49
50
|
( <guard=namespaceRestriction> namespaceDeclaration[ $source ]
|
|
50
51
|
| usingDeclaration[ $source ]
|
|
@@ -57,7 +58,7 @@ start returns[ source = new XsnSource( 'cdl' ) ]
|
|
|
57
58
|
|
|
58
59
|
artifactsBlock[ art, start = undefined ]
|
|
59
60
|
:
|
|
60
|
-
'{'
|
|
61
|
+
'{' <prepare=afterBrace, arg=init>
|
|
61
62
|
{ $art.artifacts = this.createDict( $start ); $art.extensions = []; }
|
|
62
63
|
(
|
|
63
64
|
artifactDefOrExtend[ $art ]
|
|
@@ -235,7 +236,7 @@ aspectDef[ art, outer ]
|
|
|
235
236
|
entityDef[ art, outer ]
|
|
236
237
|
@finally{ this.attachLocation( $art ); }
|
|
237
238
|
:
|
|
238
|
-
ENTITY
|
|
239
|
+
ENTITY
|
|
239
240
|
name=namePath[ 'Entity' ]
|
|
240
241
|
{ this.addDef( $art, $outer, 'artifacts', 'entity', $name ); }
|
|
241
242
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
@@ -254,17 +255,12 @@ entityDef[ art, outer ]
|
|
|
254
255
|
query=queryExpression
|
|
255
256
|
{ $art.query = $query; $art.$syntax = 'entity'; }
|
|
256
257
|
|
|
|
258
|
+
<prepare=afterBrace, arg=sloppy> // enable special loop-exit, allow no `;`
|
|
257
259
|
query=projectionSpec
|
|
258
260
|
{ $art.query = $query; $art.$syntax = 'projection'; }
|
|
259
261
|
whereGroupByHaving[ $query ]?
|
|
260
262
|
orderByLimitOffset[ $query ]?
|
|
261
|
-
{
|
|
262
|
-
// not worth to nicer location here - is standard error in v6
|
|
263
|
-
this.warning( 'syntax-missing-proj-semicolon', this.la(),
|
|
264
|
-
{ expecting: [ "';'" ], offending: this.antlrName( this.la() ) },
|
|
265
|
-
'Missing $(EXPECTING) before $(OFFENDING)');
|
|
266
|
-
} }
|
|
267
|
-
<prepare=afterBrace> // disable special loop-exit, allow no `;`
|
|
263
|
+
{;}<prepare=afterBrace, arg=normal> // disable special loop-exit, allow no `;`
|
|
268
264
|
// TODO v6: these <prepare=afterBrace>s are extremely strange
|
|
269
265
|
)
|
|
270
266
|
)
|
|
@@ -811,6 +807,9 @@ typeOrIncludesSpec[ art ]
|
|
|
811
807
|
|
|
|
812
808
|
':'
|
|
813
809
|
(
|
|
810
|
+
// Since cds-compiler v5.8; new parser only
|
|
811
|
+
query=projectionSpec { $art.query = $query; $art.$syntax = 'projection'; }
|
|
812
|
+
|
|
|
814
813
|
typeExpression[ $art ]
|
|
815
814
|
|
|
|
816
815
|
<prefer>
|
|
@@ -1073,7 +1072,6 @@ projectionSpec returns[ default query = {} ]
|
|
|
1073
1072
|
@finally{ this.attachLocation($query); }
|
|
1074
1073
|
:
|
|
1075
1074
|
PROJECTION
|
|
1076
|
-
<prepare=afterBrace, arg=entity> // enable special loop-exit, TODO v6 delete
|
|
1077
1075
|
{ $query = { op: this.valueWithLocation( 'SELECT' ) }; }
|
|
1078
1076
|
ON
|
|
1079
1077
|
tab=fromRefWithOptAlias
|
|
@@ -1716,7 +1714,8 @@ options{ minTokensMatched = 1 }
|
|
|
1716
1714
|
','<prepare=nextFunctionArgument>
|
|
1717
1715
|
( expr=funcExpression { $pathStep.args.push( $expr ); }
|
|
1718
1716
|
| DeleteStarFromSet // Workaround for missing feature, see #13485, TODO
|
|
1719
|
-
| <exitLoop>
|
|
1717
|
+
| <exitLoop, guard=atRightParen>
|
|
1718
|
+
// TODO: later allow ')' <exitRule>, or ')'<mock, exitLoop>, or <exitBlock=MainAlt> with ( options{ block=MainAlt }: …)
|
|
1720
1719
|
)
|
|
1721
1720
|
)*
|
|
1722
1721
|
( // ORDER BY in generic functions, e.g. `first_value(id order by name)`
|
|
@@ -2014,7 +2013,6 @@ annoValue returns[ default value = {} ]
|
|
|
2014
2013
|
else { $value.struct = Object.create(null); $value.literal = 'struct'; }
|
|
2015
2014
|
}
|
|
2016
2015
|
(
|
|
2017
|
-
// TODO: complain about empty loop if top-level as before
|
|
2018
2016
|
// TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
|
|
2019
2017
|
// after `{` if top-level
|
|
2020
2018
|
sub=annoStructValue
|