@sap/cds-compiler 2.7.0 → 2.11.2
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 +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
|
@@ -162,6 +162,17 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
|
|
|
162
162
|
if (rulespec.$frontend)
|
|
163
163
|
ast.$frontend = rulespec.$frontend;
|
|
164
164
|
|
|
165
|
+
// Warn about unused doc-comments.
|
|
166
|
+
// Do not warn if docComments are explicitly disabled.
|
|
167
|
+
if (options.docComment !== false) {
|
|
168
|
+
for (const token of tokenStream.tokens) {
|
|
169
|
+
if (token.channel === antlr4.Token.HIDDEN_CHANNEL && token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
170
|
+
messageFunctions.info('syntax-ignoring-doc-comment', parser.multiLineTokenLocation(token), {},
|
|
171
|
+
"Ignoring doc-comment as it does not belong to any artifact");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
165
176
|
// TODO: clarify with LSP colleagues: still necessary?
|
|
166
177
|
if (parser.messages) {
|
|
167
178
|
Object.defineProperty( ast, 'messages',
|
|
@@ -49,6 +49,8 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
49
49
|
attachLocation,
|
|
50
50
|
startLocation,
|
|
51
51
|
tokenLocation,
|
|
52
|
+
multiLineTokenLocation,
|
|
53
|
+
previousTokenAtLocation,
|
|
52
54
|
combinedLocation,
|
|
53
55
|
surroundByParens,
|
|
54
56
|
unaryOpForParens,
|
|
@@ -77,6 +79,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
77
79
|
noAssignmentInSameLine,
|
|
78
80
|
noSemicolonHere,
|
|
79
81
|
setLocalToken,
|
|
82
|
+
setLocalTokenIfBefore,
|
|
80
83
|
excludeExpected,
|
|
81
84
|
isStraightBefore,
|
|
82
85
|
meltKeywordToIdentifier,
|
|
@@ -183,6 +186,14 @@ function setLocalToken( string, tokenName, notBefore, inSameLine ) {
|
|
|
183
186
|
ll1.type = this.constructor[tokenName];
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
|
|
190
|
+
const ll1 = this.getCurrentToken();
|
|
191
|
+
if (ll1.text.toUpperCase() === string &&
|
|
192
|
+
(!inSameLine || this._input.LT(-1).line === ll1.line) &&
|
|
193
|
+
(!before || before && before.test( this._input.LT(2).text )))
|
|
194
|
+
ll1.type = this.constructor[tokenName];
|
|
195
|
+
}
|
|
196
|
+
|
|
186
197
|
// // Special function for rule `requiredSemi` before return $ctx
|
|
187
198
|
// function braceForSemi() {
|
|
188
199
|
// if (RBRACE == null)
|
|
@@ -236,9 +247,12 @@ function prepareGenericKeywords( pathItem ) {
|
|
|
236
247
|
// TODO: If not just at the beginning, we need a stack for $genericKeywords,
|
|
237
248
|
// as we can have nested special functions
|
|
238
249
|
this.$genericKeywords.argFull = Object.keys( spec );
|
|
250
|
+
// @ts-ignore
|
|
239
251
|
const token = this.getCurrentToken() || { text: '' };
|
|
240
|
-
if (spec[token.text.toUpperCase()] === 'argFull')
|
|
252
|
+
if (spec[token.text.toUpperCase()] === 'argFull') {
|
|
253
|
+
// @ts-ignore
|
|
241
254
|
token.type = this.constructor.GenericArgFull;
|
|
255
|
+
}
|
|
242
256
|
}
|
|
243
257
|
|
|
244
258
|
// Attach location matched by current rule to node `art`. If a location is
|
|
@@ -288,7 +302,7 @@ function tokenLocation( token, endToken, val ) {
|
|
|
288
302
|
file: this.filename,
|
|
289
303
|
line: token.line,
|
|
290
304
|
col: token.column + 1,
|
|
291
|
-
// we only have single-line tokens
|
|
305
|
+
// we only have single-line tokens, except for docComments
|
|
292
306
|
endLine: endToken.line,
|
|
293
307
|
endCol: endToken.stop - endToken.start + endToken.column + 2, // after the last char (special for EOF?)
|
|
294
308
|
};
|
|
@@ -297,6 +311,51 @@ function tokenLocation( token, endToken, val ) {
|
|
|
297
311
|
return r;
|
|
298
312
|
}
|
|
299
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Return location of `token`. In contrast to `tokenLocation()`, this function
|
|
316
|
+
* can handle multiline tokens.
|
|
317
|
+
*/
|
|
318
|
+
function multiLineTokenLocation(token, val) {
|
|
319
|
+
if (!token)
|
|
320
|
+
return undefined;
|
|
321
|
+
|
|
322
|
+
// Count the number of newlines in the token.
|
|
323
|
+
// TODO: I want to avoid a substring, that's why I don't use RegEx here
|
|
324
|
+
const source = token.source[1].data;
|
|
325
|
+
let newLineCount = 0;
|
|
326
|
+
let lastNewlineIndex = token.start;
|
|
327
|
+
for (let i = token.start; i < token.stop; i++) {
|
|
328
|
+
if (source[i] === 10) { // ASCII code for '\n'
|
|
329
|
+
newLineCount++;
|
|
330
|
+
lastNewlineIndex = i;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (newLineCount === 0)
|
|
334
|
+
// endCol calculation below requires at least one newLine.
|
|
335
|
+
return this.tokenLocation(token, token, val);
|
|
336
|
+
|
|
337
|
+
/** @type {CSN.Location} */
|
|
338
|
+
const r = {
|
|
339
|
+
file: this.filename,
|
|
340
|
+
line: token.line,
|
|
341
|
+
col: token.column + 1,
|
|
342
|
+
endLine: token.line + newLineCount,
|
|
343
|
+
endCol: token.stop - lastNewlineIndex + 1, // after the last char (special for EOF?)
|
|
344
|
+
};
|
|
345
|
+
if (val !== undefined)
|
|
346
|
+
return { location: r, val };
|
|
347
|
+
return r;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function previousTokenAtLocation( location ) {
|
|
351
|
+
let k = -1;
|
|
352
|
+
let token = this._input.LT(k);
|
|
353
|
+
while (token.line > location.line ||
|
|
354
|
+
token.line === location.line && token.column >= location.col)
|
|
355
|
+
token = this._input.LT(--k);
|
|
356
|
+
return (token.line === location.line && token.column + 1 === location.col) && token;
|
|
357
|
+
}
|
|
358
|
+
|
|
300
359
|
// Create a location with location properties `filename` and `start` from
|
|
301
360
|
// argument `start`, and location property `end` from argument `end`.
|
|
302
361
|
function combinedLocation( start, end ) {
|
|
@@ -333,16 +392,20 @@ function unaryOpForParens( query, val ) {
|
|
|
333
392
|
// - would influence the prediction, probably even induce adaptivePredict() calls,
|
|
334
393
|
// - is only slightly "more declarative" in the grammar.
|
|
335
394
|
function docComment( node ) {
|
|
336
|
-
if (!this.options.docComment)
|
|
337
|
-
return;
|
|
338
395
|
const token = this._input.getHiddenTokenToLeft( this.constructor.DocComment );
|
|
339
396
|
if (!token)
|
|
340
397
|
return;
|
|
398
|
+
|
|
399
|
+
// This token is actually used by / assigned to an artifact.
|
|
400
|
+
token.isUsed = true;
|
|
401
|
+
|
|
402
|
+
if (!this.options.docComment)
|
|
403
|
+
return;
|
|
341
404
|
if (node.doc) {
|
|
342
405
|
this.warning( 'syntax-duplicate-doc-comment', token, {},
|
|
343
406
|
'Repeated doc comment - previous doc is replaced' );
|
|
344
407
|
}
|
|
345
|
-
node.doc = this.
|
|
408
|
+
node.doc = this.multiLineTokenLocation( token, parseDocComment( token.text ) );
|
|
346
409
|
}
|
|
347
410
|
|
|
348
411
|
// Classify token (identifier category) for implicit names,
|
|
@@ -426,14 +489,18 @@ function valuePathAst( ref ) {
|
|
|
426
489
|
path.length = 1;
|
|
427
490
|
}
|
|
428
491
|
const { args, id, location } = path[0];
|
|
429
|
-
if (args
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
492
|
+
if (args
|
|
493
|
+
? path[0].$syntax === ':'
|
|
494
|
+
: path[0].$delimited || !functionsWithoutParens.includes( id.toUpperCase() ))
|
|
495
|
+
return ref;
|
|
496
|
+
|
|
497
|
+
const implicit = this.previousTokenAtLocation( location );
|
|
498
|
+
if (implicit && implicit.isIdentifier)
|
|
499
|
+
implicit.isIdentifier = 'func';
|
|
500
|
+
const op = { location, val: 'call' };
|
|
501
|
+
return (args)
|
|
502
|
+
? { op, func: ref, location: ref.location, args }
|
|
503
|
+
: { op, func: ref, location: ref.location };
|
|
437
504
|
}
|
|
438
505
|
|
|
439
506
|
// If a '-' is directly before an unsigned number, consider it part of the number;
|
|
@@ -592,7 +659,7 @@ function addDef( parent, env, kind, name, annos, props, location ) {
|
|
|
592
659
|
// which could be tested in name search (then no undefined-ref error)
|
|
593
660
|
return art;
|
|
594
661
|
}
|
|
595
|
-
else if (env === 'artifacts') {
|
|
662
|
+
else if (env === 'artifacts' || env === 'vocabularies') {
|
|
596
663
|
dictAddArray( parent[env], art.name.id, art );
|
|
597
664
|
}
|
|
598
665
|
else if (kind || this.options.parseOnly) {
|
package/lib/language/language.g4
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// allow integers at certain places), one token type for non-quoted and
|
|
21
21
|
// quoted identifiers.
|
|
22
22
|
//
|
|
23
|
-
// * Keep the number of keywords as small as
|
|
23
|
+
// * Keep the number of keywords as small as possible. Thus, built-ins is a
|
|
24
24
|
// topic for the semantic analysis, not the grammar. Examples: no keywords
|
|
25
25
|
// for built-in types or built-in SQL functions. This also avoids noise in
|
|
26
26
|
// the grammar and a huge/slow generated parser.
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
// is not enough for a decision), write a comment starting with `#ATN:`
|
|
44
44
|
// which describes the ambiguity. Additionally, put a comment `/* #ATN n
|
|
45
45
|
// */` INSIDE an (`@after`) action of a rule if the corresponding function
|
|
46
|
-
// in '../gen/
|
|
46
|
+
// in '../gen/languageParser.js' contains `n` occurrences of
|
|
47
47
|
// `adaptivePredict` calls. This is checked in 'test/testCompiler.js',
|
|
48
48
|
// which also counts the total number of `adaptivePredict` occurrences.
|
|
49
49
|
// └─────────────────────────────────────────────────────────────────────────┘
|
|
@@ -55,6 +55,10 @@
|
|
|
55
55
|
// good idea anyway to avoid calls to `adaptivePredict`, see the rules
|
|
56
56
|
// starting with `annotationAssignment_`.
|
|
57
57
|
//
|
|
58
|
+
// * Factoring out a sub rule into a named rule influences the error recovery:
|
|
59
|
+
// the parser tries to consume all tokens which are neither in the follow
|
|
60
|
+
// set of loops and named rules. So be careful.
|
|
61
|
+
//
|
|
58
62
|
// * Do not use actions in the lexer. Examples: de-quote string literals not
|
|
59
63
|
// in the lexer, but in the parser; do not throw errors, but produce error
|
|
60
64
|
// tokens if necessary.
|
|
@@ -106,6 +110,9 @@
|
|
|
106
110
|
//
|
|
107
111
|
// * The ANTLR error "missing attribute access on rule reference c in $c" can
|
|
108
112
|
// be solved with using $ctx.c instead of $c
|
|
113
|
+
//
|
|
114
|
+
// * If you want to set a property starting with '$' like $syntax, use
|
|
115
|
+
// obj['$'+'syntax'] as the ANTLR tool would replace $syntax by $ctx.syntax
|
|
109
116
|
|
|
110
117
|
grammar language;
|
|
111
118
|
options {
|
|
@@ -114,6 +121,7 @@ options {
|
|
|
114
121
|
}
|
|
115
122
|
tokens {
|
|
116
123
|
VIRTUAL, // used with setLocalToken()
|
|
124
|
+
OVER, // used with setLocalTokenIfBefore()
|
|
117
125
|
HelperToken1, // used with setLocalToken(), does not appear in messages
|
|
118
126
|
HelperToken2, // used with setLocalToken(), does not appear in messages
|
|
119
127
|
HideAlternatives, // hide alternative tokens (no token seq!)
|
|
@@ -490,6 +498,8 @@ projectionSpec returns[ query ] locals[ src ]
|
|
|
490
498
|
fromPath[ $src, 'ref']
|
|
491
499
|
)?
|
|
492
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' ); }
|
|
493
503
|
bracedSelectItemListDef[ $query ]?
|
|
494
504
|
excludingClause[ $query ]?
|
|
495
505
|
;
|
|
@@ -818,7 +828,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
|
|
|
818
828
|
)*
|
|
819
829
|
')'
|
|
820
830
|
(
|
|
821
|
-
RETURNS '{'
|
|
831
|
+
RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
|
|
822
832
|
annotateElement[ $art ]*
|
|
823
833
|
'}'
|
|
824
834
|
optionalSemi
|
|
@@ -826,7 +836,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
|
|
|
826
836
|
requiredSemi
|
|
827
837
|
)
|
|
828
838
|
|
|
|
829
|
-
RETURNS '{'
|
|
839
|
+
RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
|
|
830
840
|
annotateElement[ $art ]*
|
|
831
841
|
'}'
|
|
832
842
|
optionalSemi
|
|
@@ -1124,13 +1134,7 @@ bracedSelectItemListDef[ query ]
|
|
|
1124
1134
|
'{'
|
|
1125
1135
|
{ if (!$query.columns) $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
1126
1136
|
(
|
|
1127
|
-
|
|
1128
|
-
{
|
|
1129
|
-
$query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
|
|
1130
|
-
}
|
|
1131
|
-
|
|
|
1132
|
-
selectItemDef[ $query.columns ]
|
|
1133
|
-
)
|
|
1137
|
+
selectItemDef[ $query.columns ]
|
|
1134
1138
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1135
1139
|
selectItemDef[ $query.columns ]
|
|
1136
1140
|
)*
|
|
@@ -1139,8 +1143,11 @@ bracedSelectItemListDef[ query ]
|
|
|
1139
1143
|
;
|
|
1140
1144
|
|
|
1141
1145
|
selectItemDef[ outer ] locals[ annos = [] ]
|
|
1142
|
-
@after{ this.attachLocation($art.art); }
|
|
1146
|
+
@after{ if ($ctx.art) this.attachLocation($art.art); }
|
|
1143
1147
|
:
|
|
1148
|
+
star='*'
|
|
1149
|
+
{ $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
|
|
1150
|
+
|
|
|
1144
1151
|
{ this.docComment( $annos ); }
|
|
1145
1152
|
annotationAssignment_atn[ $annos ]*
|
|
1146
1153
|
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
@@ -1159,6 +1166,8 @@ selectItemDefBody[ outer, annos ] returns[ art = {} ]
|
|
|
1159
1166
|
:
|
|
1160
1167
|
(
|
|
1161
1168
|
e=expression
|
|
1169
|
+
// we cannot use 'condition' instead, as long as we allow aliases without
|
|
1170
|
+
// AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
|
|
1162
1171
|
{
|
|
1163
1172
|
$art = this.addItem( $outer, null, null, $annos, { value: $e.expr } );
|
|
1164
1173
|
}
|
|
@@ -1218,11 +1227,7 @@ selectItemInlineList[ art, clause ]
|
|
|
1218
1227
|
'{'
|
|
1219
1228
|
{ $art[$clause] = []; }
|
|
1220
1229
|
(
|
|
1221
|
-
|
|
1222
|
-
{ $art[$clause].push( this.tokenLocation( $star, undefined, '*' ) ); }
|
|
1223
|
-
|
|
|
1224
|
-
selectItemInlineDef[ $art[$clause] ]
|
|
1225
|
-
)
|
|
1230
|
+
selectItemInlineDef[ $art[$clause] ]
|
|
1226
1231
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1227
1232
|
selectItemInlineDef[ $art[$clause] ]
|
|
1228
1233
|
)*
|
|
@@ -1231,8 +1236,11 @@ selectItemInlineList[ art, clause ]
|
|
|
1231
1236
|
;
|
|
1232
1237
|
|
|
1233
1238
|
selectItemInlineDef[ outer ] locals[ annos = [] ]
|
|
1234
|
-
@after{ this.attachLocation($art.art); }
|
|
1239
|
+
@after{ if ($ctx.art) this.attachLocation($art.art); }
|
|
1235
1240
|
:
|
|
1241
|
+
star='*'
|
|
1242
|
+
{ $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
|
|
1243
|
+
|
|
|
1236
1244
|
{ this.docComment( $annos ); }
|
|
1237
1245
|
annotationAssignment_atn[ $annos ]*
|
|
1238
1246
|
art=selectItemDefBody[ $outer, $annos ]
|
|
@@ -1471,8 +1479,20 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
|
|
|
1471
1479
|
head=Number
|
|
1472
1480
|
{ $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
|
|
1473
1481
|
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1474
|
-
|
|
1475
|
-
|
|
1482
|
+
(
|
|
1483
|
+
v=VARIABLE
|
|
1484
|
+
{ $art['$'+'typeArgs'].push(
|
|
1485
|
+
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1486
|
+
}
|
|
1487
|
+
|
|
|
1488
|
+
f=FLOATING
|
|
1489
|
+
{ $art['$'+'typeArgs'].push(
|
|
1490
|
+
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1491
|
+
}
|
|
1492
|
+
|
|
|
1493
|
+
tail=Number
|
|
1494
|
+
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1495
|
+
)
|
|
1476
1496
|
)*
|
|
1477
1497
|
')'
|
|
1478
1498
|
{ this.docComment( $annos ); }
|
|
@@ -1696,8 +1716,20 @@ typeRefOptArgs[ art ]
|
|
|
1696
1716
|
head=Number
|
|
1697
1717
|
{ $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
|
|
1698
1718
|
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1699
|
-
|
|
1700
|
-
|
|
1719
|
+
(
|
|
1720
|
+
v=VARIABLE
|
|
1721
|
+
{ $art['$'+'typeArgs'].push(
|
|
1722
|
+
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1723
|
+
}
|
|
1724
|
+
|
|
|
1725
|
+
f=FLOATING
|
|
1726
|
+
{ $art['$'+'typeArgs'].push(
|
|
1727
|
+
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1728
|
+
}
|
|
1729
|
+
|
|
|
1730
|
+
tail=Number
|
|
1731
|
+
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1732
|
+
)
|
|
1701
1733
|
)*
|
|
1702
1734
|
')'
|
|
1703
1735
|
|
|
|
@@ -1732,6 +1764,87 @@ orderByClause[ inQuery ] returns [ query ]
|
|
|
1732
1764
|
( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
|
|
1733
1765
|
;
|
|
1734
1766
|
|
|
1767
|
+
overOrderByClause returns [ expr ]
|
|
1768
|
+
:
|
|
1769
|
+
o=ORDER b=BY { $expr = { op: this.tokenLocation($o, $b, 'orderBy' ) , args: [] }}
|
|
1770
|
+
ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
|
|
1771
|
+
( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
|
|
1772
|
+
;
|
|
1773
|
+
|
|
1774
|
+
partitionByClause returns [ expr ]
|
|
1775
|
+
:
|
|
1776
|
+
p=PARTITION b=BY { $expr = { op: this.tokenLocation($p, $b, 'partitionBy' ) , args: [] }}
|
|
1777
|
+
e1=expression { $expr.args.push( $e1.expr ); }
|
|
1778
|
+
( ',' en=expression { $expr.args.push( $en.expr ); } )*
|
|
1779
|
+
;
|
|
1780
|
+
|
|
1781
|
+
windowFrameClause returns [ wf ]
|
|
1782
|
+
:
|
|
1783
|
+
r=ROWS { $wf = { op: this.tokenLocation($r, null, 'rows' ) , args: [] }}
|
|
1784
|
+
wfe=windowFrameExtentSpec { $wf.args.push( $wfe.wfe ); }
|
|
1785
|
+
;
|
|
1786
|
+
|
|
1787
|
+
windowFrameExtentSpec returns[ wfe ]
|
|
1788
|
+
:
|
|
1789
|
+
{ $wfe = {} }
|
|
1790
|
+
windowFrameStartSpec [ $wfe ]
|
|
1791
|
+
|
|
|
1792
|
+
b=BETWEEN
|
|
1793
|
+
{ $wfe = { op: this.tokenLocation( $b, null, 'frameBetween' ), args: [] } }
|
|
1794
|
+
wfb1=windowFrameBoundSpec { $wfe.args.push( $wfb1.wfb ); }
|
|
1795
|
+
AND
|
|
1796
|
+
wfb2=windowFrameBoundSpec { $wfe.args.push( $wfb2.wfb ); }
|
|
1797
|
+
;
|
|
1798
|
+
|
|
1799
|
+
windowFrameBoundSpec returns [ wfb ]
|
|
1800
|
+
@after{ /* #ATN 1 */ }
|
|
1801
|
+
:
|
|
1802
|
+
// #ATN: Not ll1 because `UNBOUNDED` could also be part of the windowFrameStartSpec
|
|
1803
|
+
// `UNBOUNDED` would then be immediately followed by `PRECEDING`
|
|
1804
|
+
u=UNBOUNDED f=FOLLOWING
|
|
1805
|
+
{ $wfb = { op: this.tokenLocation($u, $f, 'unboundedFollowing' ), args: []} }
|
|
1806
|
+
|
|
|
1807
|
+
// #ATN: Not ll1 because `Number` could also be part of the windowFrameStartSpec
|
|
1808
|
+
// `Number` would then be immediately followed by `PRECEDING`
|
|
1809
|
+
n=Number f=FOLLOWING
|
|
1810
|
+
{ $wfb = { op: this.tokenLocation($n, $f, 'following' ), args: [ this.numberLiteral( $n ) ]} }
|
|
1811
|
+
|
|
|
1812
|
+
{ $wfb = {} }
|
|
1813
|
+
windowFrameStartSpec [ $wfb ]
|
|
1814
|
+
;
|
|
1815
|
+
|
|
1816
|
+
windowFrameStartSpec [ wf ]
|
|
1817
|
+
:
|
|
1818
|
+
u=UNBOUNDED p=PRECEDING
|
|
1819
|
+
{
|
|
1820
|
+
$wf.op = this.tokenLocation($u, $p, 'unboundedPreceding' );
|
|
1821
|
+
$wf.args = [];
|
|
1822
|
+
}
|
|
1823
|
+
|
|
|
1824
|
+
n=Number p=PRECEDING
|
|
1825
|
+
{
|
|
1826
|
+
$wf.op = this.tokenLocation($p, null, 'preceding' );
|
|
1827
|
+
$wf.args = [ this.numberLiteral( $n ) ];
|
|
1828
|
+
}
|
|
1829
|
+
|
|
|
1830
|
+
c=CURRENT r=ROW
|
|
1831
|
+
{
|
|
1832
|
+
$wf.op = this.tokenLocation($c, $r, 'currentRow' );
|
|
1833
|
+
$wf.args = [];
|
|
1834
|
+
}
|
|
1835
|
+
;
|
|
1836
|
+
|
|
1837
|
+
overClause returns [ over ]
|
|
1838
|
+
@after { this.attachLocation($over); }
|
|
1839
|
+
:
|
|
1840
|
+
o=OVER { $over = { op: this.tokenLocation( $o, null, 'over' ) , args: [] } }
|
|
1841
|
+
'('
|
|
1842
|
+
( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
|
|
1843
|
+
( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
|
|
1844
|
+
( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
|
|
1845
|
+
')'
|
|
1846
|
+
;
|
|
1847
|
+
|
|
1735
1848
|
limitClause[ inQuery ] returns [ query ]
|
|
1736
1849
|
:
|
|
1737
1850
|
limkw=LIMIT { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
|
|
@@ -1790,13 +1903,7 @@ queryPrimary returns[ query = {} ]
|
|
|
1790
1903
|
{ $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
|
|
1791
1904
|
)?
|
|
1792
1905
|
{ $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
1793
|
-
|
|
1794
|
-
{
|
|
1795
|
-
$query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
|
|
1796
|
-
}
|
|
1797
|
-
|
|
|
1798
|
-
selectItemDef[ $query.columns ]
|
|
1799
|
-
)
|
|
1906
|
+
selectItemDef[ $query.columns ]
|
|
1800
1907
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1801
1908
|
selectItemDef[ $query.columns ]
|
|
1802
1909
|
)*
|
|
@@ -1900,6 +2007,8 @@ tableTerm returns [ table ]
|
|
|
1900
2007
|
// if we would use rule `ident`, we would either had to make all JOIN
|
|
1901
2008
|
// kinds reserved or introduce ATN
|
|
1902
2009
|
)?
|
|
2010
|
+
// ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
|
|
2011
|
+
{ if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
|
|
1903
2012
|
|
|
|
1904
2013
|
open='('
|
|
1905
2014
|
// #ATN: The following alternative is not LL1, because both can start with
|
|
@@ -1954,7 +2063,7 @@ fromPath[ qp, idkind ]
|
|
|
1954
2063
|
condition returns [ cond ] locals [ args = [], orl = [] ]
|
|
1955
2064
|
@after{
|
|
1956
2065
|
$cond = ($args.length == 1)
|
|
1957
|
-
? $args[0]
|
|
2066
|
+
? this.attachLocation( $args[0] )
|
|
1958
2067
|
: this.attachLocation({ op: $orl[0], args: $args });
|
|
1959
2068
|
}
|
|
1960
2069
|
:
|
|
@@ -2115,6 +2224,10 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
|
|
|
2115
2224
|
this.notSupportedYet( $ne ); }
|
|
2116
2225
|
|
|
|
2117
2226
|
vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
|
|
2227
|
+
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
2228
|
+
(
|
|
2229
|
+
over=overClause { $expr.suffix = [ $over.over ] }
|
|
2230
|
+
)?
|
|
2118
2231
|
|
|
|
2119
2232
|
':'
|
|
2120
2233
|
( vp=valuePath[ 'paramref', this.startLocation() ]
|
|
@@ -2315,7 +2428,7 @@ optionalCardinality[ pathStep ]
|
|
|
2315
2428
|
// completion just produces `:`after having inserted a Number - TODO.
|
|
2316
2429
|
{ if (this._input.LT(2).text !== ':') return $ctx; }
|
|
2317
2430
|
( trgMax=Number ':'
|
|
2318
|
-
{ if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ) } }
|
|
2431
|
+
{ if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ), location: this.startLocation() }; }
|
|
2319
2432
|
)
|
|
2320
2433
|
;
|
|
2321
2434
|
|
|
@@ -2550,6 +2663,7 @@ ident[ category ] returns[ id ]
|
|
|
2550
2663
|
| COMPOSITION
|
|
2551
2664
|
| CONTEXT
|
|
2552
2665
|
| CROSS
|
|
2666
|
+
| CURRENT
|
|
2553
2667
|
| DAY
|
|
2554
2668
|
| DEFAULT
|
|
2555
2669
|
| DEFINE
|
|
@@ -2566,6 +2680,8 @@ ident[ category ] returns[ id ]
|
|
|
2566
2680
|
| EXCLUDING
|
|
2567
2681
|
| EXTEND
|
|
2568
2682
|
| FIRST
|
|
2683
|
+
| FLOATING
|
|
2684
|
+
| FOLLOWING
|
|
2569
2685
|
| FULL
|
|
2570
2686
|
| FUNCTION
|
|
2571
2687
|
| GROUP
|
|
@@ -2596,10 +2712,14 @@ ident[ category ] returns[ id ]
|
|
|
2596
2712
|
| ORDER
|
|
2597
2713
|
| OUTER
|
|
2598
2714
|
| PARAMETERS
|
|
2715
|
+
| PARTITION
|
|
2716
|
+
| PRECEDING
|
|
2599
2717
|
| PROJECTION
|
|
2600
2718
|
| REDIRECTED
|
|
2601
2719
|
| RETURNS
|
|
2602
2720
|
| RIGHT
|
|
2721
|
+
| ROW
|
|
2722
|
+
| ROWS
|
|
2603
2723
|
| SECOND
|
|
2604
2724
|
| SERVICE
|
|
2605
2725
|
| THEN
|
|
@@ -2608,6 +2728,8 @@ ident[ category ] returns[ id ]
|
|
|
2608
2728
|
| TO
|
|
2609
2729
|
| TYPE
|
|
2610
2730
|
| USING
|
|
2731
|
+
| UNBOUNDED
|
|
2732
|
+
| VARIABLE
|
|
2611
2733
|
| VIEW
|
|
2612
2734
|
| YEAR
|
|
2613
2735
|
;
|
|
@@ -2708,6 +2830,7 @@ BOTH : [bB][oO][tT][hH] ;
|
|
|
2708
2830
|
COMPOSITION : [cC][oO][mM][pP][oO][sS][iI][tT][iI][oO][nN] ;
|
|
2709
2831
|
CONTEXT : [cC][oO][nN][tT][eE][xX][tT] ;
|
|
2710
2832
|
CROSS : [cC][rR][oO][sS][sS] ;
|
|
2833
|
+
CURRENT : [cC][uU][rR][rR][eE][nN][tT] ;
|
|
2711
2834
|
DAY : [dD][aA][yY] ;
|
|
2712
2835
|
DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
|
|
2713
2836
|
DEFINE : [dD][eE][fF][iI][nN][eE] ;
|
|
@@ -2724,6 +2847,8 @@ EXCEPT : [eE][xX][cC][eE][pP][tT] ;
|
|
|
2724
2847
|
EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
|
|
2725
2848
|
EXTEND : [eE][xX][tT][eE][nN][dD] ;
|
|
2726
2849
|
FIRST : [fF][iI][rR][sS][tT] ;
|
|
2850
|
+
FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
|
|
2851
|
+
FOLLOWING : [fF][oO][lL][lL][oO][wW][iI][nN][gG] ;
|
|
2727
2852
|
FULL : [fF][uU][lL][lL] ;
|
|
2728
2853
|
FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
|
|
2729
2854
|
GROUP : [gG][rR][oO][uU][pP] ;
|
|
@@ -2753,11 +2878,16 @@ ONE : [oO][nN][eE] ;
|
|
|
2753
2878
|
OR : [oO][rR] ;
|
|
2754
2879
|
ORDER : [oO][rR][dD][eE][rR] ;
|
|
2755
2880
|
OUTER : [oO][uU][tT][eE][rR] ;
|
|
2881
|
+
// OVER : [oO][vV][eE][rR] ;
|
|
2756
2882
|
PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
|
|
2883
|
+
PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
|
|
2884
|
+
PRECEDING: [pP][rR][eE][cC][eE][dD][iI][nN][gG] ;
|
|
2757
2885
|
PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
|
|
2758
2886
|
REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
|
|
2759
2887
|
RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
|
|
2760
2888
|
RIGHT : [rR][iI][gG][hH][tT] ;
|
|
2889
|
+
ROW : [rR][oO][wW] ;
|
|
2890
|
+
ROWS : [rR][oO][wW][sS] ;
|
|
2761
2891
|
SECOND : [sS][eE][cC][oO][nN][dD] ;
|
|
2762
2892
|
SERVICE : [sS][eE][rR][vV][iI][cC][eE] ;
|
|
2763
2893
|
THEN : [tT][hH][eE][nN] ;
|
|
@@ -2765,7 +2895,9 @@ TRAILING : [tT][rR][aA][iI][lL][iI][nN][gG] ;
|
|
|
2765
2895
|
TO : [tT][oO] ; // or make reserved? (is in SQL-92)
|
|
2766
2896
|
TYPE : [tT][yY][pP][eE] ;
|
|
2767
2897
|
UNION : [uU][nN][iI][oO][nN] ;
|
|
2898
|
+
UNBOUNDED : [uU][nN][bB][oO][uU][nN][dD][eE][dD] ;
|
|
2768
2899
|
USING : [uU][sS][iI][nN][gG] ;
|
|
2900
|
+
VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
|
|
2769
2901
|
VIEW : [vV][iI][eE][wW] ;
|
|
2770
2902
|
// VIRTUAL: [vV][iI][rR][tT][uU][aA][lL] ; see tokens {}
|
|
2771
2903
|
YEAR : [yY][eE][aA][rR] ;
|