@sap/cds-compiler 3.9.4 → 4.0.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 +92 -4
- package/README.md +0 -1
- package/bin/cdsc.js +11 -23
- package/bin/cdsse.js +3 -3
- package/doc/API.md +5 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +17 -1
- package/doc/CHANGELOG_DEPRECATED.md +28 -0
- package/lib/api/.eslintrc.json +1 -1
- package/lib/api/main.js +26 -8
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +143 -64
- package/lib/base/messages.js +213 -107
- package/lib/base/model.js +11 -11
- package/lib/checks/.eslintrc.json +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +26 -3
- package/lib/checks/onConditions.js +67 -12
- package/lib/checks/queryNoDbArtifacts.js +106 -105
- package/lib/checks/sql-snippets.js +2 -0
- package/lib/checks/types.js +12 -6
- package/lib/checks/validator.js +2 -2
- package/lib/compiler/assert-consistency.js +10 -8
- package/lib/compiler/builtins.js +8 -2
- package/lib/compiler/checks.js +52 -35
- package/lib/compiler/define.js +31 -26
- package/lib/compiler/extend.js +120 -65
- package/lib/compiler/finalize-parse-cdl.js +12 -43
- package/lib/compiler/generate.js +16 -5
- package/lib/compiler/index.js +8 -5
- package/lib/compiler/kick-start.js +4 -3
- package/lib/compiler/populate.js +96 -95
- package/lib/compiler/propagator.js +7 -8
- package/lib/compiler/resolve.js +377 -103
- package/lib/compiler/shared.js +794 -517
- package/lib/compiler/tweak-assocs.js +8 -6
- package/lib/compiler/utils.js +44 -0
- package/lib/edm/annotations/genericTranslation.js +12 -4
- package/lib/edm/csn2edm.js +34 -32
- package/lib/edm/edm.js +34 -31
- package/lib/edm/edmAnnoPreprocessor.js +0 -23
- package/lib/edm/edmInboundChecks.js +7 -2
- package/lib/edm/edmPreprocessor.js +18 -17
- package/lib/edm/edmUtils.js +8 -4
- package/lib/gen/Dictionary.json +18 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -2
- package/lib/gen/languageParser.js +5006 -4582
- package/lib/json/from-csn.js +157 -112
- package/lib/json/to-csn.js +60 -89
- package/lib/language/antlrParser.js +17 -13
- package/lib/language/docCommentParser.js +11 -1
- package/lib/language/genericAntlrParser.js +13 -10
- package/lib/language/language.g4 +168 -97
- package/lib/main.d.ts +128 -36
- package/lib/main.js +1 -1
- package/lib/model/csnRefs.js +24 -5
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +7 -12
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +40 -2
- package/lib/optionProcessor.js +0 -3
- package/lib/render/toCdl.js +247 -214
- package/lib/render/toHdbcds.js +197 -181
- package/lib/render/toSql.js +325 -289
- package/lib/render/utils/common.js +42 -4
- package/lib/render/utils/delta.js +1 -1
- package/lib/render/utils/sql.js +3 -3
- package/lib/transform/braceExpression.js +2 -2
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/associations.js +24 -12
- package/lib/transform/db/expansion.js +17 -18
- package/lib/transform/db/flattening.js +17 -21
- package/lib/transform/db/rewriteCalculatedElements.js +171 -64
- package/lib/transform/db/views.js +3 -4
- package/lib/transform/draft/db.js +21 -12
- package/lib/transform/draft/odata.js +4 -0
- package/lib/transform/forOdataNew.js +11 -10
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +4 -2
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/parseExpr.js +3 -0
- package/lib/transform/transformUtilsNew.js +43 -23
- package/lib/transform/translateAssocsToJoins.js +7 -6
- package/lib/transform/universalCsn/.eslintrc.json +1 -1
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
- package/package.json +2 -2
- package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
- package/share/messages/message-explanations.json +1 -1
package/lib/language/language.g4
CHANGED
|
@@ -723,7 +723,7 @@ elementType[ art ] // TODO: split this monster rule
|
|
|
723
723
|
|
|
724
724
|
elementProperties[ elem ]
|
|
725
725
|
:
|
|
726
|
-
|
|
726
|
+
defaultAndNullability[ $elem ]
|
|
727
727
|
|
|
|
728
728
|
'=' e=expression
|
|
729
729
|
stored=STORED?
|
|
@@ -733,7 +733,7 @@ elementProperties[ elem ]
|
|
|
733
733
|
}
|
|
734
734
|
;
|
|
735
735
|
|
|
736
|
-
|
|
736
|
+
defaultAndNullability[ elem ]
|
|
737
737
|
:
|
|
738
738
|
defaultValue[ $elem ]
|
|
739
739
|
nullability[ $elem ]? // placement accoring to SQL spec
|
|
@@ -763,7 +763,8 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
763
763
|
extendWithOptElementsNoWith[ art ]
|
|
764
764
|
|
|
|
765
765
|
{ this.addExtension( $art, $outer, 'extend', $name ); }
|
|
766
|
-
WITH { this.noSemicolonHere();
|
|
766
|
+
WITH { this.noSemicolonHere(); }
|
|
767
|
+
{ this.docComment( $art ); }
|
|
767
768
|
annotationAssignment_ll1[ $art ]*
|
|
768
769
|
// #ATN: ELEMENTS, ENUM, DEFINITIONS, COLUMNS, ACTIONS are not reserved and
|
|
769
770
|
// could be includeRef
|
|
@@ -1033,57 +1034,59 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1033
1034
|
@after { this.attachLocation( $art ); }
|
|
1034
1035
|
:
|
|
1035
1036
|
simplePath[ $name, 'Annotate' ]
|
|
1036
|
-
(
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
'{' { $art.elements = this.createDict(); }
|
|
1043
|
-
annotateElement[ $art ]*
|
|
1044
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1045
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
1037
|
+
( // Element annotation
|
|
1038
|
+
':' simplePath[ $elemName, 'Element']
|
|
1039
|
+
{ this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
|
|
1040
|
+
( WITH { this.noSemicolonHere(); } )?
|
|
1041
|
+
{ this.docComment( $art ); }
|
|
1042
|
+
annotationAssignment_ll1[ $art ]*
|
|
1046
1043
|
(
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
'}' { this.finalizeDictOrArray( $art.
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
{ this.
|
|
1058
|
-
|
|
1059
|
-
|
|
|
1060
|
-
'(' { $art.params = this.createDict(); }
|
|
1061
|
-
annotateParam[ $art ]
|
|
1062
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1063
|
-
annotateParam[ $art ]
|
|
1064
|
-
)*
|
|
1065
|
-
')' { this.finalizeDictOrArray( $art.params ); }
|
|
1066
|
-
{ this.checkExtensionDict( $art.params ); }
|
|
1044
|
+
'{' { $art.elements = this.createDict(); }
|
|
1045
|
+
annotateElement[ $art ]*
|
|
1046
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1047
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
1048
|
+
optionalSemi
|
|
1049
|
+
| requiredSemi
|
|
1050
|
+
)
|
|
1051
|
+
| // Definition annotation
|
|
1052
|
+
{ this.addExtension( $art, $outer, 'annotate', $name ); }
|
|
1053
|
+
( WITH { this.noSemicolonHere(); } )?
|
|
1054
|
+
{ this.docComment( $art ); }
|
|
1055
|
+
annotationAssignment_ll1[ $art ]*
|
|
1067
1056
|
(
|
|
1068
|
-
|
|
1069
|
-
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1057
|
+
'{' { $art.elements = this.createDict(); }
|
|
1070
1058
|
annotateElement[ $art ]*
|
|
1071
1059
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1072
|
-
|
|
1060
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
1061
|
+
(
|
|
1062
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
1063
|
+
annotateAction[ $art ]*
|
|
1064
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1065
|
+
{ this.checkExtensionDict( $art.actions ); }
|
|
1066
|
+
)?
|
|
1073
1067
|
optionalSemi
|
|
1068
|
+
|
|
|
1069
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
1070
|
+
annotateAction[ $art ]*
|
|
1071
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1072
|
+
{ this.checkExtensionDict( $art.actions ); }
|
|
1073
|
+
optionalSemi
|
|
1074
|
+
|
|
|
1075
|
+
'(' { $art.params = this.createDict(); }
|
|
1076
|
+
annotateParam[ $art ]
|
|
1077
|
+
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1078
|
+
annotateParam[ $art ]
|
|
1079
|
+
)*
|
|
1080
|
+
')' { this.finalizeDictOrArray( $art.params ); }
|
|
1081
|
+
{ this.checkExtensionDict( $art.params ); }
|
|
1082
|
+
( annotateReturns[ $art ]
|
|
1083
|
+
| requiredSemi
|
|
1084
|
+
)
|
|
1085
|
+
|
|
|
1086
|
+
annotateReturns[ $art ]
|
|
1074
1087
|
|
|
|
1075
1088
|
requiredSemi
|
|
1076
1089
|
)
|
|
1077
|
-
|
|
|
1078
|
-
// TODO: set proper $art.returns
|
|
1079
|
-
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1080
|
-
annotateElement[ $art ]*
|
|
1081
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1082
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
1083
|
-
optionalSemi
|
|
1084
|
-
|
|
1085
|
-
|
|
|
1086
|
-
requiredSemi
|
|
1087
1090
|
)
|
|
1088
1091
|
;
|
|
1089
1092
|
|
|
@@ -1126,17 +1129,27 @@ annotateAction [ outer ] locals [ art = {} ]
|
|
|
1126
1129
|
{ this.checkExtensionDict( $art.params ); }
|
|
1127
1130
|
)?
|
|
1128
1131
|
(
|
|
1129
|
-
|
|
1130
|
-
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1131
|
-
annotateElement[ $art ]*
|
|
1132
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1133
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
1134
|
-
optionalSemi
|
|
1132
|
+
annotateReturns[ $art ]
|
|
1135
1133
|
|
|
|
1136
1134
|
requiredSemi
|
|
1137
1135
|
)
|
|
1138
1136
|
;
|
|
1139
1137
|
|
|
1138
|
+
annotateReturns[ art ]
|
|
1139
|
+
@after{ this.attachLocation( $art.returns ); }
|
|
1140
|
+
:
|
|
1141
|
+
ret=RETURNS { $art.returns = { kind: 'annotate' }; }
|
|
1142
|
+
{ this.docComment( $art.returns ); }
|
|
1143
|
+
annotationAssignment_ll1[ $art.returns ]*
|
|
1144
|
+
( '{' { $art.returns.elements = this.createDict(); }
|
|
1145
|
+
annotateElement[ $art.returns ]*
|
|
1146
|
+
'}' { this.finalizeDictOrArray( $art.returns.elements ); }
|
|
1147
|
+
{ this.checkExtensionDict( $art.returns.elements ); }
|
|
1148
|
+
optionalSemi
|
|
1149
|
+
| requiredSemi
|
|
1150
|
+
)
|
|
1151
|
+
;
|
|
1152
|
+
|
|
1140
1153
|
annotateParam [ outer ] locals [ art = {} ]
|
|
1141
1154
|
@after{ this.attachLocation( $art ); }
|
|
1142
1155
|
:
|
|
@@ -1190,6 +1203,8 @@ returnTypeSpec[ art ]
|
|
|
1190
1203
|
@after{ /* #ATN 1 */ }
|
|
1191
1204
|
:
|
|
1192
1205
|
ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
|
|
1206
|
+
{ this.docComment( $art.returns ); }
|
|
1207
|
+
annotationAssignment_ll1[ $art.returns ]*
|
|
1193
1208
|
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
1194
1209
|
( typeStruct[ $art.returns ]
|
|
1195
1210
|
nullability[ $art.returns ]?
|
|
@@ -1246,8 +1261,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1246
1261
|
many=HelperToken1 // rewritten MANY before '{'
|
|
1247
1262
|
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
1248
1263
|
typeStruct[ $art.items ]
|
|
1249
|
-
nullability[ $art.items ]
|
|
1250
|
-
|
|
1264
|
+
( nullability[ $art.items ]
|
|
1265
|
+
requiredSemi
|
|
1266
|
+
| optionalSemi
|
|
1267
|
+
)
|
|
1251
1268
|
|
|
|
1252
1269
|
(
|
|
1253
1270
|
array=ARRAY of=OF
|
|
@@ -1257,8 +1274,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1257
1274
|
)
|
|
1258
1275
|
// #ATN: typeRefOptArgs can start with TYPE
|
|
1259
1276
|
( typeStruct[ $art.items ]
|
|
1260
|
-
nullability[ $art.items ]
|
|
1261
|
-
|
|
1277
|
+
( nullability[ $art.items ]
|
|
1278
|
+
requiredSemi
|
|
1279
|
+
| optionalSemi
|
|
1280
|
+
)
|
|
1262
1281
|
| ( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
|
|
1263
1282
|
nullability[ $art.items ]?
|
|
1264
1283
|
{ this.docComment( $art ); }
|
|
@@ -1267,26 +1286,23 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1267
1286
|
ENUM '{' { $art.items.enum = this.createDict(); }
|
|
1268
1287
|
enumSymbolDef[ $art.items ]*
|
|
1269
1288
|
'}' { this.finalizeDictOrArray( $art.items.enum ); }
|
|
1270
|
-
(
|
|
1271
|
-
nullability[ $art.items ]
|
|
1289
|
+
( nullability[ $art.items ]
|
|
1272
1290
|
requiredSemi
|
|
1273
|
-
|
|
|
1274
|
-
optionalSemi
|
|
1291
|
+
| optionalSemi
|
|
1275
1292
|
)
|
|
1276
|
-
|
|
|
1277
|
-
requiredSemi
|
|
1293
|
+
| requiredSemi
|
|
1278
1294
|
)
|
|
1279
1295
|
)
|
|
1280
1296
|
|
|
|
1281
1297
|
typeTypeOf[ $art ]
|
|
1282
|
-
|
|
1298
|
+
defaultAndNullability[ $art ]?
|
|
1283
1299
|
{ this.docComment( $art ); }
|
|
1284
1300
|
annotationAssignment_ll1[ $art ]*
|
|
1285
1301
|
requiredSemi
|
|
1286
1302
|
|
|
|
1287
1303
|
l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1288
1304
|
typeRefOptArgs[ $art ]
|
|
1289
|
-
|
|
1305
|
+
defaultAndNullability[ $art ]?
|
|
1290
1306
|
{ this.docComment( $art ); }
|
|
1291
1307
|
annotationAssignment_ll1[ $art ]*
|
|
1292
1308
|
requiredSemi
|
|
@@ -1301,20 +1317,18 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1301
1317
|
{ $art.type.scope = $art.type.path.length; }
|
|
1302
1318
|
simplePath[ $art.type, 'ref']
|
|
1303
1319
|
)?
|
|
1320
|
+
optInvisibleNullability[ $art ]
|
|
1304
1321
|
{ this.docComment( $art ); }
|
|
1305
1322
|
annotationAssignment_ll1[ $art ]*
|
|
1306
1323
|
(
|
|
1307
1324
|
ENUM '{' { $art.enum = this.createDict(); }
|
|
1308
1325
|
enumSymbolDef[ $art ]*
|
|
1309
1326
|
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1310
|
-
(
|
|
1311
|
-
|
|
1312
|
-
|
|
|
1313
|
-
defaultAndNullablity[ $art ]
|
|
1327
|
+
( optionalSemi
|
|
1328
|
+
| defaultAndNullability[ $art ]
|
|
1314
1329
|
requiredSemi
|
|
1315
1330
|
)
|
|
1316
|
-
|
|
|
1317
|
-
defaultAndNullablity[ $art ]?
|
|
1331
|
+
| defaultAndNullability[ $art ]?
|
|
1318
1332
|
requiredSemi
|
|
1319
1333
|
)
|
|
1320
1334
|
|
|
|
@@ -1512,7 +1526,7 @@ foreignKey[ outer ] locals[ art = {}, elem = {} ]
|
|
|
1512
1526
|
typeTypeOf[ art ] locals[ _sync = 'nop' ]
|
|
1513
1527
|
@after { this.attachLocation($art.type); }
|
|
1514
1528
|
:
|
|
1515
|
-
TYPE OF
|
|
1529
|
+
t=TYPE o=OF
|
|
1516
1530
|
{ $art.type = { scope: 'typeOf' }; }
|
|
1517
1531
|
simplePath[ $art.type, 'ref' ]
|
|
1518
1532
|
( ':'
|
|
@@ -1521,6 +1535,12 @@ typeTypeOf[ art ] locals[ _sync = 'nop' ]
|
|
|
1521
1535
|
{ $art.type.scope = $art.type.path.length; }
|
|
1522
1536
|
simplePath[ $art.type, 'ref']
|
|
1523
1537
|
)?
|
|
1538
|
+
// We do not use (…|) here instead (…)? due to different ANTLR code generation:
|
|
1539
|
+
// (…|) would check for follow set, which does not work with local token rewrite
|
|
1540
|
+
{ if ($art.type.scope === 'typeOf')
|
|
1541
|
+
// Better error locations and much simpler code if we consider it as a path breakout:
|
|
1542
|
+
$art.type.path.unshift( { id: 'type of', location: this.tokenLocation( $t, $o )} );
|
|
1543
|
+
}
|
|
1524
1544
|
;
|
|
1525
1545
|
|
|
1526
1546
|
typeRefOptArgs[ art ]
|
|
@@ -1621,7 +1641,7 @@ projectionSpec returns[ query ] locals[ src ]
|
|
|
1621
1641
|
proj=PROJECTION ON
|
|
1622
1642
|
// now a simplified `tableTerm`:
|
|
1623
1643
|
{
|
|
1624
|
-
$src = { path: []
|
|
1644
|
+
$src = { path: [] };
|
|
1625
1645
|
$query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
|
|
1626
1646
|
}
|
|
1627
1647
|
fromPath[ $src, 'artref']
|
|
@@ -1750,11 +1770,19 @@ tableExpression returns[ table ] // TableOrJoin
|
|
|
1750
1770
|
tableTerm returns [ table ]
|
|
1751
1771
|
@after{ /* #ATN 1 */ this.attachLocation($table); }
|
|
1752
1772
|
:
|
|
1753
|
-
{ $table = { path: []
|
|
1754
|
-
fromPath[ $table, 'artref']
|
|
1755
|
-
(
|
|
1756
|
-
|
|
1757
|
-
|
|
1773
|
+
{ $table = { path: [] }; }
|
|
1774
|
+
f=fromPath[ $table, 'artref']
|
|
1775
|
+
{ if ($f.dotAfterFilter)
|
|
1776
|
+
this.warning( 'syntax-invalid-path-separator', $f.dotAfterFilter,
|
|
1777
|
+
{ '#': 'dot', code: '.', newcode: ':' } );
|
|
1778
|
+
}
|
|
1779
|
+
( { if (!$table.scope)
|
|
1780
|
+
$table.scope = $table.path.length;
|
|
1781
|
+
else
|
|
1782
|
+
this.warning( 'syntax-invalid-path-separator', this.getCurrentToken(),
|
|
1783
|
+
{ '#': 'colon', code: ':', newcode: '.' } );
|
|
1784
|
+
}
|
|
1785
|
+
':' fromPath[ $table, 'ref']
|
|
1758
1786
|
)?
|
|
1759
1787
|
( AS n1=ident['FromAlias'] { $table.name = $n1.id }
|
|
1760
1788
|
| n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
|
|
@@ -1770,7 +1798,7 @@ tableTerm returns [ table ]
|
|
|
1770
1798
|
(
|
|
1771
1799
|
qe=queryExpression close=')'
|
|
1772
1800
|
{ $table = this.surroundByParens( $qe.query, $open, $close, true ); }
|
|
1773
|
-
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table
|
|
1801
|
+
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table alias
|
|
1774
1802
|
| a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
|
|
1775
1803
|
// not using ident` to have a similar behavior to above
|
|
1776
1804
|
)?
|
|
@@ -1780,17 +1808,25 @@ tableTerm returns [ table ]
|
|
|
1780
1808
|
)
|
|
1781
1809
|
;
|
|
1782
1810
|
|
|
1783
|
-
fromPath[ qp, idkind ]
|
|
1811
|
+
fromPath[ qp, idkind ] returns[ dotAfterFilter = null ]
|
|
1784
1812
|
@after{ this.attachLocation($qp); }
|
|
1785
1813
|
:
|
|
1786
1814
|
id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
1787
1815
|
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1816
|
+
{ $dotAfterFilter = false; }
|
|
1788
1817
|
| cardinalityAndFilter[ $id.id ]
|
|
1818
|
+
{ $dotAfterFilter = false; }
|
|
1789
1819
|
)?
|
|
1790
1820
|
(
|
|
1821
|
+
{ if ($dotAfterFilter === false) {
|
|
1822
|
+
$dotAfterFilter = this.getCurrentToken();
|
|
1823
|
+
if (!$qp.scope) $qp.scope = $qp.path.length;
|
|
1824
|
+
} }
|
|
1791
1825
|
'.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
1792
|
-
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1826
|
+
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1827
|
+
{ if (!$dotAfterFilter) $dotAfterFilter = false; }
|
|
1793
1828
|
| cardinalityAndFilter[ $id.id ]
|
|
1829
|
+
{ if (!$dotAfterFilter) $dotAfterFilter = false; }
|
|
1794
1830
|
)?
|
|
1795
1831
|
)*
|
|
1796
1832
|
;
|
|
@@ -2301,9 +2337,7 @@ simplePath[ art, category ] locals[ _sync = 'nop' ]
|
|
|
2301
2337
|
// path as broken in this case.
|
|
2302
2338
|
:
|
|
2303
2339
|
head=ident[ $category ]
|
|
2304
|
-
{ if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id );
|
|
2305
|
-
if ($category === 'artref') $art.scope = 0;
|
|
2306
|
-
}
|
|
2340
|
+
{ if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id ); }
|
|
2307
2341
|
(
|
|
2308
2342
|
'.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
|
|
2309
2343
|
)*
|
|
@@ -2529,14 +2563,34 @@ windowFrameStartSpec[ args = [] ]
|
|
|
2529
2563
|
|
|
2530
2564
|
cardinalityAndFilter[ pathStep ] locals [ _sync = 'nop' ]
|
|
2531
2565
|
:
|
|
2532
|
-
|
|
2566
|
+
{ if (!$pathStep) $pathStep = {}; }
|
|
2567
|
+
openFilter='['
|
|
2533
2568
|
optionalCardinality[ pathStep ]?
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2569
|
+
|
|
2570
|
+
filterWhereClause[ $pathStep ] // required, see rule's comment
|
|
2571
|
+
(
|
|
2572
|
+
group=GROUP by=BY
|
|
2573
|
+
e1=expression { $pathStep.groupBy = [ $e1.expr ]; }
|
|
2574
|
+
( ',' en=expression { $pathStep.groupBy.push( $en.expr ); } )*
|
|
2575
|
+
{ this.csnParseOnly('syntax-unexpected-sql-clause', [ $group, $by ], { keyword: 'GROUP BY' }); }
|
|
2576
|
+
)?
|
|
2577
|
+
( hv=HAVING
|
|
2578
|
+
having=condition { $pathStep.having = $having.cond; }
|
|
2579
|
+
{ this.csnParseOnly('syntax-unexpected-sql-clause', [ $hv ], { keyword: 'HAVING' }); }
|
|
2580
|
+
)?
|
|
2581
|
+
( { const orderKw = this._input.LT(1); const byKw = this._input.LT(2); }
|
|
2582
|
+
ob=orderByClause[ $pathStep ] { $pathStep = $ob.query;; }
|
|
2583
|
+
{ this.csnParseOnly('syntax-unexpected-sql-clause', [ orderKw, byKw ], { keyword: 'ORDER BY' }); }
|
|
2584
|
+
)?
|
|
2585
|
+
( { const limit = this._input.LT(1); }
|
|
2586
|
+
lc=limitClause[ $pathStep ] { $pathStep = $lc.query;; }
|
|
2587
|
+
{ this.csnParseOnly('syntax-unexpected-sql-clause', [ limit ], { keyword: 'LIMIT' }); }
|
|
2588
|
+
)?
|
|
2589
|
+
|
|
2590
|
+
closeFilter=']'
|
|
2538
2591
|
;
|
|
2539
2592
|
|
|
2593
|
+
|
|
2540
2594
|
optionalCardinality[ pathStep ]
|
|
2541
2595
|
@after { if ($pathStep && $pathStep.cardinality) this.attachLocation($pathStep.cardinality); }
|
|
2542
2596
|
:
|
|
@@ -2549,17 +2603,36 @@ optionalCardinality[ pathStep ]
|
|
|
2549
2603
|
)
|
|
2550
2604
|
;
|
|
2551
2605
|
|
|
2606
|
+
filterWhereClause[ pathStep ]
|
|
2607
|
+
:
|
|
2608
|
+
// NOTE: Keep in sync with optionalWhereForFilter!
|
|
2609
|
+
//
|
|
2610
|
+
// For ANTLR, WHERE is required, but the generated parser may skip `match(WHERE)` in
|
|
2611
|
+
// `optionalWhereForFilter`. Because `(WHERE cond)?` would invoke adaptive predict,
|
|
2612
|
+
// we use this hack that skips parsing of the condition, if the token is a SQL clause,
|
|
2613
|
+
// but makes Antlr assume that it is required.
|
|
2614
|
+
{
|
|
2615
|
+
const tok = this.getCurrentToken();
|
|
2616
|
+
if (tok.type === languageParser.GROUP
|
|
2617
|
+
|| tok.type === languageParser.ORDER
|
|
2618
|
+
|| tok.type === languageParser.LIMIT
|
|
2619
|
+
|| tok.type === languageParser.HAVING)
|
|
2620
|
+
return $ctx;
|
|
2621
|
+
}
|
|
2622
|
+
optionalWhereForFilter cond=condition { if ($pathStep) $pathStep.where = $cond.cond; }
|
|
2623
|
+
;
|
|
2624
|
+
|
|
2552
2625
|
optionalWhereForFilter
|
|
2553
2626
|
:
|
|
2627
|
+
// NOTE: only call from rule filterWhereClause!
|
|
2628
|
+
//
|
|
2554
2629
|
// For ANTLR, WHERE is required, but we allow the generated parser skipping
|
|
2555
|
-
// the call of match(WHERE)
|
|
2556
|
-
//
|
|
2557
|
-
// calling rule does not throw an error if the current token does not match
|
|
2630
|
+
// the call of match(WHERE). This hack requires that sync() at each state in
|
|
2631
|
+
// the calling rule does not throw an error if the current token does not match
|
|
2558
2632
|
// one of the expected ones.
|
|
2559
2633
|
{
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
// TODO: should we somehow add those keywords to $(EXPECTED)?
|
|
2634
|
+
if (this.getCurrentToken().type !== languageParser.WHERE)
|
|
2635
|
+
return $ctx; // TODO: should we somehow add those keywords to $(EXPECTED)?
|
|
2563
2636
|
}
|
|
2564
2637
|
WHERE
|
|
2565
2638
|
;
|
|
@@ -2693,9 +2766,7 @@ annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
|
|
|
2693
2766
|
// path as broken in this case.
|
|
2694
2767
|
:
|
|
2695
2768
|
head=ident[ $category ]
|
|
2696
|
-
{ $art.path = []; this.pushIdent( $art.path, $head.id, $headat );
|
|
2697
|
-
if ($category === 'artref') $art.scope = 0;
|
|
2698
|
-
}
|
|
2769
|
+
{ $art.path = []; this.pushIdent( $art.path, $head.id, $headat ); }
|
|
2699
2770
|
(
|
|
2700
2771
|
'.' at='@'? tail=ident[ $category ]
|
|
2701
2772
|
{ this.pushIdent( $art.path, $tail.id, $at );
|