@sap/cds-compiler 5.7.2 → 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 +62 -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/location.js +1 -1
- package/lib/base/message-registry.js +55 -20
- package/lib/base/messages.js +5 -2
- package/lib/base/model.js +8 -6
- 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 +60 -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 +2239 -2229
- 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 -1
- 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 +72 -34
- package/lib/parsers/CdlGrammar.g4 +20 -19
- package/lib/parsers/XprTree.js +206 -0
- package/lib/parsers/index.js +1 -1
- 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
|
@@ -172,7 +172,6 @@ declare namespace compiler {
|
|
|
172
172
|
withLocations?: boolean|string
|
|
173
173
|
/**
|
|
174
174
|
* Use the new non-ANTLR based parser for compilation.
|
|
175
|
-
* Experimental flag!
|
|
176
175
|
*
|
|
177
176
|
* @since v5.2.0
|
|
178
177
|
*/
|
|
@@ -934,6 +933,11 @@ declare namespace compiler {
|
|
|
934
933
|
/**
|
|
935
934
|
* Renders the given CSN into a CDL source representation.
|
|
936
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
|
+
*
|
|
937
941
|
* @returns Object containing the rendered model.
|
|
938
942
|
*/
|
|
939
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
|
|
@@ -171,7 +171,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
171
171
|
this.dynamic_.generic = spec ? spec[call.argPos] : specialFunctions[''][1];
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
lGenericIntroOrExpr( tryGenericIntro = true ) {
|
|
174
|
+
lGenericIntroOrExpr( _mode, tryGenericIntro = true ) {
|
|
175
175
|
const { keyword, type } = this.la();
|
|
176
176
|
// TODO: use lower-case in specialFunctions
|
|
177
177
|
const text = typeof keyword === 'string' ? keyword.toUpperCase() : type;
|
|
@@ -180,37 +180,44 @@ class AstBuildingParser extends BaseParser {
|
|
|
180
180
|
if (this.dynamic_.generic?.IN === 'separator')
|
|
181
181
|
this.prec_ = PRECEDENCE_OF_IN_PREDICATE; // only expressions if `in` is separator
|
|
182
182
|
if (generic !== 'expr')
|
|
183
|
-
return (generic === 'intro') ? 'GenericIntro' :
|
|
183
|
+
return (generic === 'intro') ? 'GenericIntro' : type;
|
|
184
|
+
// if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
|
|
184
185
|
const next = this.tokens[this.tokenIdx + 1];
|
|
185
|
-
if (next.type !== ',' && next.type !== ')' &&
|
|
186
|
+
if (next && next.type !== ',' && next.type !== ')' &&
|
|
186
187
|
this.dynamic_.generic[next.keyword?.toUpperCase?.()] !== 'separator')
|
|
187
188
|
return 'GenericIntro';
|
|
188
189
|
}
|
|
189
|
-
return (generic === 'expr') ? 'GenericExpr' :
|
|
190
|
+
return (generic === 'expr') ? 'GenericExpr' : type;
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
lGenericExpr() {
|
|
193
|
-
return this.lGenericIntroOrExpr( false );
|
|
194
|
+
return this.lGenericIntroOrExpr( null, false );
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
lGenericSeparator() {
|
|
197
|
+
lGenericSeparator() {
|
|
197
198
|
const { keyword, type } = this.la();
|
|
198
199
|
// TODO: use lower-case in specialFunctions
|
|
199
200
|
const text = typeof keyword === 'string' ? keyword.toUpperCase() : type;
|
|
200
201
|
const generic = this.dynamic_.generic?.[text];
|
|
201
|
-
return (generic === 'separator') ? 'GenericSeparator' :
|
|
202
|
+
return (generic === 'separator') ? 'GenericSeparator' : type;
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
addTokenToSet_( set, tokenName, val, collectKeywordsOnly ) {
|
|
205
|
-
const
|
|
206
|
-
// TODO:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
206
|
+
const token = parserTokens[tokenName];
|
|
207
|
+
// TODO: use lower-case in specialFunctions
|
|
208
|
+
const realTokens = token && this.dynamic_.generic?.[token];
|
|
209
|
+
if (realTokens) {
|
|
211
210
|
for (const t of realTokens)
|
|
212
211
|
super.addTokenToSet_( set, t.toLowerCase(), val, collectKeywordsOnly );
|
|
213
212
|
}
|
|
213
|
+
else if (tokenName === 'DeleteStarFromSet') { // in rule `argumentsAndFilter`
|
|
214
|
+
// TODO: workaround for (`GenericExpr : Id_all | '*'`), see #13485.
|
|
215
|
+
// Works, since `DeleteStarFromSet` comes after `*` (length-sorted):
|
|
216
|
+
delete set['*'];
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
super.addTokenToSet_( set, tokenName, val, collectKeywordsOnly );
|
|
220
|
+
}
|
|
214
221
|
}
|
|
215
222
|
|
|
216
223
|
inSelectItem( _test, arg ) { // only as action
|
|
@@ -334,7 +341,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
334
341
|
}
|
|
335
342
|
|
|
336
343
|
/**
|
|
337
|
-
* Prepare element restrictions and check
|
|
344
|
+
* Prepare element restrictions and check validity of final anno assignments.
|
|
338
345
|
*
|
|
339
346
|
* Called as <prepare=…>:
|
|
340
347
|
*
|
|
@@ -432,27 +439,49 @@ class AstBuildingParser extends BaseParser {
|
|
|
432
439
|
* `;` between statements is optional only after a `}` (ex braces of structure
|
|
433
440
|
* values for annotations and foreign key specifications).
|
|
434
441
|
*
|
|
442
|
+
* Unfortunate exception: always optional after `entity … as projection on`.
|
|
443
|
+
*
|
|
435
444
|
* Beware: mentioned in leanConditions, i.e. executed in predictions!
|
|
436
445
|
*/
|
|
437
446
|
afterBrace( test, arg ) {
|
|
438
447
|
if (!test) {
|
|
439
|
-
this.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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;
|
|
443
467
|
}
|
|
444
468
|
// TODO TOOL: the following test belongs to the BaseParser.js:
|
|
445
469
|
if (this.conditionTokenIdx === this.tokenIdx && // tested on same
|
|
446
470
|
this.conditionStackLength == null && // after error recover
|
|
447
471
|
test !== 'M')
|
|
448
472
|
return false;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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() !== ')';
|
|
456
485
|
}
|
|
457
486
|
|
|
458
487
|
/**
|
|
@@ -731,14 +760,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
731
760
|
}
|
|
732
761
|
|
|
733
762
|
taggedIfQuery( query ) {
|
|
734
|
-
|
|
763
|
+
// attached actions are run even if rules ends prematurely → query can be
|
|
764
|
+
// undefined
|
|
765
|
+
return (query?.op && queryOps[query.op.val])
|
|
735
766
|
? { query, location: query.$parens?.at( -1 ) ?? query.location }
|
|
736
767
|
: query;
|
|
737
768
|
}
|
|
738
769
|
|
|
739
|
-
addNamedArg(
|
|
740
|
-
|
|
741
|
-
(args.args ?? args)[expr.name.id] = expr;
|
|
770
|
+
addNamedArg( pathItem, idToken, expr ) {
|
|
771
|
+
this.addDef( expr, pathItem, 'args', 0, this.identAst( idToken ) );
|
|
742
772
|
}
|
|
743
773
|
|
|
744
774
|
ixprAst( args ) {
|
|
@@ -1096,7 +1126,6 @@ class AstBuildingParser extends BaseParser {
|
|
|
1096
1126
|
}
|
|
1097
1127
|
|
|
1098
1128
|
// make sure that the parens of `IN (…)` do not disappear:
|
|
1099
|
-
// TODO: make this a to-csn thing
|
|
1100
1129
|
secureParens( expr ) {
|
|
1101
1130
|
const op = expr?.op?.val;
|
|
1102
1131
|
const $parens = expr?.$parens;
|
|
@@ -1155,6 +1184,10 @@ class AstBuildingParser extends BaseParser {
|
|
|
1155
1184
|
// TODO: we could have an opt(?) parameter funcToken for speed-up (passing this.lr())
|
|
1156
1185
|
if (funcToken)
|
|
1157
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, {} );
|
|
1158
1191
|
// TODO: XSN representation of functions is a bit strange - rework
|
|
1159
1192
|
const op = { location, val: 'call' };
|
|
1160
1193
|
return this.attachLocation( { op, func: ref, args } );
|
|
@@ -1171,13 +1204,18 @@ class AstBuildingParser extends BaseParser {
|
|
|
1171
1204
|
// Everything after the first function is also a function, and not a reference.
|
|
1172
1205
|
|
|
1173
1206
|
for (let i = firstFunc; i < path.length; ++i) {
|
|
1174
|
-
|
|
1207
|
+
const item = path[i];
|
|
1208
|
+
if (item.args && item.$syntax === ':') {
|
|
1175
1209
|
// Error for `a(P => 1).b.c(P: 1)`: no ref after function.
|
|
1176
|
-
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 ?
|
|
1177
1213
|
code: '=>',
|
|
1178
1214
|
}, 'References after function calls can\'t be resolved. Use $(CODE) in function arguments');
|
|
1179
|
-
break;
|
|
1180
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, {} );
|
|
1181
1219
|
}
|
|
1182
1220
|
|
|
1183
1221
|
const args = [];
|