@sap/cds-compiler 2.10.4 → 2.12.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +58 -35
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +16 -0
  9. package/lib/api/.eslintrc.json +2 -0
  10. package/lib/api/main.js +10 -36
  11. package/lib/api/options.js +17 -8
  12. package/lib/api/validate.js +30 -3
  13. package/lib/backends.js +12 -13
  14. package/lib/base/dictionaries.js +2 -1
  15. package/lib/base/keywords.js +3 -2
  16. package/lib/base/message-registry.js +64 -11
  17. package/lib/base/messages.js +38 -18
  18. package/lib/base/model.js +6 -4
  19. package/lib/base/optionProcessorHelper.js +148 -86
  20. package/lib/checks/.eslintrc.json +2 -0
  21. package/lib/checks/actionsFunctions.js +2 -1
  22. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  23. package/lib/checks/foreignKeys.js +4 -4
  24. package/lib/checks/managedInType.js +4 -4
  25. package/lib/checks/queryNoDbArtifacts.js +1 -3
  26. package/lib/checks/selectItems.js +4 -0
  27. package/lib/checks/sql-snippets.js +93 -0
  28. package/lib/checks/unknownMagic.js +6 -3
  29. package/lib/checks/validator.js +8 -0
  30. package/lib/compiler/assert-consistency.js +14 -5
  31. package/lib/compiler/base.js +64 -0
  32. package/lib/compiler/builtins.js +62 -16
  33. package/lib/compiler/checks.js +34 -10
  34. package/lib/compiler/definer.js +91 -112
  35. package/lib/compiler/index.js +30 -30
  36. package/lib/compiler/propagator.js +8 -4
  37. package/lib/compiler/resolver.js +279 -63
  38. package/lib/compiler/shared.js +65 -230
  39. package/lib/compiler/utils.js +191 -0
  40. package/lib/edm/annotations/genericTranslation.js +35 -18
  41. package/lib/edm/annotations/preprocessAnnotations.js +1 -1
  42. package/lib/edm/csn2edm.js +4 -3
  43. package/lib/edm/edm.js +8 -8
  44. package/lib/edm/edmPreprocessor.js +61 -59
  45. package/lib/edm/edmUtils.js +14 -15
  46. package/lib/gen/Dictionary.json +82 -40
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +19 -1
  49. package/lib/gen/language.tokens +80 -73
  50. package/lib/gen/languageLexer.interp +27 -1
  51. package/lib/gen/languageLexer.js +925 -826
  52. package/lib/gen/languageLexer.tokens +72 -65
  53. package/lib/gen/languageParser.js +4817 -4102
  54. package/lib/json/from-csn.js +57 -26
  55. package/lib/json/to-csn.js +244 -51
  56. package/lib/language/antlrParser.js +12 -1
  57. package/lib/language/docCommentParser.js +1 -1
  58. package/lib/language/errorStrategy.js +26 -8
  59. package/lib/language/genericAntlrParser.js +106 -30
  60. package/lib/language/language.g4 +200 -70
  61. package/lib/language/multiLineStringParser.js +536 -0
  62. package/lib/main.d.ts +220 -21
  63. package/lib/main.js +6 -3
  64. package/lib/model/api.js +2 -2
  65. package/lib/model/csnRefs.js +218 -86
  66. package/lib/model/csnUtils.js +99 -178
  67. package/lib/model/enrichCsn.js +84 -43
  68. package/lib/model/revealInternalProperties.js +25 -8
  69. package/lib/model/sortViews.js +8 -1
  70. package/lib/modelCompare/compare.js +2 -1
  71. package/lib/optionProcessor.js +33 -18
  72. package/lib/render/.eslintrc.json +1 -2
  73. package/lib/render/DuplicateChecker.js +2 -2
  74. package/lib/render/manageConstraints.js +1 -1
  75. package/lib/render/toCdl.js +202 -82
  76. package/lib/render/toHdbcds.js +194 -135
  77. package/lib/render/toRename.js +7 -10
  78. package/lib/render/toSql.js +91 -51
  79. package/lib/render/utils/common.js +24 -5
  80. package/lib/render/utils/sql.js +6 -4
  81. package/lib/transform/braceExpression.js +4 -2
  82. package/lib/transform/db/applyTransformations.js +189 -0
  83. package/lib/transform/db/associations.js +389 -0
  84. package/lib/transform/db/cdsPersistence.js +150 -0
  85. package/lib/transform/db/constraints.js +275 -119
  86. package/lib/transform/db/draft.js +6 -4
  87. package/lib/transform/db/expansion.js +10 -9
  88. package/lib/transform/db/flattening.js +23 -8
  89. package/lib/transform/db/temporal.js +236 -0
  90. package/lib/transform/db/transformExists.js +106 -25
  91. package/lib/transform/db/views.js +485 -0
  92. package/lib/transform/forHanaNew.js +90 -1036
  93. package/lib/transform/forOdataNew.js +11 -3
  94. package/lib/transform/localized.js +5 -14
  95. package/lib/transform/odata/generateForeignKeyElements.js +2 -2
  96. package/lib/transform/transformUtilsNew.js +34 -20
  97. package/lib/transform/translateAssocsToJoins.js +15 -23
  98. package/lib/transform/universalCsnEnricher.js +217 -47
  99. package/lib/utils/file.js +13 -6
  100. package/lib/utils/term.js +65 -42
  101. package/lib/utils/timetrace.js +55 -27
  102. package/package.json +1 -1
  103. package/lib/transform/db/helpers.js +0 -58
@@ -490,7 +490,7 @@ projectionSpec returns[ query ] locals[ src ]
490
490
  // now a simplified `tableTerm`:
491
491
  {
492
492
  $src = { path: [], scope: 0 };
493
- $query = { op: this.tokenLocation( $proj, undefined, 'SELECT' ), from: $src, location: this.startLocation() };
493
+ $query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
494
494
  }
495
495
  fromPath[ $src, 'artref']
496
496
  ( ':'
@@ -498,6 +498,8 @@ projectionSpec returns[ query ] locals[ src ]
498
498
  fromPath[ $src, 'ref']
499
499
  )?
500
500
  ( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
501
+ // ANTLR errors are better if we use ( A )? instead of ( A | ):
502
+ { if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
501
503
  bracedSelectItemListDef[ $query ]?
502
504
  excludingClause[ $query ]?
503
505
  ;
@@ -981,6 +983,7 @@ mixinElementDef[ outer ] locals[ art ]
981
983
 
982
984
  misplacedAnnotations[ annos, messageId ]
983
985
  :
986
+ // No docComment() here
984
987
  annotationAssignment_ll1[ $annos ]+
985
988
  { if ($messageId) // issue specified in central registry
986
989
  this.message( messageId, this.tokenLocation( $ctx.start, this.getCurrentToken() ) );
@@ -1076,7 +1079,7 @@ elementDefInner[ outer, loc, annos, allowEq ] returns[ art ]
1076
1079
  annotationAssignment_ll1[ $annos ]*
1077
1080
  requiredSemi // also req after foreign key spec
1078
1081
  |
1079
- l=LOCALIZED { $art.localized = this.tokenLocation( $l, undefined, true ); }
1082
+ l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1080
1083
  typeRefOptArgs[ $art ]
1081
1084
  { this.docComment( $annos ); }
1082
1085
  annotationAssignment_ll1[ $annos ]*
@@ -1144,7 +1147,7 @@ selectItemDef[ outer ] locals[ annos = [] ]
1144
1147
  @after{ if ($ctx.art) this.attachLocation($art.art); }
1145
1148
  :
1146
1149
  star='*'
1147
- { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1150
+ { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1148
1151
  |
1149
1152
  { this.docComment( $annos ); }
1150
1153
  annotationAssignment_atn[ $annos ]*
@@ -1154,8 +1157,8 @@ selectItemDef[ outer ] locals[ annos = [] ]
1154
1157
  key=KEY?
1155
1158
  art=selectItemDefBody[ $outer, $annos ]
1156
1159
  {
1157
- if ($virtual) $art.art.virtual = this.tokenLocation( $virtual, undefined, true );
1158
- if ($key) $art.art.key = this.tokenLocation( $key, undefined, true );
1160
+ if ($virtual) $art.art.virtual = this.valueWithTokenLocation( true, $virtual );
1161
+ if ($key) $art.art.key = this.valueWithTokenLocation( true, $key );
1159
1162
  }
1160
1163
  ;
1161
1164
 
@@ -1188,7 +1191,7 @@ selectItemDefBody[ outer, annos ] returns[ art = {} ]
1188
1191
  excludingClause[ $art ]?
1189
1192
  |
1190
1193
  star='*'
1191
- { $art.inline = [ this.tokenLocation( $star, undefined, '*' ) ]; }
1194
+ { $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
1192
1195
  )
1193
1196
  )?
1194
1197
  |
@@ -1237,7 +1240,7 @@ selectItemInlineDef[ outer ] locals[ annos = [] ]
1237
1240
  @after{ if ($ctx.art) this.attachLocation($art.art); }
1238
1241
  :
1239
1242
  star='*'
1240
- { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1243
+ { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1241
1244
  |
1242
1245
  { this.docComment( $annos ); }
1243
1246
  annotationAssignment_atn[ $annos ]*
@@ -1298,15 +1301,17 @@ entityParameterDef[ outer ] locals[ art, annos = [] ]
1298
1301
  annotationAssignment_fix[ $annos ]*
1299
1302
  typeSpec[ $art ]
1300
1303
  ( DEFAULT expr=expression { $art.default = $expr.expr; } )?
1304
+ { this.docComment( $annos ); }
1305
+ annotationAssignment_ll1[ $annos ]*
1301
1306
  ;
1302
1307
 
1303
1308
  nullability[ art ]
1304
1309
  :
1305
1310
  not=NOT n1=NULL
1306
- { $art.notNull = this.tokenLocation($not,$n1,true); }
1311
+ { $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
1307
1312
  |
1308
1313
  n2=NULL
1309
- { $art.notNull = this.tokenLocation($n2,undefined,false); }
1314
+ { $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
1310
1315
  ;
1311
1316
 
1312
1317
  elementProperties[ elem ]
@@ -1463,7 +1468,7 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1463
1468
  { this.docComment( $annos ); }
1464
1469
  annotationAssignment_ll1[ $annos ]* requiredSemi
1465
1470
  |
1466
- l=LOCALIZED { $art.localized = this.tokenLocation( $l, undefined, true ); }
1471
+ l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1467
1472
  typeRefOptArgs[ $art ]
1468
1473
  { this.docComment( $annos ); }
1469
1474
  annotationAssignment_ll1[ $annos ]*
@@ -1499,7 +1504,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1499
1504
  { $art.enum = Object.create(null); }
1500
1505
  enumSymbolDef[ $art ]*
1501
1506
  '}'
1502
- optionalSemi | requiredSemi
1507
+ optionalSemi
1508
+ | requiredSemi
1503
1509
  )
1504
1510
  |
1505
1511
  ':' // with element, e.g. `type T : E:elem enum { ... }`
@@ -1511,7 +1517,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1511
1517
  { $art.enum = Object.create(null); }
1512
1518
  enumSymbolDef[ $art ]*
1513
1519
  '}'
1514
- optionalSemi | requiredSemi
1520
+ optionalSemi
1521
+ | requiredSemi
1515
1522
  )
1516
1523
  |
1517
1524
  { this.docComment( $annos ); }
@@ -1520,7 +1527,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1520
1527
  { $art.enum = Object.create(null); }
1521
1528
  enumSymbolDef[ $art ]*
1522
1529
  '}'
1523
- optionalSemi | requiredSemi
1530
+ optionalSemi
1531
+ | requiredSemi
1524
1532
  )
1525
1533
  |
1526
1534
  // TODO: complain if used in anno def?
@@ -1764,25 +1772,82 @@ orderByClause[ inQuery ] returns [ query ]
1764
1772
 
1765
1773
  overOrderByClause returns [ expr ]
1766
1774
  :
1767
- o=ORDER b=BY { $expr = { op: this.tokenLocation($o, $b, 'orderBy' ) , args: [] }}
1775
+ o=ORDER b=BY { $expr = { op: this.valueWithTokenLocation( 'orderBy', $o, $b ) , args: [] }}
1768
1776
  ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1769
1777
  ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1770
1778
  ;
1771
1779
 
1772
1780
  partitionByClause returns [ expr ]
1773
1781
  :
1774
- p=PARTITION b=BY { $expr = { op: this.tokenLocation($p, $b, 'partitionBy' ) , args: [] }}
1782
+ p=PARTITION b=BY { $expr = { op: this.valueWithTokenLocation( 'partitionBy', $p, $b ) , args: [] }}
1775
1783
  e1=expression { $expr.args.push( $e1.expr ); }
1776
1784
  ( ',' en=expression { $expr.args.push( $en.expr ); } )*
1777
1785
  ;
1778
1786
 
1787
+ windowFrameClause returns [ wf ]
1788
+ :
1789
+ r=ROWS { $wf = { op: this.valueWithTokenLocation( 'rows', $r ) , args: [] }}
1790
+ wfe=windowFrameExtentSpec { $wf.args.push( $wfe.wfe ); }
1791
+ ;
1792
+
1793
+ windowFrameExtentSpec returns[ wfe ]
1794
+ :
1795
+ { $wfe = {} }
1796
+ windowFrameStartSpec [ $wfe ]
1797
+ |
1798
+ b=BETWEEN
1799
+ { $wfe = { op: this.valueWithTokenLocation( 'frameBetween', $b ), args: [] } }
1800
+ wfb1=windowFrameBoundSpec { $wfe.args.push( $wfb1.wfb ); }
1801
+ AND
1802
+ wfb2=windowFrameBoundSpec { $wfe.args.push( $wfb2.wfb ); }
1803
+ ;
1804
+
1805
+ windowFrameBoundSpec returns [ wfb ]
1806
+ @after{ /* #ATN 1 */ }
1807
+ :
1808
+ // #ATN: Not ll1 because `UNBOUNDED` could also be part of the windowFrameStartSpec
1809
+ // `UNBOUNDED` would then be immediately followed by `PRECEDING`
1810
+ u=UNBOUNDED f=FOLLOWING
1811
+ { $wfb = { op: this.valueWithTokenLocation( 'unboundedFollowing', $u, $f ), args: []} }
1812
+ |
1813
+ // #ATN: Not ll1 because `Number` could also be part of the windowFrameStartSpec
1814
+ // `Number` would then be immediately followed by `PRECEDING`
1815
+ n=Number f=FOLLOWING
1816
+ { $wfb = { op: this.valueWithTokenLocation( 'following', $n, $f ), args: [ this.numberLiteral( $n ) ]} }
1817
+ |
1818
+ { $wfb = {} }
1819
+ windowFrameStartSpec [ $wfb ]
1820
+ ;
1821
+
1822
+ windowFrameStartSpec [ wf ]
1823
+ :
1824
+ u=UNBOUNDED p=PRECEDING
1825
+ {
1826
+ $wf.op = this.valueWithTokenLocation( 'unboundedPreceding', $u, $p );
1827
+ $wf.args = [];
1828
+ }
1829
+ |
1830
+ n=Number p=PRECEDING
1831
+ {
1832
+ $wf.op = this.valueWithTokenLocation( 'preceding', $p );
1833
+ $wf.args = [ this.numberLiteral( $n ) ];
1834
+ }
1835
+ |
1836
+ c=CURRENT r=ROW
1837
+ {
1838
+ $wf.op = this.valueWithTokenLocation( 'currentRow', $c, $r );
1839
+ $wf.args = [];
1840
+ }
1841
+ ;
1842
+
1779
1843
  overClause returns [ over ]
1780
1844
  @after { this.attachLocation($over); }
1781
1845
  :
1782
- o=OVER { $over = { op: this.tokenLocation( $o, null, 'over' ) , args: [] } }
1846
+ o=OVER { $over = { op: this.valueWithTokenLocation( 'over', $o ) , args: [] } }
1783
1847
  '('
1784
1848
  ( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
1785
1849
  ( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
1850
+ ( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
1786
1851
  ')'
1787
1852
  ;
1788
1853
 
@@ -1799,11 +1864,11 @@ limitClause[ inQuery ] returns [ query ]
1799
1864
  orderBySpec returns[ ob ]
1800
1865
  :
1801
1866
  e=expression { $ob = $e.expr; }
1802
- ( asc=ASC { $ob.sort = this.tokenLocation( $asc, undefined, 'asc' ); }
1803
- | desc=DESC { $ob.sort = this.tokenLocation( $desc, undefined, 'desc' ); }
1867
+ ( asc=ASC { $ob.sort = this.valueWithTokenLocation( 'asc', $asc ); }
1868
+ | desc=DESC { $ob.sort = this.valueWithTokenLocation( 'desc', $desc ); }
1804
1869
  )?
1805
1870
  ( nb=NULLS ne=( FIRST | LAST )
1806
- { $ob.nulls = this.tokenLocation( $nb, $ne, $ne.text.toLowerCase() ); }
1871
+ { $ob.nulls = this.valueWithTokenLocation( $ne.text.toLowerCase(), $nb, $ne ); }
1807
1872
  )?
1808
1873
  ;
1809
1874
 
@@ -1826,7 +1891,7 @@ queryPrimary returns[ query = {} ]
1826
1891
  { $query = this.surroundByParens( $qe.query, $open, $close ); }
1827
1892
  |
1828
1893
  select=SELECT
1829
- { $query = { op: this.tokenLocation( $select, undefined, 'SELECT' ), location: this.startLocation() }; }
1894
+ { $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
1830
1895
  (
1831
1896
  FROM querySource[ $query ]
1832
1897
  (
@@ -1835,13 +1900,13 @@ queryPrimary returns[ query = {} ]
1835
1900
  '}' INTO
1836
1901
  )?
1837
1902
  ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1838
- { $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
1903
+ { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1839
1904
  )?
1840
1905
  bracedSelectItemListDef[ $query ]?
1841
1906
  excludingClause[ $query ]?
1842
1907
  |
1843
1908
  ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1844
- { $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
1909
+ { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1845
1910
  )?
1846
1911
  { $query.columns = []; } // set it early to avoid "wildcard" errors
1847
1912
  selectItemDef[ $query.columns ]
@@ -1895,8 +1960,8 @@ joinOp[ left ] returns[ table ] locals [ join ]
1895
1960
  | t1=RIGHT t2=OUTER? c=joinCardinality? op=JOIN { $join = 'right' }
1896
1961
  | t1=FULL t2=OUTER? c=joinCardinality? op=JOIN { $join = 'full' }
1897
1962
  )
1898
- { $table = { op: this.tokenLocation( $op, undefined, 'join' ),
1899
- join: this.tokenLocation( $t1 || $op, $t2, $join ),
1963
+ { $table = { op: this.valueWithTokenLocation( 'join', $op ),
1964
+ join: this.valueWithTokenLocation( $join, $t1 || $op, $t2 ),
1900
1965
  args: ($left ? [$left] : []),
1901
1966
  location: $left && $left.location };
1902
1967
  if ($ctx.c) $table.cardinality = $c.joinCard; }
@@ -1948,6 +2013,8 @@ tableTerm returns [ table ]
1948
2013
  // if we would use rule `ident`, we would either had to make all JOIN
1949
2014
  // kinds reserved or introduce ATN
1950
2015
  )?
2016
+ // ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
2017
+ { if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
1951
2018
  |
1952
2019
  open='('
1953
2020
  // #ATN: The following alternative is not LL1, because both can start with
@@ -2001,24 +2068,24 @@ fromPath[ qp, idkind ]
2001
2068
 
2002
2069
  condition returns [ cond ] locals [ args = [], orl = [] ]
2003
2070
  @after{
2004
- $cond = ($args.length == 1)
2071
+ $cond = ($args.length === 1)
2005
2072
  ? this.attachLocation( $args[0] )
2006
2073
  : this.attachLocation({ op: $orl[0], args: $args });
2007
2074
  }
2008
2075
  :
2009
2076
  c1=conditionAnd { $args.push($c1.cond); }
2010
- ( or=OR c2=conditionAnd { $args.push($c2.cond); $orl.push(this.tokenLocation( $or, undefined, 'or' ))} )*
2077
+ ( or=OR c2=conditionAnd { $args.push($c2.cond); $orl.push(this.valueWithTokenLocation( 'or', $or ))} )*
2011
2078
  ;
2012
2079
 
2013
2080
  conditionAnd returns [ cond ] locals [ args = [], andl = [] ]
2014
2081
  @after{
2015
- $cond = ($args.length == 1)
2082
+ $cond = ($args.length === 1)
2016
2083
  ? $args[0]
2017
2084
  : this.attachLocation({ op: $andl[0], args: $args });
2018
2085
  }
2019
2086
  :
2020
2087
  c1=conditionTerm { $args.push($c1.cond); }
2021
- ( and=AND c2=conditionTerm { $args.push($c2.cond); $andl.push(this.tokenLocation( $and, undefined, 'and' )) } )*
2088
+ ( and=AND c2=conditionTerm { $args.push($c2.cond); $andl.push(this.valueWithTokenLocation( 'and', $and )) } )*
2022
2089
  ;
2023
2090
 
2024
2091
  conditionTerm returns [ cond ]
@@ -2027,38 +2094,38 @@ conditionTerm returns [ cond ]
2027
2094
  }
2028
2095
  :
2029
2096
  nt=NOT ct=conditionTerm
2030
- { $cond = { op: this.tokenLocation( $nt, undefined, 'not' ), args: [ $ct.cond ] }; }
2097
+ { $cond = { op: this.valueWithTokenLocation( 'not', $nt ), args: [ $ct.cond ] }; }
2031
2098
  |
2032
2099
  ex=EXISTS
2033
2100
  (
2034
2101
  open='(' qe=queryExpression close=')'
2035
- { $cond = { op: this.tokenLocation( $ex, undefined, 'exists' ),
2102
+ { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ),
2036
2103
  args: [ this.surroundByParens( $qe.query, $open, $close, true ) ] }; }
2037
2104
  |
2038
2105
  qm=( HideAlternatives | '?' )
2039
- { $cond = { op: this.tokenLocation( $ex, undefined, 'exists' ), args: [
2040
- { param: this.tokenLocation( $qm, undefined, '?' ), scope: 'param' }
2106
+ { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [
2107
+ { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' }
2041
2108
  ] };
2042
2109
  this.csnParseOnly( 'Dynamic parameter "?" is not supported', $qm );
2043
2110
  }
2044
2111
  |
2045
2112
  ep=valuePath[ 'ref' ]
2046
2113
  { $ep.qp['$'+'expected'] = 'exists';
2047
- $cond = { op: this.tokenLocation( $ex, undefined, 'exists' ), args: [ $ep.qp ] };
2114
+ $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [ $ep.qp ] };
2048
2115
  }
2049
2116
  )
2050
2117
  |
2051
2118
  expr=expression // see @after
2052
2119
  (
2053
2120
  rel=( '=' | '<>' | '>' | '>=' | '<' | '<=' | '!=' )
2054
- { $cond = { op: this.tokenLocation( $rel, undefined, $rel.text), args: [ $expr.expr ] }; }
2121
+ { $cond = { op: this.valueWithTokenLocation( $rel.text, $rel ), args: [ $expr.expr ] }; }
2055
2122
  ( asa=( ANY | SOME | ALL )
2056
- { $cond.quantifier = this.tokenLocation($asa, undefined, $asa.text.toLowerCase()); }
2123
+ { $cond.quantifier = this.valueWithTokenLocation( $asa.text.toLowerCase(), $asa ); }
2057
2124
  )?
2058
2125
  e2=expression { $cond.args.push($e2.expr); }
2059
2126
  |
2060
2127
  IS ( inn=NOT NULL | innu=NULL )
2061
- { $cond = { op: $inn ? this.tokenLocation( $inn, undefined, 'isNotNull' ) : this.tokenLocation( $innu, undefined, 'isNull' ), args: [ $expr.expr ] }; }
2128
+ { $cond = { op: $inn ? this.valueWithTokenLocation( 'isNotNull', $inn ) : this.valueWithTokenLocation( 'isNull', $innu ), args: [ $expr.expr ] }; }
2062
2129
  |
2063
2130
  { $cond = { args: [ $expr.expr ] }; }
2064
2131
  NOT predicate[ $cond, true ]
@@ -2074,14 +2141,14 @@ predicate[ cond, negated ]
2074
2141
  // NOT (a BETWEEN b AND c)
2075
2142
  :
2076
2143
  ino=IN e1=expression // including ExpressionList
2077
- { $cond.op = this.tokenLocation( $ino, undefined, (negated) ? 'notIn' : 'in'); $cond.args.push( $e1.expr ); }
2144
+ { $cond.op = this.valueWithTokenLocation( (negated) ? 'notIn' : 'in', $ino ); $cond.args.push( $e1.expr ); }
2078
2145
  |
2079
2146
  bw=BETWEEN e2=expression
2080
- { $cond.op = this.tokenLocation( $bw, undefined, (negated) ? 'notBetween' : 'between' ); $cond.args.push( $e2.expr ); }
2147
+ { $cond.op = this.valueWithTokenLocation( (negated) ? 'notBetween' : 'between', $bw ); $cond.args.push( $e2.expr ); }
2081
2148
  AND e3=expression { $cond.args.push( $e3.expr ); }
2082
2149
  |
2083
2150
  lk=LIKE e4=expression
2084
- { $cond.op = this.tokenLocation( $lk, undefined, (negated) ? 'notLike' : 'like' ); $cond.args.push( $e4.expr ); }
2151
+ { $cond.op = this.valueWithTokenLocation( (negated) ? 'notLike' : 'like', $lk ); $cond.args.push( $e4.expr ); }
2085
2152
  ( ESCAPE e5=expression { $cond.args.push( $e5.expr ); } )?
2086
2153
  ;
2087
2154
 
@@ -2093,7 +2160,7 @@ expression returns [ expr ]
2093
2160
  or='||' e2=expressionSum
2094
2161
  {
2095
2162
  $expr = {
2096
- op: this.tokenLocation( $or, undefined, '||' ), args: [$expr, $e2.expr],
2163
+ op: this.valueWithTokenLocation( '||', $or ), args: [$expr, $e2.expr],
2097
2164
  location: this.combinedLocation( $expr, $e2.expr ) };
2098
2165
  }
2099
2166
  )*
@@ -2107,7 +2174,7 @@ expressionSum returns [ expr ]
2107
2174
  op=( '+' | '-' ) e2=expressionFactor
2108
2175
  {
2109
2176
  $expr = {
2110
- op: this.tokenLocation($op, undefined, $op.text), args: [$expr, $e2.expr],
2177
+ op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
2111
2178
  location: this.combinedLocation( $expr, $e2.expr ) };
2112
2179
  }
2113
2180
  )*
@@ -2121,7 +2188,7 @@ expressionFactor returns [ expr ]
2121
2188
  op=( '*' | '/' ) e2=expressionTerm
2122
2189
  {
2123
2190
  $expr = {
2124
- op: this.tokenLocation($op, undefined, $op.text), args: [$expr, $e2.expr],
2191
+ op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
2125
2192
  location: this.combinedLocation( $expr, $e2.expr ) };
2126
2193
  }
2127
2194
  )*
@@ -2141,7 +2208,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2141
2208
  { $expr = $sf.ret; }
2142
2209
  |
2143
2210
  ca=CASE
2144
- { $expr = { op : this.tokenLocation( $ca, undefined, 'case' ), args: [] }; }
2211
+ { $expr = { op : this.valueWithTokenLocation( 'case', $ca ), args: [] }; }
2145
2212
  (
2146
2213
  e2=expression { $expr.args.push($e2.expr); }
2147
2214
  ( ow=WHEN ew=expression THEN e3=expression
@@ -2159,7 +2226,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2159
2226
  |
2160
2227
  ne=NEW nqp=valuePath[ 'ref', null] // token rewrite for NEW
2161
2228
  // please note: there will be no compiler-supported code completion after NEW
2162
- { $expr = { op: this.tokenLocation( $ne, undefined, 'new' ), args: [] };
2229
+ { $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
2163
2230
  this.notSupportedYet( $ne ); }
2164
2231
  |
2165
2232
  vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
@@ -2178,7 +2245,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2178
2245
  )
2179
2246
  |
2180
2247
  qm=( HideAlternatives | '?' )
2181
- { $expr = { param: this.tokenLocation( $qm, undefined, '?' ), scope: 'param' };
2248
+ { $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
2182
2249
  this.csnParseOnly( 'Dynamic parameter "?" is not supported', $qm );
2183
2250
  }
2184
2251
  |
@@ -2196,7 +2263,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2196
2263
  close=')'
2197
2264
  {
2198
2265
  if ($expr.length > 1)
2199
- $expr = { op: this.tokenLocation( $open, undefined, ',' ), args: $expr };
2266
+ $expr = { op: this.valueWithTokenLocation( ',', $open ), args: $expr };
2200
2267
  else if ($expr[0]) // can be `null` if condition failed to parse
2201
2268
  $expr = this.surroundByParens( $expr[0], $open, $close );
2202
2269
  }
@@ -2236,7 +2303,7 @@ specialFunction returns [ ret = { } ] locals[ art = {} ]
2236
2303
  ca=CAST open='('
2237
2304
  {
2238
2305
  $ret = {
2239
- op: this.tokenLocation( $ca, undefined, 'cast' ),
2306
+ op: this.valueWithTokenLocation( 'cast', $ca ),
2240
2307
  args: [ ],
2241
2308
  location: this.tokenLocation( $ca )
2242
2309
  };
@@ -2306,15 +2373,15 @@ pathArguments[ pathStep, considerSpecial ]
2306
2373
  funcExpression[ $pathStep, $considerSpecial ]
2307
2374
  )*
2308
2375
  |
2309
- a=ALL { $pathStep.quantifier = this.tokenLocation( $a, undefined, 'all' ); }
2376
+ a=ALL { $pathStep.quantifier = this.valueWithTokenLocation( 'all', $a ); }
2310
2377
  e1=expression { $pathStep.args = [ $e1.expr ]; }
2311
2378
  |
2312
- d=DISTINCT { $pathStep.quantifier = this.tokenLocation( $d, undefined, 'distinct' ); }
2379
+ d=DISTINCT { $pathStep.quantifier = this.valueWithTokenLocation( 'distinct', $d ); }
2313
2380
  e1=expression { $pathStep.args = [ $e1.expr ]; }
2314
2381
  ( ',' e2=expression { $pathStep.args.push( $e2.expr ); } )*
2315
2382
  |
2316
2383
  star='*'
2317
- { $pathStep.args = [ { location: this.tokenLocation($star), val: '*', literal: 'token' } ]; }
2384
+ { $pathStep.args = [ { location: this.tokenLocation( $star ), val: '*', literal: 'token' } ]; }
2318
2385
  |
2319
2386
  { $pathStep.args = []; }
2320
2387
  )
@@ -2388,7 +2455,7 @@ optionalWhereForFilter
2388
2455
 
2389
2456
  // Simple paths and values ---------------------------------------------------
2390
2457
 
2391
- annoValueBase returns[ val ] locals [ hasEllipsis=0 ]
2458
+ annoValueBase returns[ val ] locals [ seenEllipsis = false ]
2392
2459
  @after { this.attachLocation($val); }
2393
2460
  :
2394
2461
  { $val = { literal: 'struct', location: this.startLocation() }; }
@@ -2408,30 +2475,43 @@ annoValueBase returns[ val ] locals [ hasEllipsis=0 ]
2408
2475
  '['
2409
2476
  (
2410
2477
  (
2411
- head=arrayValue { $val.val.push( $head.val ); }
2412
- |
2413
- e='...'
2414
- {
2415
- $val.val.push( { literal: 'token', val: '...', location: this.tokenLocation($e) } );
2416
- $hasEllipsis++;
2417
- }
2478
+ head=arrayValue { $val.val.push( $head.val ); }
2479
+ |
2480
+ e='...' ( UP TO upTo=arrayValue )?
2481
+ {{
2482
+ const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2483
+ $val.val.push( item );
2484
+ if ($ctx.upTo) item.upTo = $upTo.val;
2485
+ $seenEllipsis = !$ctx.upTo || 'upTo';
2486
+ }}
2418
2487
  )
2419
2488
  (
2420
2489
  ',' { if (this.isStraightBefore(']')) break; } // allow ',' before ']'
2421
2490
  (
2422
- tail=arrayValue { $val.val.push( $tail.val ); }
2423
- |
2424
- e='...'
2425
- {
2426
- $val.val.push( { literal: 'token', val: '...', location: this.tokenLocation($e) } );
2427
- if(++$hasEllipsis > 1)
2428
- this.error( 'syntax-unexpected-ellipsis', $e, { code: '...' },
2429
- 'Expected no more than one $(CODE)' );
2430
- }
2491
+ tail=arrayValue { $val.val.push( $tail.val ); }
2492
+ |
2493
+ { $ctx.upTo = null; } // is not reset
2494
+ e='...' ( UP TO upTo=arrayValue )?
2495
+ {{
2496
+ const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2497
+ if ($ctx.upTo) item.upTo = $upTo.val;
2498
+ $val.val.push( item );
2499
+ if ($seenEllipsis === true) // TODO: adapt msg to UP TO
2500
+ this.error( 'syntax-unexpected-ellipsis', $e, { code: '...' },
2501
+ 'Expected no more than one $(CODE)' );
2502
+ else
2503
+ $seenEllipsis = !$ctx.upTo || 'upTo';
2504
+ }}
2431
2505
  )
2432
2506
  )*
2433
2507
  )?
2434
- ']'
2508
+ cb=']'
2509
+ {
2510
+ if ($seenEllipsis === 'upTo')
2511
+ this.error( 'syntax-expecting-ellipsis', $cb, // at closing bracket
2512
+ { code: '... up to', newcode: '...' },
2513
+ 'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
2514
+ }
2435
2515
  |
2436
2516
  v1=literalValue { $val = $v1.val; }
2437
2517
  |
@@ -2602,6 +2682,7 @@ ident[ category ] returns[ id ]
2602
2682
  | COMPOSITION
2603
2683
  | CONTEXT
2604
2684
  | CROSS
2685
+ | CURRENT
2605
2686
  | DAY
2606
2687
  | DEFAULT
2607
2688
  | DEFINE
@@ -2619,6 +2700,7 @@ ident[ category ] returns[ id ]
2619
2700
  | EXTEND
2620
2701
  | FIRST
2621
2702
  | FLOATING
2703
+ | FOLLOWING
2622
2704
  | FULL
2623
2705
  | FUNCTION
2624
2706
  | GROUP
@@ -2650,18 +2732,23 @@ ident[ category ] returns[ id ]
2650
2732
  | OUTER
2651
2733
  | PARAMETERS
2652
2734
  | PARTITION
2735
+ | PRECEDING
2653
2736
  | PROJECTION
2654
2737
  | REDIRECTED
2655
2738
  | RETURNS
2656
2739
  | RIGHT
2740
+ | ROW
2741
+ | ROWS
2657
2742
  | SECOND
2658
2743
  | SERVICE
2659
2744
  | THEN
2660
2745
  | TRAILING
2661
2746
  | UNION
2747
+ | UP
2662
2748
  | TO
2663
2749
  | TYPE
2664
2750
  | USING
2751
+ | UNBOUNDED
2665
2752
  | VARIABLE
2666
2753
  | VIEW
2667
2754
  | YEAR
@@ -2682,9 +2769,40 @@ LineComment : '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
2682
2769
 
2683
2770
  // Values --------------------------------------------------------------------
2684
2771
 
2685
- String
2772
+ // for syntactic code-completion: Combine all three string styles
2773
+ // Note: Use rule `string` instead as that also parses escape sequences!
2774
+ String : SingleLineString
2775
+ | MultiLineString
2776
+ | MutlLineStringBlock;
2777
+
2778
+ fragment SingleLineString
2686
2779
  :
2687
- ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ // \u0027 = '\''
2780
+ // \u0027 = '\''
2781
+ // \u2028 = LS (Line Separator)
2782
+ // \u2029 = PS (Paragraph Separator)
2783
+ ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ //
2784
+ ;
2785
+
2786
+ fragment MultiLineString
2787
+ :
2788
+ ('`' ( MultiLineStringContentChar | EscapeSequence )* '`' )
2789
+ ;
2790
+
2791
+ fragment MutlLineStringBlock
2792
+ :
2793
+ ('```' ( MultiLineStringContentChar | EscapeSequence )* '```')
2794
+ ;
2795
+
2796
+ fragment EscapeSequence
2797
+ :
2798
+ // we could list each escape sequence explicitly, but we already
2799
+ // decode them in genericAntlrParser.js, so no need to do work twice.
2800
+ '\\' .
2801
+ ;
2802
+
2803
+ fragment MultiLineStringContentChar
2804
+ :
2805
+ (~[\u0060\\]) // \u0060 = '`'
2688
2806
  ;
2689
2807
 
2690
2808
  QuotedLiteral
@@ -2693,10 +2811,15 @@ QuotedLiteral
2693
2811
  ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ // \u0027 = '\''
2694
2812
  ;
2695
2813
 
2814
+ // This literal improves error messages for unterminated literals.
2696
2815
  UnterminatedLiteral
2697
2816
  :
2698
2817
  ( [xX] | [dD][aA][tT][eE] | [tT][iI][mM][eE] ( [sS][tT][aA][mM][pP] )? )?
2699
2818
  '\'' ~[\u0027\n\r\u2028\u2029]* // \u0027 = '\''
2819
+ |
2820
+ ('`' ( MultiLineStringContentChar | EscapeSequence )* )
2821
+ |
2822
+ ('```' ( MultiLineStringContentChar | EscapeSequence )* )
2700
2823
  ;
2701
2824
 
2702
2825
  UnterminatedDelimitedIdentifier
@@ -2763,6 +2886,7 @@ BOTH : [bB][oO][tT][hH] ;
2763
2886
  COMPOSITION : [cC][oO][mM][pP][oO][sS][iI][tT][iI][oO][nN] ;
2764
2887
  CONTEXT : [cC][oO][nN][tT][eE][xX][tT] ;
2765
2888
  CROSS : [cC][rR][oO][sS][sS] ;
2889
+ CURRENT : [cC][uU][rR][rR][eE][nN][tT] ;
2766
2890
  DAY : [dD][aA][yY] ;
2767
2891
  DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
2768
2892
  DEFINE : [dD][eE][fF][iI][nN][eE] ;
@@ -2780,6 +2904,7 @@ EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
2780
2904
  EXTEND : [eE][xX][tT][eE][nN][dD] ;
2781
2905
  FIRST : [fF][iI][rR][sS][tT] ;
2782
2906
  FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
2907
+ FOLLOWING : [fF][oO][lL][lL][oO][wW][iI][nN][gG] ;
2783
2908
  FULL : [fF][uU][lL][lL] ;
2784
2909
  FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
2785
2910
  GROUP : [gG][rR][oO][uU][pP] ;
@@ -2812,10 +2937,13 @@ OUTER : [oO][uU][tT][eE][rR] ;
2812
2937
  // OVER : [oO][vV][eE][rR] ;
2813
2938
  PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
2814
2939
  PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
2940
+ PRECEDING: [pP][rR][eE][cC][eE][dD][iI][nN][gG] ;
2815
2941
  PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
2816
2942
  REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
2817
2943
  RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
2818
2944
  RIGHT : [rR][iI][gG][hH][tT] ;
2945
+ ROW : [rR][oO][wW] ;
2946
+ ROWS : [rR][oO][wW][sS] ;
2819
2947
  SECOND : [sS][eE][cC][oO][nN][dD] ;
2820
2948
  SERVICE : [sS][eE][rR][vV][iI][cC][eE] ;
2821
2949
  THEN : [tT][hH][eE][nN] ;
@@ -2823,6 +2951,8 @@ TRAILING : [tT][rR][aA][iI][lL][iI][nN][gG] ;
2823
2951
  TO : [tT][oO] ; // or make reserved? (is in SQL-92)
2824
2952
  TYPE : [tT][yY][pP][eE] ;
2825
2953
  UNION : [uU][nN][iI][oO][nN] ;
2954
+ UNBOUNDED : [uU][nN][bB][oO][uU][nN][dD][eE][dD] ;
2955
+ UP : [uU][pP] ;
2826
2956
  USING : [uU][sS][iI][nN][gG] ;
2827
2957
  VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
2828
2958
  VIEW : [vV][iI][eE][wW] ;