@cloudcare/codemirror-dql 0.0.56 → 0.0.58
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/dist/cjs/complete/dql.terms.d.ts +6 -0
- package/dist/cjs/complete/dql.terms.js +60 -4
- package/dist/cjs/complete/dql.terms.js.map +1 -1
- package/dist/cjs/complete/hybrid.d.ts +2 -1
- package/dist/cjs/complete/hybrid.js +42 -5
- package/dist/cjs/complete/hybrid.js.map +1 -1
- package/dist/cjs/parser/parser.d.ts +16 -0
- package/dist/cjs/parser/parser.js +621 -31
- package/dist/cjs/parser/parser.js.map +1 -1
- package/dist/esm/complete/dql.terms.d.ts +6 -0
- package/dist/esm/complete/dql.terms.js +22 -1
- package/dist/esm/complete/dql.terms.js.map +1 -1
- package/dist/esm/complete/hybrid.d.ts +2 -1
- package/dist/esm/complete/hybrid.js +44 -7
- package/dist/esm/complete/hybrid.js.map +1 -1
- package/dist/esm/parser/parser.d.ts +16 -0
- package/dist/esm/parser/parser.js +584 -29
- package/dist/esm/parser/parser.js.map +1 -1
- package/package.json +3 -3
|
@@ -14,6 +14,7 @@ const queryClauseLabels = new Map([
|
|
|
14
14
|
['TargetClause', 'projection'],
|
|
15
15
|
['FilterClause', 'WHERE'],
|
|
16
16
|
['TimeClause', 'time range'],
|
|
17
|
+
['ShiftClause', 'SHIFT'],
|
|
17
18
|
['ByClause', 'BY'],
|
|
18
19
|
['HavingClause', 'HAVING'],
|
|
19
20
|
['OrderClause', 'ORDER BY'],
|
|
@@ -23,8 +24,142 @@ const queryClauseLabels = new Map([
|
|
|
23
24
|
['SLimitClause', 'SLIMIT'],
|
|
24
25
|
['SOffsetClause', 'SOFFSET'],
|
|
25
26
|
]);
|
|
26
|
-
const expressionParents = new Set(['BinaryExpr', 'Expr', 'HavingExpr', 'ProjectionExpr']);
|
|
27
|
+
const expressionParents = new Set(['BinaryExpr', 'Expr', 'HavingExpr', 'ProjectionExpr', 'CaseArithmeticExpr']);
|
|
27
28
|
const commaContextBoundaries = new Set(['CaseExpr', 'Query']);
|
|
29
|
+
const documentedFunctionArities = new Map([
|
|
30
|
+
...[
|
|
31
|
+
'abs',
|
|
32
|
+
'any',
|
|
33
|
+
'avg',
|
|
34
|
+
'bool',
|
|
35
|
+
'ceil',
|
|
36
|
+
'count',
|
|
37
|
+
'count_distinct',
|
|
38
|
+
'count_series',
|
|
39
|
+
'deriv',
|
|
40
|
+
'derivative',
|
|
41
|
+
'difference',
|
|
42
|
+
'distinct',
|
|
43
|
+
'field_values',
|
|
44
|
+
'first',
|
|
45
|
+
'float',
|
|
46
|
+
'floor',
|
|
47
|
+
'histogram_auto',
|
|
48
|
+
'increase',
|
|
49
|
+
'int',
|
|
50
|
+
'irate',
|
|
51
|
+
'last',
|
|
52
|
+
'last_row',
|
|
53
|
+
'length',
|
|
54
|
+
'log',
|
|
55
|
+
'log2',
|
|
56
|
+
'log10',
|
|
57
|
+
'lower',
|
|
58
|
+
'ltrim',
|
|
59
|
+
'max',
|
|
60
|
+
'md5',
|
|
61
|
+
'median',
|
|
62
|
+
'min',
|
|
63
|
+
'mode',
|
|
64
|
+
'non_negative_derivative',
|
|
65
|
+
'non_negative_difference',
|
|
66
|
+
'rate',
|
|
67
|
+
'rate_over_count',
|
|
68
|
+
'rate_over_sum',
|
|
69
|
+
'rtrim',
|
|
70
|
+
'set',
|
|
71
|
+
'spread',
|
|
72
|
+
'stddev',
|
|
73
|
+
'string',
|
|
74
|
+
'sum',
|
|
75
|
+
'trim',
|
|
76
|
+
'uint',
|
|
77
|
+
'unwrap',
|
|
78
|
+
'upper',
|
|
79
|
+
].map((name) => [name, { min: 1, max: 1 }]),
|
|
80
|
+
['bottom', { min: 2, max: 2 }],
|
|
81
|
+
['cidr', { min: 1, max: 2 }],
|
|
82
|
+
['collect', { min: 1, max: 2 }],
|
|
83
|
+
['collect_distinct', { min: 1, max: 2 }],
|
|
84
|
+
['concat', { min: 1, max: Number.POSITIVE_INFINITY }],
|
|
85
|
+
['count_filter', { min: 2, max: 2 }],
|
|
86
|
+
['default', { min: 2, max: 2 }],
|
|
87
|
+
['distinct_by_collapse', { min: 1, max: 2 }],
|
|
88
|
+
['drain', { min: 2, max: 3 }],
|
|
89
|
+
['exists', { min: 0, max: 0 }],
|
|
90
|
+
['fill', { min: 2, max: 2 }],
|
|
91
|
+
['histogram', { min: 4, max: 5 }],
|
|
92
|
+
['histogram_quantile', { min: 2, max: 2 }],
|
|
93
|
+
['match', { min: 1, max: 2 }],
|
|
94
|
+
['now', { min: 0, max: 0 }],
|
|
95
|
+
['query_string', { min: 1, max: 2 }],
|
|
96
|
+
['querystring', { min: 1, max: 2 }],
|
|
97
|
+
['re', { min: 1, max: 2 }],
|
|
98
|
+
['regexp', { min: 1, max: 2 }],
|
|
99
|
+
['regex', { min: 1, max: 2 }],
|
|
100
|
+
['regexp_extract', { min: 2, max: 3 }],
|
|
101
|
+
['regexp_extract_all', { min: 2, max: 3 }],
|
|
102
|
+
['regexp_replace', { min: 3, max: 3 }],
|
|
103
|
+
['round', { min: 1, max: 2 }],
|
|
104
|
+
['search', { min: 1, max: 2 }],
|
|
105
|
+
['substr', { min: 2, max: 3 }],
|
|
106
|
+
['top', { min: 2, max: 2 }],
|
|
107
|
+
['wildcard', { min: 1, max: 2 }],
|
|
108
|
+
]);
|
|
109
|
+
const stableShiftAggregateFunctions = new Set([
|
|
110
|
+
'any',
|
|
111
|
+
'avg',
|
|
112
|
+
'change_score',
|
|
113
|
+
'collect',
|
|
114
|
+
'collect_distinct',
|
|
115
|
+
'corr',
|
|
116
|
+
'count',
|
|
117
|
+
'count_distinct',
|
|
118
|
+
'count_filter',
|
|
119
|
+
'count_series',
|
|
120
|
+
'deriv',
|
|
121
|
+
'derivative',
|
|
122
|
+
'difference',
|
|
123
|
+
'ewma',
|
|
124
|
+
'first',
|
|
125
|
+
'histogram_quantile',
|
|
126
|
+
'increase',
|
|
127
|
+
'irate',
|
|
128
|
+
'last',
|
|
129
|
+
'last_row',
|
|
130
|
+
'mad_score',
|
|
131
|
+
'max',
|
|
132
|
+
'median',
|
|
133
|
+
'min',
|
|
134
|
+
'mode',
|
|
135
|
+
'moving_average',
|
|
136
|
+
'non_negative_derivative',
|
|
137
|
+
'non_negative_difference',
|
|
138
|
+
'p50',
|
|
139
|
+
'p75',
|
|
140
|
+
'p90',
|
|
141
|
+
'p95',
|
|
142
|
+
'p99',
|
|
143
|
+
'percentile',
|
|
144
|
+
'rate',
|
|
145
|
+
'rate_over_count',
|
|
146
|
+
'rate_over_sum',
|
|
147
|
+
'slope',
|
|
148
|
+
'spread',
|
|
149
|
+
'stddev',
|
|
150
|
+
'sum',
|
|
151
|
+
'zscore',
|
|
152
|
+
]);
|
|
153
|
+
const unstableShiftAggregateFunctions = new Set([
|
|
154
|
+
'distinct',
|
|
155
|
+
'distinct_by_collapse',
|
|
156
|
+
'drain',
|
|
157
|
+
'field_values',
|
|
158
|
+
'histogram',
|
|
159
|
+
'histogram_auto',
|
|
160
|
+
'schema',
|
|
161
|
+
]);
|
|
162
|
+
const caseScalarFunctions = new Set(['int', 'float', 'string', 'md5', 'lower', 'upper', 'trim', 'ltrim', 'rtrim', 'regexp_replace', 'length']);
|
|
28
163
|
export const dqlSyntaxContexts = {
|
|
29
164
|
DQL: 'DQL query',
|
|
30
165
|
DQLConditions: 'DQL condition',
|
|
@@ -60,8 +195,15 @@ export const dqlSyntaxContexts = {
|
|
|
60
195
|
OuterArgItem: 'outer function argument',
|
|
61
196
|
Queted: 'quoted query delimiter',
|
|
62
197
|
EvalCallBody: 'eval function arguments',
|
|
198
|
+
EvalArgItem: 'eval expression argument',
|
|
199
|
+
EvalExpression: 'eval expression',
|
|
200
|
+
EvalAtom: 'eval expression value',
|
|
201
|
+
EvalFunctionCall: 'eval function call',
|
|
202
|
+
EvalFunctionCallBody: 'eval function arguments',
|
|
203
|
+
EvalFunctionArg: 'eval function argument',
|
|
63
204
|
FunctionCallBody: 'function arguments',
|
|
64
205
|
ArgItem: 'function argument',
|
|
206
|
+
EvalFieldReference: 'eval field reference',
|
|
65
207
|
InValue: 'argument value list',
|
|
66
208
|
SingleExpr: 'expression value',
|
|
67
209
|
Expr: 'expression',
|
|
@@ -78,6 +220,8 @@ export const dqlSyntaxContexts = {
|
|
|
78
220
|
ProjectionFieldFragment: 'projection field fragment',
|
|
79
221
|
ProjectionField: 'projection field',
|
|
80
222
|
SingleFieldExpr: 'projection expression value',
|
|
223
|
+
ShiftExpr: 'SHIFT expression',
|
|
224
|
+
ShiftOperand: 'SHIFT operand',
|
|
81
225
|
ProjectionExpr: 'projection expression',
|
|
82
226
|
AsExpr: 'alias',
|
|
83
227
|
FilterClause: 'filter clause',
|
|
@@ -91,10 +235,10 @@ export const dqlSyntaxContexts = {
|
|
|
91
235
|
CaseCondition: 'CASE condition',
|
|
92
236
|
CaseFilterExpr: 'CASE filter expression',
|
|
93
237
|
CasePredicate: 'CASE predicate',
|
|
238
|
+
CaseComparableValue: 'CASE comparison value',
|
|
94
239
|
CaseValue: 'CASE value',
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
CaseFunctionCallBody: 'CASE function arguments',
|
|
240
|
+
CaseArithmeticAtom: 'CASE arithmetic value',
|
|
241
|
+
CaseArithmeticExpr: 'CASE arithmetic expression',
|
|
98
242
|
FilterFragmentExpr: 'filter fragment',
|
|
99
243
|
ComparisonExpr: 'comparison expression',
|
|
100
244
|
LogicalFragmentExpr: 'logical filter expression',
|
|
@@ -105,6 +249,7 @@ export const dqlSyntaxContexts = {
|
|
|
105
249
|
FilterExpr: 'filter expression',
|
|
106
250
|
BinaryExpr: 'binary expression',
|
|
107
251
|
TimeClause: 'time range clause',
|
|
252
|
+
ShiftClause: 'SHIFT clause',
|
|
108
253
|
TimeRange: 'time range',
|
|
109
254
|
TimeExpr: 'time range value',
|
|
110
255
|
ByClause: 'BY clause',
|
|
@@ -124,6 +269,7 @@ export const dqlSyntaxContexts = {
|
|
|
124
269
|
};
|
|
125
270
|
const functionCallBodyParents = new Set([
|
|
126
271
|
'FunctionCallBody',
|
|
272
|
+
'EvalFunctionCallBody',
|
|
127
273
|
'EvalCallBody',
|
|
128
274
|
'OuterFunctionCallBody',
|
|
129
275
|
'RawOuterFunctionCallBody',
|
|
@@ -132,7 +278,6 @@ const functionCallBodyParents = new Set([
|
|
|
132
278
|
'FillOuterFunctionCallBody',
|
|
133
279
|
'VariadicOuterFunctionCallBody',
|
|
134
280
|
'RollupFunctionCallBody',
|
|
135
|
-
'CaseFunctionCallBody',
|
|
136
281
|
]);
|
|
137
282
|
export class Parser {
|
|
138
283
|
constructor(state, deadline = Date.now() + analysisBudgetMs) {
|
|
@@ -149,6 +294,7 @@ export class Parser {
|
|
|
149
294
|
this.quotedQueryDelimiterInfo = undefined;
|
|
150
295
|
this.caseResultDiagnostics = undefined;
|
|
151
296
|
this.caseResultRecoveryErrors = new Set();
|
|
297
|
+
this.caseShiftPresence = new Map();
|
|
152
298
|
}
|
|
153
299
|
getDiagnostics() {
|
|
154
300
|
const seen = new Set();
|
|
@@ -401,7 +547,7 @@ export class Parser {
|
|
|
401
547
|
this.appendDiagnostics(diagnostics);
|
|
402
548
|
}
|
|
403
549
|
describeError(node) {
|
|
404
|
-
var _a;
|
|
550
|
+
var _a, _b, _c;
|
|
405
551
|
const delimiterIssue = this.getDelimiterIssue();
|
|
406
552
|
if (delimiterIssue && delimiterIssue.kind !== 'unclosed' && this.isRelatedDelimiterError(node, delimiterIssue)) {
|
|
407
553
|
return null;
|
|
@@ -483,6 +629,26 @@ export class Parser {
|
|
|
483
629
|
}
|
|
484
630
|
return this.makeDiagnostic(node, 'expected "AS" before projection alias', group);
|
|
485
631
|
}
|
|
632
|
+
const projectionField = (_a = this.findAncestor(parent, 'ProjectionField')) !== null && _a !== void 0 ? _a : this.findAncestor(parent, 'ProjectionFieldFragment');
|
|
633
|
+
if (projectionField) {
|
|
634
|
+
const shiftError = this.errorDescendantsWithText(projectionField, 'shift').find((error) => error.from <= node.from);
|
|
635
|
+
if (shiftError && this.containsNode(projectionField, 'ShiftExpr')) {
|
|
636
|
+
return this.makeDiagnosticAt(shiftError.from, shiftError.to, 'SHIFT expressions cannot be nested', `shift-nested-syntax:${projectionField.from}`);
|
|
637
|
+
}
|
|
638
|
+
if (shiftError) {
|
|
639
|
+
const rawOperand = this.state.sliceDoc(projectionField.from, shiftError.from);
|
|
640
|
+
const leading = rawOperand.length - rawOperand.trimStart().length;
|
|
641
|
+
const trailing = rawOperand.length - rawOperand.trimEnd().length;
|
|
642
|
+
return this.makeDiagnosticAt(projectionField.from + leading, Math.max(projectionField.from + leading + 1, projectionField.from + rawOperand.length - trailing), 'SHIFT operand must be a stable aggregate or subquery measure', `shift-operand-syntax:${projectionField.from}`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const invalidShiftContext = (_b = this.findAncestor(parent, 'FilterClause')) !== null && _b !== void 0 ? _b : this.findAncestor(parent, 'HavingClause');
|
|
646
|
+
if (invalidShiftContext) {
|
|
647
|
+
const shiftError = this.errorDescendantsWithText(invalidShiftContext, 'shift').find((error) => error.from <= node.from);
|
|
648
|
+
if (shiftError) {
|
|
649
|
+
return this.makeDiagnosticAt(shiftError.from, shiftError.to, 'expression-level SHIFT is only allowed in the projection', `shift-context-syntax:${invalidShiftContext.from}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
486
652
|
if (parentName === 'OuterFunctionCall') {
|
|
487
653
|
return this.makeUnexpectedTokenDiagnostic(node, dqlSyntaxContexts.OuterFunctionCall, group);
|
|
488
654
|
}
|
|
@@ -507,7 +673,7 @@ export class Parser {
|
|
|
507
673
|
}
|
|
508
674
|
return this.makeUnexpectedTokenDiagnostic(node, 'function call', group);
|
|
509
675
|
}
|
|
510
|
-
if (parentName === 'ArgItem' && node.from === node.to) {
|
|
676
|
+
if ((parentName === 'ArgItem' || parentName === 'EvalFunctionArg') && node.from === node.to) {
|
|
511
677
|
if ((previous === null || previous === void 0 ? void 0 : previous.name) === 'EqlSingle') {
|
|
512
678
|
return this.makeDiagnostic(node, 'function argument value expected after "="', group);
|
|
513
679
|
}
|
|
@@ -553,19 +719,51 @@ export class Parser {
|
|
|
553
719
|
if (parentName === 'ByExpr' && errorText === ',') {
|
|
554
720
|
return this.makeDiagnostic(node, 'grouping field expected before ","', group);
|
|
555
721
|
}
|
|
556
|
-
if (expressionParents.has(parentName)) {
|
|
557
|
-
if (previous && this.isOperator(previous)) {
|
|
558
|
-
return this.makeDiagnostic(node, `right-hand expression expected after "${previousText}"`, group);
|
|
559
|
-
}
|
|
560
|
-
return this.makeUnexpectedTokenDiagnostic(node, 'expression', group);
|
|
561
|
-
}
|
|
562
722
|
const caseWhenClause = this.findAncestor(parent, 'CaseWhenClause');
|
|
563
723
|
if (caseWhenClause) {
|
|
724
|
+
const firstCaseError = this.firstErrorFrom(caseWhenClause);
|
|
564
725
|
const missingCondition = /^\s*when\s+(then)\b/i.exec(this.maskLineComments(this.nodeText(caseWhenClause)));
|
|
565
726
|
if (missingCondition) {
|
|
566
|
-
|
|
567
|
-
|
|
727
|
+
if (node.from === firstCaseError) {
|
|
728
|
+
const thenOffset = missingCondition.index + missingCondition[0].toLowerCase().lastIndexOf('then');
|
|
729
|
+
return this.makeDiagnosticAt(caseWhenClause.from + thenOffset, caseWhenClause.from + thenOffset + 4, 'CASE condition expected after "WHEN"', this.caseGroup(caseWhenClause));
|
|
730
|
+
}
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
if (!caseWhenClause.getChild('Then')) {
|
|
734
|
+
if (node.from === firstCaseError) {
|
|
735
|
+
return this.makeDiagnostic(node, 'expected "THEN" after CASE condition', this.caseGroup(caseWhenClause));
|
|
736
|
+
}
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
const missingResult = /\bthen\s+(else)\b/i.exec(this.maskLineComments(this.nodeText(caseWhenClause)));
|
|
740
|
+
if (missingResult) {
|
|
741
|
+
if (node.from === firstCaseError) {
|
|
742
|
+
const keyword = missingResult[1];
|
|
743
|
+
const keywordOffset = missingResult.index + missingResult[0].toLowerCase().lastIndexOf(keyword.toLowerCase());
|
|
744
|
+
return this.makeDiagnosticAt(caseWhenClause.from + keywordOffset, caseWhenClause.from + keywordOffset + keyword.length, 'CASE result expected after "THEN"', this.caseGroup(caseWhenClause));
|
|
745
|
+
}
|
|
746
|
+
return errorText ? this.makeUnexpectedTokenDiagnostic(node, dqlSyntaxContexts.CaseExpr, group) : null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (parentName === 'EvalAtom' && errorText === '=' && (previous === null || previous === void 0 ? void 0 : previous.name) === 'Identifier') {
|
|
750
|
+
return this.makeDiagnostic(node, 'function argument value expected after "="', group);
|
|
751
|
+
}
|
|
752
|
+
if (parentName === 'EvalExpression' && node.from === node.to && (previous === null || previous === void 0 ? void 0 : previous.name) === 'EvalAtom' && (next === null || next === void 0 ? void 0 : next.name) === 'EvalAtom') {
|
|
753
|
+
return this.makeDiagnostic(node, 'expected "," between function arguments', group);
|
|
754
|
+
}
|
|
755
|
+
if (parentName === 'CaseArithmeticExpr' && errorText && !(previous === null || previous === void 0 ? void 0 : previous.type.isError)) {
|
|
756
|
+
return this.makeUnexpectedTokenDiagnostic(node, dqlSyntaxContexts.CaseExpr, group);
|
|
757
|
+
}
|
|
758
|
+
if (parentName === 'ShiftExpr' || parentName === 'ShiftClause') {
|
|
759
|
+
const message = errorText ? 'SHIFT duration must be a positive fixed duration' : 'duration expected after "SHIFT"';
|
|
760
|
+
return this.makeDiagnostic(node, message, `shift-duration:${parent.from}`);
|
|
761
|
+
}
|
|
762
|
+
if (expressionParents.has(parentName)) {
|
|
763
|
+
if (previous && this.isOperator(previous)) {
|
|
764
|
+
return this.makeDiagnostic(node, `right-hand expression expected after "${previousText}"`, group);
|
|
568
765
|
}
|
|
766
|
+
return this.makeUnexpectedTokenDiagnostic(node, 'expression', group);
|
|
569
767
|
}
|
|
570
768
|
if (parentName === 'CaseCondition') {
|
|
571
769
|
return this.makeDiagnostic(node, 'expected "THEN" after CASE condition', this.caseGroup(parent));
|
|
@@ -614,7 +812,7 @@ export class Parser {
|
|
|
614
812
|
}
|
|
615
813
|
if (parentName === 'RollupIdentifier') {
|
|
616
814
|
const timeClause = this.findAncestor(parent, 'TimeClause');
|
|
617
|
-
let child = (
|
|
815
|
+
let child = (_c = timeClause === null || timeClause === void 0 ? void 0 : timeClause.firstChild) !== null && _c !== void 0 ? _c : null;
|
|
618
816
|
while (child) {
|
|
619
817
|
if (child.type.isError)
|
|
620
818
|
return null;
|
|
@@ -1154,6 +1352,7 @@ export class Parser {
|
|
|
1154
1352
|
const contexts = {
|
|
1155
1353
|
Projection: 'projection item',
|
|
1156
1354
|
FunctionCallBody: 'function argument',
|
|
1355
|
+
EvalFunctionCallBody: 'function argument',
|
|
1157
1356
|
EvalCallBody: 'function argument',
|
|
1158
1357
|
OuterFunctionCallBody: 'function argument',
|
|
1159
1358
|
RawOuterFunctionCallBody: 'function argument',
|
|
@@ -1162,7 +1361,6 @@ export class Parser {
|
|
|
1162
1361
|
FillOuterFunctionCallBody: 'function argument',
|
|
1163
1362
|
VariadicOuterFunctionCallBody: 'function argument',
|
|
1164
1363
|
RollupFunctionCallBody: 'function argument',
|
|
1165
|
-
CaseFunctionCallBody: 'function argument',
|
|
1166
1364
|
InValue: 'argument value',
|
|
1167
1365
|
InFieldValue: 'filter value',
|
|
1168
1366
|
IndexExpr: 'namespace index',
|
|
@@ -1347,6 +1545,20 @@ export class Parser {
|
|
|
1347
1545
|
}
|
|
1348
1546
|
return false;
|
|
1349
1547
|
}
|
|
1548
|
+
firstErrorFrom(node) {
|
|
1549
|
+
let first = null;
|
|
1550
|
+
const visit = (current) => {
|
|
1551
|
+
let child = current.firstChild;
|
|
1552
|
+
while (child) {
|
|
1553
|
+
if (child.type.isError && (first === null || child.from < first))
|
|
1554
|
+
first = child.from;
|
|
1555
|
+
visit(child);
|
|
1556
|
+
child = child.nextSibling;
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
visit(node);
|
|
1560
|
+
return first;
|
|
1561
|
+
}
|
|
1350
1562
|
looksLikeProjectionAfterMissingColon(node) {
|
|
1351
1563
|
const querySource = node.getChild('QuerySource');
|
|
1352
1564
|
return Boolean(querySource && this.containsError(querySource) && !this.containsNode(querySource, 'DColon'));
|
|
@@ -2294,7 +2506,10 @@ export class Parser {
|
|
|
2294
2506
|
if (!node.type.isError && !this.containsError(node)) {
|
|
2295
2507
|
switch (node.name) {
|
|
2296
2508
|
case 'Query':
|
|
2509
|
+
case 'QueryFragment':
|
|
2297
2510
|
this.checkDuplicateQueryClauses(node);
|
|
2511
|
+
this.checkQueryShiftPlacement(node);
|
|
2512
|
+
this.checkExpressionShiftOffsets(node);
|
|
2298
2513
|
break;
|
|
2299
2514
|
case 'ShowExpr':
|
|
2300
2515
|
this.checkDuplicateShowClauses(node);
|
|
@@ -2303,6 +2518,19 @@ export class Parser {
|
|
|
2303
2518
|
case 'TimeRange':
|
|
2304
2519
|
this.checkTimeRangeShape(node);
|
|
2305
2520
|
break;
|
|
2521
|
+
case 'ShiftClause':
|
|
2522
|
+
this.checkShiftDuration(node);
|
|
2523
|
+
break;
|
|
2524
|
+
case 'ShiftExpr':
|
|
2525
|
+
this.checkShiftDuration(node);
|
|
2526
|
+
this.checkShiftExpression(node);
|
|
2527
|
+
break;
|
|
2528
|
+
case 'EvalExpr':
|
|
2529
|
+
this.checkEvalReferences(node);
|
|
2530
|
+
break;
|
|
2531
|
+
case 'EvalFieldReference':
|
|
2532
|
+
this.checkEvalFieldReferenceContext(node);
|
|
2533
|
+
break;
|
|
2306
2534
|
case 'RollupIdentifier':
|
|
2307
2535
|
this.checkBareRollup(node);
|
|
2308
2536
|
break;
|
|
@@ -2310,7 +2538,10 @@ export class Parser {
|
|
|
2310
2538
|
this.checkRollupFunctionCall(node);
|
|
2311
2539
|
break;
|
|
2312
2540
|
case 'FunctionCall':
|
|
2541
|
+
case 'EvalFunctionCall':
|
|
2313
2542
|
this.checkFunctionCall(node);
|
|
2543
|
+
if (node.name === 'FunctionCall')
|
|
2544
|
+
this.checkCaseFunction(node);
|
|
2314
2545
|
break;
|
|
2315
2546
|
case 'OuterFunctionCall':
|
|
2316
2547
|
this.checkOuterFunctionCall(node);
|
|
@@ -2330,9 +2561,6 @@ export class Parser {
|
|
|
2330
2561
|
case 'FieldName':
|
|
2331
2562
|
this.checkFieldName(node);
|
|
2332
2563
|
break;
|
|
2333
|
-
case 'CaseFunctionIdentifier':
|
|
2334
|
-
this.checkCaseFunction(node);
|
|
2335
|
-
break;
|
|
2336
2564
|
case 'CaseExpr':
|
|
2337
2565
|
this.checkCaseResults(node);
|
|
2338
2566
|
break;
|
|
@@ -2348,7 +2576,7 @@ export class Parser {
|
|
|
2348
2576
|
const seen = new Set();
|
|
2349
2577
|
let child = query.firstChild;
|
|
2350
2578
|
while (child) {
|
|
2351
|
-
if (child.name === 'QueryClause') {
|
|
2579
|
+
if (child.name === 'QueryClause' || child.name === 'QueryFragmentClause') {
|
|
2352
2580
|
const clause = child.firstChild;
|
|
2353
2581
|
if (clause) {
|
|
2354
2582
|
const label = queryClauseLabels.get(clause.name);
|
|
@@ -2365,6 +2593,168 @@ export class Parser {
|
|
|
2365
2593
|
child = child.nextSibling;
|
|
2366
2594
|
}
|
|
2367
2595
|
}
|
|
2596
|
+
checkQueryShiftPlacement(query) {
|
|
2597
|
+
var _a;
|
|
2598
|
+
const clauses = [];
|
|
2599
|
+
let child = query.firstChild;
|
|
2600
|
+
while (child) {
|
|
2601
|
+
if (child.name === 'QueryClause' || child.name === 'QueryFragmentClause') {
|
|
2602
|
+
const clause = child.firstChild;
|
|
2603
|
+
if (clause)
|
|
2604
|
+
clauses.push(clause);
|
|
2605
|
+
}
|
|
2606
|
+
child = child.nextSibling;
|
|
2607
|
+
}
|
|
2608
|
+
const shifts = clauses.filter((clause) => clause.name === 'ShiftClause');
|
|
2609
|
+
const timeRanges = clauses.filter((clause) => clause.name === 'TimeClause');
|
|
2610
|
+
if (shifts.length !== 1 || timeRanges.length > 1)
|
|
2611
|
+
return;
|
|
2612
|
+
const shift = shifts[0];
|
|
2613
|
+
const shiftIndex = clauses.indexOf(shift);
|
|
2614
|
+
const laterTarget = clauses.slice(shiftIndex + 1).find((clause) => clause.name === 'TargetClause');
|
|
2615
|
+
if (laterTarget) {
|
|
2616
|
+
this.pushSemanticDiagnostic(shift, '"SHIFT" must appear after the projection', `shift-placement:${shift.from}`);
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
const laterFilter = clauses.slice(shiftIndex + 1).find((clause) => clause.name === 'FilterClause');
|
|
2620
|
+
if (laterFilter) {
|
|
2621
|
+
this.pushSemanticDiagnostic(shift, '"SHIFT" must appear after "WHERE"', `shift-placement:${shift.from}`);
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
if (timeRanges.length === 1) {
|
|
2625
|
+
const timeIndex = clauses.indexOf(timeRanges[0]);
|
|
2626
|
+
if (shiftIndex !== timeIndex + 1) {
|
|
2627
|
+
this.pushSemanticDiagnostic(shift, '"SHIFT" must immediately follow the time range', `shift-placement:${shift.from}`);
|
|
2628
|
+
}
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
const outputClause = clauses
|
|
2632
|
+
.slice(0, shiftIndex)
|
|
2633
|
+
.reverse()
|
|
2634
|
+
.find((clause) => ['ByClause', 'HavingClause', 'OrderClause', 'SOrderClause', 'LimitClause', 'OffsetClause', 'SLimitClause', 'SOffsetClause'].includes(clause.name));
|
|
2635
|
+
if (outputClause) {
|
|
2636
|
+
const label = (_a = queryClauseLabels.get(outputClause.name)) !== null && _a !== void 0 ? _a : outputClause.name;
|
|
2637
|
+
this.pushSemanticDiagnostic(shift, `"SHIFT" must appear before "${label}"`, `shift-placement:${shift.from}`);
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
checkExpressionShiftOffsets(query) {
|
|
2641
|
+
const offsets = new Set();
|
|
2642
|
+
let overflowReported = false;
|
|
2643
|
+
const visit = (node) => {
|
|
2644
|
+
if (overflowReported || Date.now() >= this.deadline)
|
|
2645
|
+
return;
|
|
2646
|
+
if (node !== query && node.name === 'Query')
|
|
2647
|
+
return;
|
|
2648
|
+
if (node.name === 'ShiftExpr') {
|
|
2649
|
+
const duration = node.getChild('Duration');
|
|
2650
|
+
if (duration) {
|
|
2651
|
+
const rawOffset = this.nodeText(duration).toLowerCase();
|
|
2652
|
+
const milliseconds = this.fixedDurationMilliseconds(rawOffset);
|
|
2653
|
+
const offset = milliseconds !== null && Number.isSafeInteger(milliseconds) ? `${milliseconds}ms` : rawOffset;
|
|
2654
|
+
if (!offsets.has(offset)) {
|
|
2655
|
+
offsets.add(offset);
|
|
2656
|
+
if (offsets.size > 16) {
|
|
2657
|
+
this.pushSemanticDiagnostic(duration, 'a SELECT can use at most 16 distinct SHIFT offsets', `shift-offsets:${query.from}`);
|
|
2658
|
+
overflowReported = true;
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
let child = node.firstChild;
|
|
2665
|
+
while (child) {
|
|
2666
|
+
visit(child);
|
|
2667
|
+
child = child.nextSibling;
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
2670
|
+
visit(query);
|
|
2671
|
+
}
|
|
2672
|
+
checkShiftDuration(shift) {
|
|
2673
|
+
const duration = shift.getChild('Duration');
|
|
2674
|
+
if (!duration || this.isPositiveFixedDuration(this.nodeText(duration)))
|
|
2675
|
+
return;
|
|
2676
|
+
this.pushSemanticDiagnostic(duration, 'SHIFT duration must be a positive fixed duration', `shift-duration:${shift.from}`);
|
|
2677
|
+
}
|
|
2678
|
+
checkShiftExpression(shift) {
|
|
2679
|
+
var _a, _b;
|
|
2680
|
+
const nested = this.findAncestor(shift.parent, 'ShiftExpr');
|
|
2681
|
+
if (nested) {
|
|
2682
|
+
this.pushSemanticDiagnostic(shift, 'SHIFT expressions cannot be nested', `shift-nested:${shift.from}`);
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
if (!this.findAncestor(shift.parent, 'TargetClause') && !this.findAncestor(shift.parent, 'ProjectionFragment')) {
|
|
2686
|
+
this.pushSemanticDiagnostic(shift, 'expression-level SHIFT is only allowed in the projection', `shift-context:${shift.from}`);
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
const operand = shift.getChild('ShiftOperand');
|
|
2690
|
+
if (!operand)
|
|
2691
|
+
return;
|
|
2692
|
+
const calls = this.shiftOperandFunctionCalls(operand);
|
|
2693
|
+
const unstable = calls.find((call) => {
|
|
2694
|
+
const name = this.functionCallName(call);
|
|
2695
|
+
return name !== null && unstableShiftAggregateFunctions.has(name);
|
|
2696
|
+
});
|
|
2697
|
+
if (unstable) {
|
|
2698
|
+
const name = (_a = this.functionCallName(unstable)) !== null && _a !== void 0 ? _a : 'function';
|
|
2699
|
+
const identifier = (_b = unstable.getChild('FunctionIdentifier')) !== null && _b !== void 0 ? _b : unstable;
|
|
2700
|
+
this.pushSemanticDiagnostic(identifier, `function "${name}" cannot be used as a SHIFT operand`, `shift-operand:${shift.from}`);
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
const hasStableAggregate = calls.some((call) => {
|
|
2704
|
+
const name = this.functionCallName(call);
|
|
2705
|
+
return name !== null && stableShiftAggregateFunctions.has(name);
|
|
2706
|
+
});
|
|
2707
|
+
if (!hasStableAggregate) {
|
|
2708
|
+
this.pushSemanticDiagnostic(operand, 'SHIFT operand must be a stable aggregate or subquery measure', `shift-operand:${shift.from}`);
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
checkEvalFieldReferenceContext(reference) {
|
|
2712
|
+
if (this.findAncestor(reference.parent, 'EvalExpr'))
|
|
2713
|
+
return;
|
|
2714
|
+
this.pushSemanticDiagnostic(reference, 'qualified field references are only supported inside eval', `eval-field-context:${reference.from}`);
|
|
2715
|
+
}
|
|
2716
|
+
checkEvalReferences(evalExpr) {
|
|
2717
|
+
const body = evalExpr.getChild('EvalCallBody');
|
|
2718
|
+
if (!body)
|
|
2719
|
+
return;
|
|
2720
|
+
const aliases = new Set();
|
|
2721
|
+
for (const argument of this.directChildren(body, 'OuterArgItem')) {
|
|
2722
|
+
const identifier = argument.getChild('Identifier');
|
|
2723
|
+
if (!identifier || !argument.getChild('EqlSingle'))
|
|
2724
|
+
continue;
|
|
2725
|
+
if (!argument.getChild('Query') && !argument.getChild('OuterFunctionCall'))
|
|
2726
|
+
continue;
|
|
2727
|
+
const name = this.nodeText(identifier).toLowerCase();
|
|
2728
|
+
if (aliases.has(name)) {
|
|
2729
|
+
this.pushSemanticDiagnostic(identifier, `duplicate eval subquery "${name}"`, `eval-alias-duplicate:${identifier.from}`);
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2732
|
+
aliases.add(name);
|
|
2733
|
+
}
|
|
2734
|
+
const expression = body.getChild('EvalArgItem');
|
|
2735
|
+
if (!expression)
|
|
2736
|
+
return;
|
|
2737
|
+
for (const reference of this.descendantsNamed(expression, 'EvalFieldReference')) {
|
|
2738
|
+
const alias = reference.getChild('Identifier');
|
|
2739
|
+
if (!alias)
|
|
2740
|
+
continue;
|
|
2741
|
+
const name = this.nodeText(alias).toLowerCase();
|
|
2742
|
+
if (!aliases.has(name)) {
|
|
2743
|
+
this.pushSemanticDiagnostic(alias, `unknown eval subquery "${name}" in field reference`, `eval-field:${reference.from}`);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
if (aliases.size > 1) {
|
|
2747
|
+
for (const atom of this.descendantsNamed(expression, 'EvalAtom')) {
|
|
2748
|
+
const identifier = atom.getChild('Identifier');
|
|
2749
|
+
if (!identifier)
|
|
2750
|
+
continue;
|
|
2751
|
+
const name = this.nodeText(identifier).toLowerCase();
|
|
2752
|
+
if (!aliases.has(name)) {
|
|
2753
|
+
this.pushSemanticDiagnostic(identifier, `unknown eval subquery "${name}" in expression`, `eval-alias:${identifier.from}`);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2368
2758
|
checkDuplicateShowClauses(show) {
|
|
2369
2759
|
const seen = new Set();
|
|
2370
2760
|
let child = show.firstChild;
|
|
@@ -2480,12 +2870,44 @@ export class Parser {
|
|
|
2480
2870
|
}
|
|
2481
2871
|
}
|
|
2482
2872
|
checkFunctionCall(call) {
|
|
2873
|
+
var _a;
|
|
2483
2874
|
const identifier = call.getChild('FunctionIdentifier');
|
|
2484
|
-
const body = call.getChild('FunctionCallBody');
|
|
2875
|
+
const body = (_a = call.getChild('FunctionCallBody')) !== null && _a !== void 0 ? _a : call.getChild('EvalFunctionCallBody');
|
|
2485
2876
|
if (!identifier || !body)
|
|
2486
2877
|
return;
|
|
2487
2878
|
const name = this.nodeText(identifier).toLowerCase();
|
|
2488
|
-
const
|
|
2879
|
+
const argumentName = body.name === 'EvalFunctionCallBody' ? 'EvalFunctionArg' : 'ArgItem';
|
|
2880
|
+
const args = this.directChildren(body, argumentName);
|
|
2881
|
+
const arity = documentedFunctionArities.get(name);
|
|
2882
|
+
if (arity && (args.length < arity.min || args.length > arity.max)) {
|
|
2883
|
+
this.pushSemanticDiagnostic(body, this.functionArityMessage(name, arity), `function-arity:${call.from}`);
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
if ((name === 'count_filter' || name === 'distinct_by_collapse') && args.length === 2 && !args[1].getChild('InValue')) {
|
|
2887
|
+
this.pushSemanticDiagnostic(args[1], `function "${name}" expects a value list as its second argument`, `function:${call.from}`);
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
if (name === 'histogram_quantile' && args.length === 2) {
|
|
2891
|
+
const quantile = this.numericValue(args[1]);
|
|
2892
|
+
if (quantile === null || quantile < 0 || quantile > 1) {
|
|
2893
|
+
this.pushSemanticDiagnostic(args[1], 'function "histogram_quantile" expects a quantile in range [0, 1]', `function:${call.from}`);
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
if (name === 'drain' && args.length >= 2) {
|
|
2898
|
+
const threshold = this.numericValue(args[1]);
|
|
2899
|
+
if (threshold === null || threshold <= 0 || threshold > 1) {
|
|
2900
|
+
this.pushSemanticDiagnostic(args[1], 'function "drain" expects a similarity threshold in range (0, 1]', `function:${call.from}`);
|
|
2901
|
+
return;
|
|
2902
|
+
}
|
|
2903
|
+
if (args.length === 3) {
|
|
2904
|
+
const maxClusters = this.numericValue(args[2]);
|
|
2905
|
+
if (maxClusters === null || !Number.isInteger(maxClusters) || maxClusters < 1 || maxClusters > 10000) {
|
|
2906
|
+
this.pushSemanticDiagnostic(args[2], 'function "drain" expects max_clusters to be an integer in range [1, 10000]', `function:${call.from}`);
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2489
2911
|
if (name === 'ewma') {
|
|
2490
2912
|
const alpha = args.length === 2 ? this.numericValue(args[1]) : null;
|
|
2491
2913
|
if (alpha === null || alpha <= 0 || alpha > 1) {
|
|
@@ -2514,6 +2936,17 @@ export class Parser {
|
|
|
2514
2936
|
this.pushSemanticDiagnostic(body, `function "${name}" expects exactly one input field`, `function:${call.from}`);
|
|
2515
2937
|
}
|
|
2516
2938
|
}
|
|
2939
|
+
functionArityMessage(name, arity) {
|
|
2940
|
+
if (arity.min === arity.max) {
|
|
2941
|
+
if (arity.min === 0)
|
|
2942
|
+
return `function "${name}" does not accept arguments`;
|
|
2943
|
+
return `function "${name}" expects exactly ${arity.min} ${arity.min === 1 ? 'argument' : 'arguments'}`;
|
|
2944
|
+
}
|
|
2945
|
+
if (!Number.isFinite(arity.max)) {
|
|
2946
|
+
return `function "${name}" expects at least ${arity.min} ${arity.min === 1 ? 'argument' : 'arguments'}`;
|
|
2947
|
+
}
|
|
2948
|
+
return `function "${name}" expects between ${arity.min} and ${arity.max} arguments`;
|
|
2949
|
+
}
|
|
2517
2950
|
checkOuterFunctionCall(call) {
|
|
2518
2951
|
const identifier = call.firstChild;
|
|
2519
2952
|
if (!identifier || identifier.name === 'FunctionIdentifier')
|
|
@@ -2612,10 +3045,36 @@ export class Parser {
|
|
|
2612
3045
|
this.pushSemanticDiagnostic(field, 'field names containing "-" must be enclosed in backticks', `field-name:${field.from}`);
|
|
2613
3046
|
}
|
|
2614
3047
|
}
|
|
2615
|
-
checkCaseFunction(
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
3048
|
+
checkCaseFunction(call) {
|
|
3049
|
+
const caseExpr = this.findAncestor(call.parent, 'CaseExpr');
|
|
3050
|
+
if (!caseExpr)
|
|
3051
|
+
return;
|
|
3052
|
+
const shiftExpr = this.findAncestor(call.parent, 'ShiftExpr');
|
|
3053
|
+
if (shiftExpr && shiftExpr.from >= caseExpr.from && shiftExpr.to <= caseExpr.to)
|
|
3054
|
+
return;
|
|
3055
|
+
const identifier = call.getChild('FunctionIdentifier');
|
|
3056
|
+
const body = call.getChild('FunctionCallBody');
|
|
3057
|
+
if (!identifier || !body)
|
|
3058
|
+
return;
|
|
3059
|
+
const name = this.nodeText(identifier).toLowerCase();
|
|
3060
|
+
const args = this.directChildren(body, 'ArgItem');
|
|
3061
|
+
const arity = documentedFunctionArities.get(name);
|
|
3062
|
+
if (arity && (args.length < arity.min || args.length > arity.max))
|
|
3063
|
+
return;
|
|
3064
|
+
if (caseScalarFunctions.has(name))
|
|
3065
|
+
return;
|
|
3066
|
+
if (stableShiftAggregateFunctions.has(name) && this.caseContainsShift(caseExpr))
|
|
3067
|
+
return;
|
|
3068
|
+
this.pushSemanticDiagnostic(identifier, `function "${name}" is not supported inside CASE`, `case-function:${identifier.from}`);
|
|
3069
|
+
}
|
|
3070
|
+
caseContainsShift(caseExpr) {
|
|
3071
|
+
const key = this.nodeRangeKey(caseExpr);
|
|
3072
|
+
const cached = this.caseShiftPresence.get(key);
|
|
3073
|
+
if (cached !== undefined)
|
|
3074
|
+
return cached;
|
|
3075
|
+
const containsShift = this.containsNode(caseExpr, 'ShiftExpr');
|
|
3076
|
+
this.caseShiftPresence.set(key, containsShift);
|
|
3077
|
+
return containsShift;
|
|
2619
3078
|
}
|
|
2620
3079
|
checkCaseResults(caseExpr) {
|
|
2621
3080
|
let clause = caseExpr.firstChild;
|
|
@@ -2642,6 +3101,10 @@ export class Parser {
|
|
|
2642
3101
|
const hasFrom = /\bfrom\s*=/.test(bodyText.toLowerCase());
|
|
2643
3102
|
const filter = show.getChild('FilterClause');
|
|
2644
3103
|
const hasSourceFilter = Boolean(filter && /@__source__/.test(this.maskStringsAndLineComments(this.nodeText(filter))));
|
|
3104
|
+
if (/^show_[a-z0-9]+_index$/.test(name)) {
|
|
3105
|
+
this.pushSemanticDiagnostic(identifier, `"${name}" is not supported by the DQL parser`, `show:${show.from}:unsupported`);
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
2645
3108
|
if ((name === 'show_tag_value' || name === 'show_tag_value_cardinality' || name === 'show_series_count_by_tag_value') && !hasKeyin) {
|
|
2646
3109
|
this.pushSemanticDiagnostic(identifier, `"${name}" requires the named argument "keyin"`, `show:${show.from}:keyin`);
|
|
2647
3110
|
}
|
|
@@ -2667,13 +3130,105 @@ export class Parser {
|
|
|
2667
3130
|
}
|
|
2668
3131
|
return children;
|
|
2669
3132
|
}
|
|
3133
|
+
descendantsNamed(node, name) {
|
|
3134
|
+
const descendants = [];
|
|
3135
|
+
const visit = (current) => {
|
|
3136
|
+
let child = current.firstChild;
|
|
3137
|
+
while (child) {
|
|
3138
|
+
if (child.name === name)
|
|
3139
|
+
descendants.push(child);
|
|
3140
|
+
visit(child);
|
|
3141
|
+
child = child.nextSibling;
|
|
3142
|
+
}
|
|
3143
|
+
};
|
|
3144
|
+
visit(node);
|
|
3145
|
+
return descendants;
|
|
3146
|
+
}
|
|
3147
|
+
shiftOperandFunctionCalls(operand) {
|
|
3148
|
+
const querySource = operand.getChild('QuerySource');
|
|
3149
|
+
if (!querySource)
|
|
3150
|
+
return this.descendantsNamed(operand, 'FunctionCall');
|
|
3151
|
+
const query = querySource.getChild('Query');
|
|
3152
|
+
if (!query)
|
|
3153
|
+
return [];
|
|
3154
|
+
const calls = [];
|
|
3155
|
+
let clause = query.firstChild;
|
|
3156
|
+
while (clause) {
|
|
3157
|
+
if (clause.name === 'QueryClause') {
|
|
3158
|
+
const target = clause.getChild('TargetClause');
|
|
3159
|
+
if (target)
|
|
3160
|
+
calls.push(...this.descendantsNamed(target, 'FunctionCall'));
|
|
3161
|
+
}
|
|
3162
|
+
clause = clause.nextSibling;
|
|
3163
|
+
}
|
|
3164
|
+
return calls;
|
|
3165
|
+
}
|
|
3166
|
+
errorDescendantsWithText(node, text) {
|
|
3167
|
+
const errors = [];
|
|
3168
|
+
const expected = text.toLowerCase();
|
|
3169
|
+
const visit = (current) => {
|
|
3170
|
+
let child = current.firstChild;
|
|
3171
|
+
while (child) {
|
|
3172
|
+
if (child.type.isError && this.nodeText(child).toLowerCase() === expected)
|
|
3173
|
+
errors.push(child);
|
|
3174
|
+
visit(child);
|
|
3175
|
+
child = child.nextSibling;
|
|
3176
|
+
}
|
|
3177
|
+
};
|
|
3178
|
+
visit(node);
|
|
3179
|
+
return errors;
|
|
3180
|
+
}
|
|
3181
|
+
functionCallName(call) {
|
|
3182
|
+
const identifier = call.getChild('FunctionIdentifier');
|
|
3183
|
+
return identifier ? this.nodeText(identifier).toLowerCase() : null;
|
|
3184
|
+
}
|
|
3185
|
+
isPositiveFixedDuration(text) {
|
|
3186
|
+
const milliseconds = this.fixedDurationMilliseconds(text);
|
|
3187
|
+
return milliseconds !== null && milliseconds > 0;
|
|
3188
|
+
}
|
|
3189
|
+
fixedDurationMilliseconds(text) {
|
|
3190
|
+
const multipliers = {
|
|
3191
|
+
y: 365 * 24 * 60 * 60 * 1000,
|
|
3192
|
+
w: 7 * 24 * 60 * 60 * 1000,
|
|
3193
|
+
d: 24 * 60 * 60 * 1000,
|
|
3194
|
+
h: 60 * 60 * 1000,
|
|
3195
|
+
m: 60 * 1000,
|
|
3196
|
+
s: 1000,
|
|
3197
|
+
ms: 1,
|
|
3198
|
+
};
|
|
3199
|
+
const normalized = text.toLowerCase();
|
|
3200
|
+
const pattern = /(\d+)(ms|[ywdhms])/g;
|
|
3201
|
+
let total = 0;
|
|
3202
|
+
let consumed = 0;
|
|
3203
|
+
let part = pattern.exec(normalized);
|
|
3204
|
+
while (part) {
|
|
3205
|
+
if (part.index !== consumed)
|
|
3206
|
+
return null;
|
|
3207
|
+
const amount = Number(part[1]);
|
|
3208
|
+
const component = amount * multipliers[part[2]];
|
|
3209
|
+
if (!Number.isSafeInteger(amount) || !Number.isSafeInteger(component) || !Number.isSafeInteger(total + component))
|
|
3210
|
+
return null;
|
|
3211
|
+
total += component;
|
|
3212
|
+
consumed = pattern.lastIndex;
|
|
3213
|
+
part = pattern.exec(normalized);
|
|
3214
|
+
}
|
|
3215
|
+
return consumed === normalized.length ? total : null;
|
|
3216
|
+
}
|
|
2670
3217
|
numericValue(node) {
|
|
2671
3218
|
if (!node)
|
|
2672
3219
|
return null;
|
|
2673
|
-
|
|
3220
|
+
let valueNode = node;
|
|
3221
|
+
if (node.name === 'EvalFunctionArg') {
|
|
3222
|
+
const expression = node.getChild('EvalExpression');
|
|
3223
|
+
const atom = expression === null || expression === void 0 ? void 0 : expression.firstChild;
|
|
3224
|
+
if (!expression || (atom === null || atom === void 0 ? void 0 : atom.name) !== 'EvalAtom' || atom.nextSibling)
|
|
3225
|
+
return null;
|
|
3226
|
+
valueNode = atom;
|
|
3227
|
+
}
|
|
3228
|
+
const number = valueNode.name === 'Number' ? valueNode : valueNode.getChild('Number');
|
|
2674
3229
|
if (!number)
|
|
2675
3230
|
return null;
|
|
2676
|
-
const sign =
|
|
3231
|
+
const sign = valueNode.name !== 'Number' && valueNode.getChild('Sub') ? -1 : 1;
|
|
2677
3232
|
const value = sign * Number(this.nodeText(number).replace(/_/g, ''));
|
|
2678
3233
|
return Number.isFinite(value) ? value : null;
|
|
2679
3234
|
}
|