@cloudcare/codemirror-dql 0.0.57 → 0.0.61

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.
@@ -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,12 +24,151 @@ 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',
31
166
  QueryExpr: 'DQL query',
167
+ StudioQuery: 'STUDIOQL query',
168
+ StudioModule: 'STUDIOQL module',
169
+ StudioFunction: 'STUDIOQL function',
170
+ StudioCallBody: 'function arguments',
171
+ StudioArgument: 'named function argument',
32
172
  ShowExpr: 'SHOW expression',
33
173
  QueryFragment: 'query fragment',
34
174
  QueryFragmentClause: 'query fragment clause',
@@ -60,8 +200,15 @@ export const dqlSyntaxContexts = {
60
200
  OuterArgItem: 'outer function argument',
61
201
  Queted: 'quoted query delimiter',
62
202
  EvalCallBody: 'eval function arguments',
203
+ EvalArgItem: 'eval expression argument',
204
+ EvalExpression: 'eval expression',
205
+ EvalAtom: 'eval expression value',
206
+ EvalFunctionCall: 'eval function call',
207
+ EvalFunctionCallBody: 'eval function arguments',
208
+ EvalFunctionArg: 'eval function argument',
63
209
  FunctionCallBody: 'function arguments',
64
210
  ArgItem: 'function argument',
211
+ EvalFieldReference: 'eval field reference',
65
212
  InValue: 'argument value list',
66
213
  SingleExpr: 'expression value',
67
214
  Expr: 'expression',
@@ -78,6 +225,8 @@ export const dqlSyntaxContexts = {
78
225
  ProjectionFieldFragment: 'projection field fragment',
79
226
  ProjectionField: 'projection field',
80
227
  SingleFieldExpr: 'projection expression value',
228
+ ShiftExpr: 'SHIFT expression',
229
+ ShiftOperand: 'SHIFT operand',
81
230
  ProjectionExpr: 'projection expression',
82
231
  AsExpr: 'alias',
83
232
  FilterClause: 'filter clause',
@@ -91,10 +240,10 @@ export const dqlSyntaxContexts = {
91
240
  CaseCondition: 'CASE condition',
92
241
  CaseFilterExpr: 'CASE filter expression',
93
242
  CasePredicate: 'CASE predicate',
243
+ CaseComparableValue: 'CASE comparison value',
94
244
  CaseValue: 'CASE value',
95
- CaseFunctionCall: 'CASE function call',
96
- CaseFunctionIdentifier: 'CASE function name',
97
- CaseFunctionCallBody: 'CASE function arguments',
245
+ CaseArithmeticAtom: 'CASE arithmetic value',
246
+ CaseArithmeticExpr: 'CASE arithmetic expression',
98
247
  FilterFragmentExpr: 'filter fragment',
99
248
  ComparisonExpr: 'comparison expression',
100
249
  LogicalFragmentExpr: 'logical filter expression',
@@ -105,6 +254,7 @@ export const dqlSyntaxContexts = {
105
254
  FilterExpr: 'filter expression',
106
255
  BinaryExpr: 'binary expression',
107
256
  TimeClause: 'time range clause',
257
+ ShiftClause: 'SHIFT clause',
108
258
  TimeRange: 'time range',
109
259
  TimeExpr: 'time range value',
110
260
  ByClause: 'BY clause',
@@ -123,7 +273,9 @@ export const dqlSyntaxContexts = {
123
273
  NestedByMathExpr: 'nested grouping expression',
124
274
  };
125
275
  const functionCallBodyParents = new Set([
276
+ 'StudioCallBody',
126
277
  'FunctionCallBody',
278
+ 'EvalFunctionCallBody',
127
279
  'EvalCallBody',
128
280
  'OuterFunctionCallBody',
129
281
  'RawOuterFunctionCallBody',
@@ -132,7 +284,6 @@ const functionCallBodyParents = new Set([
132
284
  'FillOuterFunctionCallBody',
133
285
  'VariadicOuterFunctionCallBody',
134
286
  'RollupFunctionCallBody',
135
- 'CaseFunctionCallBody',
136
287
  ]);
137
288
  export class Parser {
138
289
  constructor(state, deadline = Date.now() + analysisBudgetMs) {
@@ -149,6 +300,7 @@ export class Parser {
149
300
  this.quotedQueryDelimiterInfo = undefined;
150
301
  this.caseResultDiagnostics = undefined;
151
302
  this.caseResultRecoveryErrors = new Set();
303
+ this.caseShiftPresence = new Map();
152
304
  }
153
305
  getDiagnostics() {
154
306
  const seen = new Set();
@@ -401,7 +553,7 @@ export class Parser {
401
553
  this.appendDiagnostics(diagnostics);
402
554
  }
403
555
  describeError(node) {
404
- var _a;
556
+ var _a, _b, _c;
405
557
  const delimiterIssue = this.getDelimiterIssue();
406
558
  if (delimiterIssue && delimiterIssue.kind !== 'unclosed' && this.isRelatedDelimiterError(node, delimiterIssue)) {
407
559
  return null;
@@ -483,6 +635,26 @@ export class Parser {
483
635
  }
484
636
  return this.makeDiagnostic(node, 'expected "AS" before projection alias', group);
485
637
  }
638
+ const projectionField = (_a = this.findAncestor(parent, 'ProjectionField')) !== null && _a !== void 0 ? _a : this.findAncestor(parent, 'ProjectionFieldFragment');
639
+ if (projectionField) {
640
+ const shiftError = this.errorDescendantsWithText(projectionField, 'shift').find((error) => error.from <= node.from);
641
+ if (shiftError && this.containsNode(projectionField, 'ShiftExpr')) {
642
+ return this.makeDiagnosticAt(shiftError.from, shiftError.to, 'SHIFT expressions cannot be nested', `shift-nested-syntax:${projectionField.from}`);
643
+ }
644
+ if (shiftError) {
645
+ const rawOperand = this.state.sliceDoc(projectionField.from, shiftError.from);
646
+ const leading = rawOperand.length - rawOperand.trimStart().length;
647
+ const trailing = rawOperand.length - rawOperand.trimEnd().length;
648
+ 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}`);
649
+ }
650
+ }
651
+ const invalidShiftContext = (_b = this.findAncestor(parent, 'FilterClause')) !== null && _b !== void 0 ? _b : this.findAncestor(parent, 'HavingClause');
652
+ if (invalidShiftContext) {
653
+ const shiftError = this.errorDescendantsWithText(invalidShiftContext, 'shift').find((error) => error.from <= node.from);
654
+ if (shiftError) {
655
+ return this.makeDiagnosticAt(shiftError.from, shiftError.to, 'expression-level SHIFT is only allowed in the projection', `shift-context-syntax:${invalidShiftContext.from}`);
656
+ }
657
+ }
486
658
  if (parentName === 'OuterFunctionCall') {
487
659
  return this.makeUnexpectedTokenDiagnostic(node, dqlSyntaxContexts.OuterFunctionCall, group);
488
660
  }
@@ -507,7 +679,7 @@ export class Parser {
507
679
  }
508
680
  return this.makeUnexpectedTokenDiagnostic(node, 'function call', group);
509
681
  }
510
- if (parentName === 'ArgItem' && node.from === node.to) {
682
+ if ((parentName === 'ArgItem' || parentName === 'EvalFunctionArg') && node.from === node.to) {
511
683
  if ((previous === null || previous === void 0 ? void 0 : previous.name) === 'EqlSingle') {
512
684
  return this.makeDiagnostic(node, 'function argument value expected after "="', group);
513
685
  }
@@ -553,19 +725,51 @@ export class Parser {
553
725
  if (parentName === 'ByExpr' && errorText === ',') {
554
726
  return this.makeDiagnostic(node, 'grouping field expected before ","', group);
555
727
  }
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
728
  const caseWhenClause = this.findAncestor(parent, 'CaseWhenClause');
563
729
  if (caseWhenClause) {
730
+ const firstCaseError = this.firstErrorFrom(caseWhenClause);
564
731
  const missingCondition = /^\s*when\s+(then)\b/i.exec(this.maskLineComments(this.nodeText(caseWhenClause)));
565
732
  if (missingCondition) {
566
- const thenOffset = missingCondition.index + missingCondition[0].toLowerCase().lastIndexOf('then');
567
- return this.makeDiagnosticAt(caseWhenClause.from + thenOffset, caseWhenClause.from + thenOffset + 4, 'CASE condition expected after "WHEN"', this.caseGroup(caseWhenClause));
733
+ if (node.from === firstCaseError) {
734
+ const thenOffset = missingCondition.index + missingCondition[0].toLowerCase().lastIndexOf('then');
735
+ return this.makeDiagnosticAt(caseWhenClause.from + thenOffset, caseWhenClause.from + thenOffset + 4, 'CASE condition expected after "WHEN"', this.caseGroup(caseWhenClause));
736
+ }
737
+ return null;
738
+ }
739
+ if (!caseWhenClause.getChild('Then')) {
740
+ if (node.from === firstCaseError) {
741
+ return this.makeDiagnostic(node, 'expected "THEN" after CASE condition', this.caseGroup(caseWhenClause));
742
+ }
743
+ return null;
744
+ }
745
+ const missingResult = /\bthen\s+(else)\b/i.exec(this.maskLineComments(this.nodeText(caseWhenClause)));
746
+ if (missingResult) {
747
+ if (node.from === firstCaseError) {
748
+ const keyword = missingResult[1];
749
+ const keywordOffset = missingResult.index + missingResult[0].toLowerCase().lastIndexOf(keyword.toLowerCase());
750
+ return this.makeDiagnosticAt(caseWhenClause.from + keywordOffset, caseWhenClause.from + keywordOffset + keyword.length, 'CASE result expected after "THEN"', this.caseGroup(caseWhenClause));
751
+ }
752
+ return errorText ? this.makeUnexpectedTokenDiagnostic(node, dqlSyntaxContexts.CaseExpr, group) : null;
753
+ }
754
+ }
755
+ if (parentName === 'EvalAtom' && errorText === '=' && (previous === null || previous === void 0 ? void 0 : previous.name) === 'Identifier') {
756
+ return this.makeDiagnostic(node, 'function argument value expected after "="', group);
757
+ }
758
+ 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') {
759
+ return this.makeDiagnostic(node, 'expected "," between function arguments', group);
760
+ }
761
+ if (parentName === 'CaseArithmeticExpr' && errorText && !(previous === null || previous === void 0 ? void 0 : previous.type.isError)) {
762
+ return this.makeUnexpectedTokenDiagnostic(node, dqlSyntaxContexts.CaseExpr, group);
763
+ }
764
+ if (parentName === 'ShiftExpr' || parentName === 'ShiftClause') {
765
+ const message = errorText ? 'SHIFT duration must be a positive fixed duration' : 'duration expected after "SHIFT"';
766
+ return this.makeDiagnostic(node, message, `shift-duration:${parent.from}`);
767
+ }
768
+ if (expressionParents.has(parentName)) {
769
+ if (previous && this.isOperator(previous)) {
770
+ return this.makeDiagnostic(node, `right-hand expression expected after "${previousText}"`, group);
568
771
  }
772
+ return this.makeUnexpectedTokenDiagnostic(node, 'expression', group);
569
773
  }
570
774
  if (parentName === 'CaseCondition') {
571
775
  return this.makeDiagnostic(node, 'expected "THEN" after CASE condition', this.caseGroup(parent));
@@ -614,7 +818,7 @@ export class Parser {
614
818
  }
615
819
  if (parentName === 'RollupIdentifier') {
616
820
  const timeClause = this.findAncestor(parent, 'TimeClause');
617
- let child = (_a = timeClause === null || timeClause === void 0 ? void 0 : timeClause.firstChild) !== null && _a !== void 0 ? _a : null;
821
+ let child = (_c = timeClause === null || timeClause === void 0 ? void 0 : timeClause.firstChild) !== null && _c !== void 0 ? _c : null;
618
822
  while (child) {
619
823
  if (child.type.isError)
620
824
  return null;
@@ -1153,7 +1357,9 @@ export class Parser {
1153
1357
  describeConsecutiveCommaError(node, parent) {
1154
1358
  const contexts = {
1155
1359
  Projection: 'projection item',
1360
+ StudioCallBody: 'function argument',
1156
1361
  FunctionCallBody: 'function argument',
1362
+ EvalFunctionCallBody: 'function argument',
1157
1363
  EvalCallBody: 'function argument',
1158
1364
  OuterFunctionCallBody: 'function argument',
1159
1365
  RawOuterFunctionCallBody: 'function argument',
@@ -1162,7 +1368,6 @@ export class Parser {
1162
1368
  FillOuterFunctionCallBody: 'function argument',
1163
1369
  VariadicOuterFunctionCallBody: 'function argument',
1164
1370
  RollupFunctionCallBody: 'function argument',
1165
- CaseFunctionCallBody: 'function argument',
1166
1371
  InValue: 'argument value',
1167
1372
  InFieldValue: 'filter value',
1168
1373
  IndexExpr: 'namespace index',
@@ -1347,6 +1552,20 @@ export class Parser {
1347
1552
  }
1348
1553
  return false;
1349
1554
  }
1555
+ firstErrorFrom(node) {
1556
+ let first = null;
1557
+ const visit = (current) => {
1558
+ let child = current.firstChild;
1559
+ while (child) {
1560
+ if (child.type.isError && (first === null || child.from < first))
1561
+ first = child.from;
1562
+ visit(child);
1563
+ child = child.nextSibling;
1564
+ }
1565
+ };
1566
+ visit(node);
1567
+ return first;
1568
+ }
1350
1569
  looksLikeProjectionAfterMissingColon(node) {
1351
1570
  const querySource = node.getChild('QuerySource');
1352
1571
  return Boolean(querySource && this.containsError(querySource) && !this.containsNode(querySource, 'DColon'));
@@ -2296,6 +2515,8 @@ export class Parser {
2296
2515
  case 'Query':
2297
2516
  case 'QueryFragment':
2298
2517
  this.checkDuplicateQueryClauses(node);
2518
+ this.checkQueryShiftPlacement(node);
2519
+ this.checkExpressionShiftOffsets(node);
2299
2520
  break;
2300
2521
  case 'ShowExpr':
2301
2522
  this.checkDuplicateShowClauses(node);
@@ -2304,6 +2525,19 @@ export class Parser {
2304
2525
  case 'TimeRange':
2305
2526
  this.checkTimeRangeShape(node);
2306
2527
  break;
2528
+ case 'ShiftClause':
2529
+ this.checkShiftDuration(node);
2530
+ break;
2531
+ case 'ShiftExpr':
2532
+ this.checkShiftDuration(node);
2533
+ this.checkShiftExpression(node);
2534
+ break;
2535
+ case 'EvalExpr':
2536
+ this.checkEvalReferences(node);
2537
+ break;
2538
+ case 'EvalFieldReference':
2539
+ this.checkEvalFieldReferenceContext(node);
2540
+ break;
2307
2541
  case 'RollupIdentifier':
2308
2542
  this.checkBareRollup(node);
2309
2543
  break;
@@ -2311,7 +2545,10 @@ export class Parser {
2311
2545
  this.checkRollupFunctionCall(node);
2312
2546
  break;
2313
2547
  case 'FunctionCall':
2548
+ case 'EvalFunctionCall':
2314
2549
  this.checkFunctionCall(node);
2550
+ if (node.name === 'FunctionCall')
2551
+ this.checkCaseFunction(node);
2315
2552
  break;
2316
2553
  case 'OuterFunctionCall':
2317
2554
  this.checkOuterFunctionCall(node);
@@ -2331,9 +2568,6 @@ export class Parser {
2331
2568
  case 'FieldName':
2332
2569
  this.checkFieldName(node);
2333
2570
  break;
2334
- case 'CaseFunctionIdentifier':
2335
- this.checkCaseFunction(node);
2336
- break;
2337
2571
  case 'CaseExpr':
2338
2572
  this.checkCaseResults(node);
2339
2573
  break;
@@ -2366,6 +2600,168 @@ export class Parser {
2366
2600
  child = child.nextSibling;
2367
2601
  }
2368
2602
  }
2603
+ checkQueryShiftPlacement(query) {
2604
+ var _a;
2605
+ const clauses = [];
2606
+ let child = query.firstChild;
2607
+ while (child) {
2608
+ if (child.name === 'QueryClause' || child.name === 'QueryFragmentClause') {
2609
+ const clause = child.firstChild;
2610
+ if (clause)
2611
+ clauses.push(clause);
2612
+ }
2613
+ child = child.nextSibling;
2614
+ }
2615
+ const shifts = clauses.filter((clause) => clause.name === 'ShiftClause');
2616
+ const timeRanges = clauses.filter((clause) => clause.name === 'TimeClause');
2617
+ if (shifts.length !== 1 || timeRanges.length > 1)
2618
+ return;
2619
+ const shift = shifts[0];
2620
+ const shiftIndex = clauses.indexOf(shift);
2621
+ const laterTarget = clauses.slice(shiftIndex + 1).find((clause) => clause.name === 'TargetClause');
2622
+ if (laterTarget) {
2623
+ this.pushSemanticDiagnostic(shift, '"SHIFT" must appear after the projection', `shift-placement:${shift.from}`);
2624
+ return;
2625
+ }
2626
+ const laterFilter = clauses.slice(shiftIndex + 1).find((clause) => clause.name === 'FilterClause');
2627
+ if (laterFilter) {
2628
+ this.pushSemanticDiagnostic(shift, '"SHIFT" must appear after "WHERE"', `shift-placement:${shift.from}`);
2629
+ return;
2630
+ }
2631
+ if (timeRanges.length === 1) {
2632
+ const timeIndex = clauses.indexOf(timeRanges[0]);
2633
+ if (shiftIndex !== timeIndex + 1) {
2634
+ this.pushSemanticDiagnostic(shift, '"SHIFT" must immediately follow the time range', `shift-placement:${shift.from}`);
2635
+ }
2636
+ return;
2637
+ }
2638
+ const outputClause = clauses
2639
+ .slice(0, shiftIndex)
2640
+ .reverse()
2641
+ .find((clause) => ['ByClause', 'HavingClause', 'OrderClause', 'SOrderClause', 'LimitClause', 'OffsetClause', 'SLimitClause', 'SOffsetClause'].includes(clause.name));
2642
+ if (outputClause) {
2643
+ const label = (_a = queryClauseLabels.get(outputClause.name)) !== null && _a !== void 0 ? _a : outputClause.name;
2644
+ this.pushSemanticDiagnostic(shift, `"SHIFT" must appear before "${label}"`, `shift-placement:${shift.from}`);
2645
+ }
2646
+ }
2647
+ checkExpressionShiftOffsets(query) {
2648
+ const offsets = new Set();
2649
+ let overflowReported = false;
2650
+ const visit = (node) => {
2651
+ if (overflowReported || Date.now() >= this.deadline)
2652
+ return;
2653
+ if (node !== query && node.name === 'Query')
2654
+ return;
2655
+ if (node.name === 'ShiftExpr') {
2656
+ const duration = node.getChild('Duration');
2657
+ if (duration) {
2658
+ const rawOffset = this.nodeText(duration).toLowerCase();
2659
+ const milliseconds = this.fixedDurationMilliseconds(rawOffset);
2660
+ const offset = milliseconds !== null && Number.isSafeInteger(milliseconds) ? `${milliseconds}ms` : rawOffset;
2661
+ if (!offsets.has(offset)) {
2662
+ offsets.add(offset);
2663
+ if (offsets.size > 16) {
2664
+ this.pushSemanticDiagnostic(duration, 'a SELECT can use at most 16 distinct SHIFT offsets', `shift-offsets:${query.from}`);
2665
+ overflowReported = true;
2666
+ return;
2667
+ }
2668
+ }
2669
+ }
2670
+ }
2671
+ let child = node.firstChild;
2672
+ while (child) {
2673
+ visit(child);
2674
+ child = child.nextSibling;
2675
+ }
2676
+ };
2677
+ visit(query);
2678
+ }
2679
+ checkShiftDuration(shift) {
2680
+ const duration = shift.getChild('Duration');
2681
+ if (!duration || this.isPositiveFixedDuration(this.nodeText(duration)))
2682
+ return;
2683
+ this.pushSemanticDiagnostic(duration, 'SHIFT duration must be a positive fixed duration', `shift-duration:${shift.from}`);
2684
+ }
2685
+ checkShiftExpression(shift) {
2686
+ var _a, _b;
2687
+ const nested = this.findAncestor(shift.parent, 'ShiftExpr');
2688
+ if (nested) {
2689
+ this.pushSemanticDiagnostic(shift, 'SHIFT expressions cannot be nested', `shift-nested:${shift.from}`);
2690
+ return;
2691
+ }
2692
+ if (!this.findAncestor(shift.parent, 'TargetClause') && !this.findAncestor(shift.parent, 'ProjectionFragment')) {
2693
+ this.pushSemanticDiagnostic(shift, 'expression-level SHIFT is only allowed in the projection', `shift-context:${shift.from}`);
2694
+ return;
2695
+ }
2696
+ const operand = shift.getChild('ShiftOperand');
2697
+ if (!operand)
2698
+ return;
2699
+ const calls = this.shiftOperandFunctionCalls(operand);
2700
+ const unstable = calls.find((call) => {
2701
+ const name = this.functionCallName(call);
2702
+ return name !== null && unstableShiftAggregateFunctions.has(name);
2703
+ });
2704
+ if (unstable) {
2705
+ const name = (_a = this.functionCallName(unstable)) !== null && _a !== void 0 ? _a : 'function';
2706
+ const identifier = (_b = unstable.getChild('FunctionIdentifier')) !== null && _b !== void 0 ? _b : unstable;
2707
+ this.pushSemanticDiagnostic(identifier, `function "${name}" cannot be used as a SHIFT operand`, `shift-operand:${shift.from}`);
2708
+ return;
2709
+ }
2710
+ const hasStableAggregate = calls.some((call) => {
2711
+ const name = this.functionCallName(call);
2712
+ return name !== null && stableShiftAggregateFunctions.has(name);
2713
+ });
2714
+ if (!hasStableAggregate) {
2715
+ this.pushSemanticDiagnostic(operand, 'SHIFT operand must be a stable aggregate or subquery measure', `shift-operand:${shift.from}`);
2716
+ }
2717
+ }
2718
+ checkEvalFieldReferenceContext(reference) {
2719
+ if (this.findAncestor(reference.parent, 'EvalExpr'))
2720
+ return;
2721
+ this.pushSemanticDiagnostic(reference, 'qualified field references are only supported inside eval', `eval-field-context:${reference.from}`);
2722
+ }
2723
+ checkEvalReferences(evalExpr) {
2724
+ const body = evalExpr.getChild('EvalCallBody');
2725
+ if (!body)
2726
+ return;
2727
+ const aliases = new Set();
2728
+ for (const argument of this.directChildren(body, 'OuterArgItem')) {
2729
+ const identifier = argument.getChild('Identifier');
2730
+ if (!identifier || !argument.getChild('EqlSingle'))
2731
+ continue;
2732
+ if (!argument.getChild('Query') && !argument.getChild('OuterFunctionCall'))
2733
+ continue;
2734
+ const name = this.nodeText(identifier).toLowerCase();
2735
+ if (aliases.has(name)) {
2736
+ this.pushSemanticDiagnostic(identifier, `duplicate eval subquery "${name}"`, `eval-alias-duplicate:${identifier.from}`);
2737
+ continue;
2738
+ }
2739
+ aliases.add(name);
2740
+ }
2741
+ const expression = body.getChild('EvalArgItem');
2742
+ if (!expression)
2743
+ return;
2744
+ for (const reference of this.descendantsNamed(expression, 'EvalFieldReference')) {
2745
+ const alias = reference.getChild('Identifier');
2746
+ if (!alias)
2747
+ continue;
2748
+ const name = this.nodeText(alias).toLowerCase();
2749
+ if (!aliases.has(name)) {
2750
+ this.pushSemanticDiagnostic(alias, `unknown eval subquery "${name}" in field reference`, `eval-field:${reference.from}`);
2751
+ }
2752
+ }
2753
+ if (aliases.size > 1) {
2754
+ for (const atom of this.descendantsNamed(expression, 'EvalAtom')) {
2755
+ const identifier = atom.getChild('Identifier');
2756
+ if (!identifier)
2757
+ continue;
2758
+ const name = this.nodeText(identifier).toLowerCase();
2759
+ if (!aliases.has(name)) {
2760
+ this.pushSemanticDiagnostic(identifier, `unknown eval subquery "${name}" in expression`, `eval-alias:${identifier.from}`);
2761
+ }
2762
+ }
2763
+ }
2764
+ }
2369
2765
  checkDuplicateShowClauses(show) {
2370
2766
  const seen = new Set();
2371
2767
  let child = show.firstChild;
@@ -2481,12 +2877,44 @@ export class Parser {
2481
2877
  }
2482
2878
  }
2483
2879
  checkFunctionCall(call) {
2880
+ var _a;
2484
2881
  const identifier = call.getChild('FunctionIdentifier');
2485
- const body = call.getChild('FunctionCallBody');
2882
+ const body = (_a = call.getChild('FunctionCallBody')) !== null && _a !== void 0 ? _a : call.getChild('EvalFunctionCallBody');
2486
2883
  if (!identifier || !body)
2487
2884
  return;
2488
2885
  const name = this.nodeText(identifier).toLowerCase();
2489
- const args = this.directChildren(body, 'ArgItem');
2886
+ const argumentName = body.name === 'EvalFunctionCallBody' ? 'EvalFunctionArg' : 'ArgItem';
2887
+ const args = this.directChildren(body, argumentName);
2888
+ const arity = documentedFunctionArities.get(name);
2889
+ if (arity && (args.length < arity.min || args.length > arity.max)) {
2890
+ this.pushSemanticDiagnostic(body, this.functionArityMessage(name, arity), `function-arity:${call.from}`);
2891
+ return;
2892
+ }
2893
+ if ((name === 'count_filter' || name === 'distinct_by_collapse') && args.length === 2 && !args[1].getChild('InValue')) {
2894
+ this.pushSemanticDiagnostic(args[1], `function "${name}" expects a value list as its second argument`, `function:${call.from}`);
2895
+ return;
2896
+ }
2897
+ if (name === 'histogram_quantile' && args.length === 2) {
2898
+ const quantile = this.numericValue(args[1]);
2899
+ if (quantile === null || quantile < 0 || quantile > 1) {
2900
+ this.pushSemanticDiagnostic(args[1], 'function "histogram_quantile" expects a quantile in range [0, 1]', `function:${call.from}`);
2901
+ return;
2902
+ }
2903
+ }
2904
+ if (name === 'drain' && args.length >= 2) {
2905
+ const threshold = this.numericValue(args[1]);
2906
+ if (threshold === null || threshold <= 0 || threshold > 1) {
2907
+ this.pushSemanticDiagnostic(args[1], 'function "drain" expects a similarity threshold in range (0, 1]', `function:${call.from}`);
2908
+ return;
2909
+ }
2910
+ if (args.length === 3) {
2911
+ const maxClusters = this.numericValue(args[2]);
2912
+ if (maxClusters === null || !Number.isInteger(maxClusters) || maxClusters < 1 || maxClusters > 10000) {
2913
+ this.pushSemanticDiagnostic(args[2], 'function "drain" expects max_clusters to be an integer in range [1, 10000]', `function:${call.from}`);
2914
+ return;
2915
+ }
2916
+ }
2917
+ }
2490
2918
  if (name === 'ewma') {
2491
2919
  const alpha = args.length === 2 ? this.numericValue(args[1]) : null;
2492
2920
  if (alpha === null || alpha <= 0 || alpha > 1) {
@@ -2515,6 +2943,17 @@ export class Parser {
2515
2943
  this.pushSemanticDiagnostic(body, `function "${name}" expects exactly one input field`, `function:${call.from}`);
2516
2944
  }
2517
2945
  }
2946
+ functionArityMessage(name, arity) {
2947
+ if (arity.min === arity.max) {
2948
+ if (arity.min === 0)
2949
+ return `function "${name}" does not accept arguments`;
2950
+ return `function "${name}" expects exactly ${arity.min} ${arity.min === 1 ? 'argument' : 'arguments'}`;
2951
+ }
2952
+ if (!Number.isFinite(arity.max)) {
2953
+ return `function "${name}" expects at least ${arity.min} ${arity.min === 1 ? 'argument' : 'arguments'}`;
2954
+ }
2955
+ return `function "${name}" expects between ${arity.min} and ${arity.max} arguments`;
2956
+ }
2518
2957
  checkOuterFunctionCall(call) {
2519
2958
  const identifier = call.firstChild;
2520
2959
  if (!identifier || identifier.name === 'FunctionIdentifier')
@@ -2613,11 +3052,36 @@ export class Parser {
2613
3052
  this.pushSemanticDiagnostic(field, 'field names containing "-" must be enclosed in backticks', `field-name:${field.from}`);
2614
3053
  }
2615
3054
  }
2616
- checkCaseFunction(identifier) {
3055
+ checkCaseFunction(call) {
3056
+ const caseExpr = this.findAncestor(call.parent, 'CaseExpr');
3057
+ if (!caseExpr)
3058
+ return;
3059
+ const shiftExpr = this.findAncestor(call.parent, 'ShiftExpr');
3060
+ if (shiftExpr && shiftExpr.from >= caseExpr.from && shiftExpr.to <= caseExpr.to)
3061
+ return;
3062
+ const identifier = call.getChild('FunctionIdentifier');
3063
+ const body = call.getChild('FunctionCallBody');
3064
+ if (!identifier || !body)
3065
+ return;
2617
3066
  const name = this.nodeText(identifier).toLowerCase();
2618
- if (name === 'uint' || name === 'bool' || name === 'substr') {
2619
- this.pushSemanticDiagnostic(identifier, `function "${name}" is not supported inside CASE`, `case-function:${identifier.from}`);
2620
- }
3067
+ const args = this.directChildren(body, 'ArgItem');
3068
+ const arity = documentedFunctionArities.get(name);
3069
+ if (arity && (args.length < arity.min || args.length > arity.max))
3070
+ return;
3071
+ if (caseScalarFunctions.has(name))
3072
+ return;
3073
+ if (stableShiftAggregateFunctions.has(name) && this.caseContainsShift(caseExpr))
3074
+ return;
3075
+ this.pushSemanticDiagnostic(identifier, `function "${name}" is not supported inside CASE`, `case-function:${identifier.from}`);
3076
+ }
3077
+ caseContainsShift(caseExpr) {
3078
+ const key = this.nodeRangeKey(caseExpr);
3079
+ const cached = this.caseShiftPresence.get(key);
3080
+ if (cached !== undefined)
3081
+ return cached;
3082
+ const containsShift = this.containsNode(caseExpr, 'ShiftExpr');
3083
+ this.caseShiftPresence.set(key, containsShift);
3084
+ return containsShift;
2621
3085
  }
2622
3086
  checkCaseResults(caseExpr) {
2623
3087
  let clause = caseExpr.firstChild;
@@ -2644,6 +3108,10 @@ export class Parser {
2644
3108
  const hasFrom = /\bfrom\s*=/.test(bodyText.toLowerCase());
2645
3109
  const filter = show.getChild('FilterClause');
2646
3110
  const hasSourceFilter = Boolean(filter && /@__source__/.test(this.maskStringsAndLineComments(this.nodeText(filter))));
3111
+ if (/^show_[a-z0-9]+_index$/.test(name)) {
3112
+ this.pushSemanticDiagnostic(identifier, `"${name}" is not supported by the DQL parser`, `show:${show.from}:unsupported`);
3113
+ return;
3114
+ }
2647
3115
  if ((name === 'show_tag_value' || name === 'show_tag_value_cardinality' || name === 'show_series_count_by_tag_value') && !hasKeyin) {
2648
3116
  this.pushSemanticDiagnostic(identifier, `"${name}" requires the named argument "keyin"`, `show:${show.from}:keyin`);
2649
3117
  }
@@ -2669,13 +3137,105 @@ export class Parser {
2669
3137
  }
2670
3138
  return children;
2671
3139
  }
3140
+ descendantsNamed(node, name) {
3141
+ const descendants = [];
3142
+ const visit = (current) => {
3143
+ let child = current.firstChild;
3144
+ while (child) {
3145
+ if (child.name === name)
3146
+ descendants.push(child);
3147
+ visit(child);
3148
+ child = child.nextSibling;
3149
+ }
3150
+ };
3151
+ visit(node);
3152
+ return descendants;
3153
+ }
3154
+ shiftOperandFunctionCalls(operand) {
3155
+ const querySource = operand.getChild('QuerySource');
3156
+ if (!querySource)
3157
+ return this.descendantsNamed(operand, 'FunctionCall');
3158
+ const query = querySource.getChild('Query');
3159
+ if (!query)
3160
+ return [];
3161
+ const calls = [];
3162
+ let clause = query.firstChild;
3163
+ while (clause) {
3164
+ if (clause.name === 'QueryClause') {
3165
+ const target = clause.getChild('TargetClause');
3166
+ if (target)
3167
+ calls.push(...this.descendantsNamed(target, 'FunctionCall'));
3168
+ }
3169
+ clause = clause.nextSibling;
3170
+ }
3171
+ return calls;
3172
+ }
3173
+ errorDescendantsWithText(node, text) {
3174
+ const errors = [];
3175
+ const expected = text.toLowerCase();
3176
+ const visit = (current) => {
3177
+ let child = current.firstChild;
3178
+ while (child) {
3179
+ if (child.type.isError && this.nodeText(child).toLowerCase() === expected)
3180
+ errors.push(child);
3181
+ visit(child);
3182
+ child = child.nextSibling;
3183
+ }
3184
+ };
3185
+ visit(node);
3186
+ return errors;
3187
+ }
3188
+ functionCallName(call) {
3189
+ const identifier = call.getChild('FunctionIdentifier');
3190
+ return identifier ? this.nodeText(identifier).toLowerCase() : null;
3191
+ }
3192
+ isPositiveFixedDuration(text) {
3193
+ const milliseconds = this.fixedDurationMilliseconds(text);
3194
+ return milliseconds !== null && milliseconds > 0;
3195
+ }
3196
+ fixedDurationMilliseconds(text) {
3197
+ const multipliers = {
3198
+ y: 365 * 24 * 60 * 60 * 1000,
3199
+ w: 7 * 24 * 60 * 60 * 1000,
3200
+ d: 24 * 60 * 60 * 1000,
3201
+ h: 60 * 60 * 1000,
3202
+ m: 60 * 1000,
3203
+ s: 1000,
3204
+ ms: 1,
3205
+ };
3206
+ const normalized = text.toLowerCase();
3207
+ const pattern = /(\d+)(ms|[ywdhms])/g;
3208
+ let total = 0;
3209
+ let consumed = 0;
3210
+ let part = pattern.exec(normalized);
3211
+ while (part) {
3212
+ if (part.index !== consumed)
3213
+ return null;
3214
+ const amount = Number(part[1]);
3215
+ const component = amount * multipliers[part[2]];
3216
+ if (!Number.isSafeInteger(amount) || !Number.isSafeInteger(component) || !Number.isSafeInteger(total + component))
3217
+ return null;
3218
+ total += component;
3219
+ consumed = pattern.lastIndex;
3220
+ part = pattern.exec(normalized);
3221
+ }
3222
+ return consumed === normalized.length ? total : null;
3223
+ }
2672
3224
  numericValue(node) {
2673
3225
  if (!node)
2674
3226
  return null;
2675
- const number = node.name === 'Number' ? node : node.getChild('Number');
3227
+ let valueNode = node;
3228
+ if (node.name === 'EvalFunctionArg') {
3229
+ const expression = node.getChild('EvalExpression');
3230
+ const atom = expression === null || expression === void 0 ? void 0 : expression.firstChild;
3231
+ if (!expression || (atom === null || atom === void 0 ? void 0 : atom.name) !== 'EvalAtom' || atom.nextSibling)
3232
+ return null;
3233
+ valueNode = atom;
3234
+ }
3235
+ const number = valueNode.name === 'Number' ? valueNode : valueNode.getChild('Number');
2676
3236
  if (!number)
2677
3237
  return null;
2678
- const sign = node.name !== 'Number' && node.getChild('Sub') ? -1 : 1;
3238
+ const sign = valueNode.name !== 'Number' && valueNode.getChild('Sub') ? -1 : 1;
2679
3239
  const value = sign * Number(this.nodeText(number).replace(/_/g, ''));
2680
3240
  return Number.isFinite(value) ? value : null;
2681
3241
  }