Compare commits

...

2 Commits

Author SHA1 Message Date
Tushar Vats
25ac727789 feat(querybuilder): read aggregation keys through the grammar
Grammar failures are only logged, so no expression that runs today starts
failing; a heatmap aggregation will reject them.
2026-11-03 19:24:34 +05:30
Tushar Vats
ff637baae5 feat(querybuilder): add the aggregation expression grammar 2026-11-03 19:24:34 +05:30
17 changed files with 2852 additions and 4 deletions

View File

@@ -0,0 +1,103 @@
grammar AggregationExpression;
/*
* Parser Rules
*/
// Expressions after the first are ignored, as the rewriter always has.
query
: expression ( COMMA expression )* EOF
;
/*
* Aggregation calls combined with arithmetic. Earlier alternatives bind tighter.
* E.g.: count(), p99(duration_nano), sum(a) + sum(b), count() / 60, -(sum(a) - sum(b))
*/
expression
: (PLUS | MINUS) expression # unaryExpression
| expression (STAR | SLASH | PERCENT) expression # multiplicativeExpression
| expression (PLUS | MINUS) expression # additiveExpression
| LPAREN expression RPAREN # parenthesizedExpression
| functionCall # functionExpression
| NUMBER # numberExpression
;
/*
* E.g.: count(), sum(bytes), count_distinct(a, b), countIf(level = 'error'),
* sumIf(bytes, status IN (500, 503))
*/
functionCall
: KEY LPAREN argumentList? RPAREN
;
argumentList
: argument ( COMMA argument )*
;
/*
* An argument is a field key, or for a *If combinator a filter predicate, which
* the filter grammar validates. Commas separate arguments only at the top level.
*/
argument
: argumentToken+
;
argumentToken
: KEY
| NUMBER
| QUOTED_TEXT
| OPERATOR
| PLUS | MINUS | STAR | SLASH | PERCENT
| LPAREN ( argumentToken | COMMA )* RPAREN
| LBRACK ( argumentToken | COMMA )* RBRACK
| OTHER
;
/*
* Lexer Rules
*/
LPAREN : '(' ;
RPAREN : ')' ;
LBRACK : '[' ;
RBRACK : ']' ;
COMMA : ',' ;
PLUS : '+' ;
MINUS : '-' ;
STAR : '*' ;
SLASH : '/' ;
PERCENT : '%' ;
OPERATOR
: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
;
// No leading sign, so `count()-10` reads as a subtraction.
NUMBER
: DIGIT+ ('.' DIGIT*)? ([eE] [+-]? DIGIT+)?
| '.' DIGIT+ ([eE] [+-]? DIGIT+)?
;
QUOTED_TEXT
: '"' ( ~["\\] | '\\' . )* '"'
| '\'' ( ~['\\] | '\\' . )* '\''
;
// Same key syntax as the filter grammar.
fragment SEGMENT : [a-zA-Z$_@{#] [a-zA-Z0-9$_@#{}:\-/]* ;
fragment EMPTY_BRACKS : '[' ']' ;
fragment OLD_JSON_BRACKS : '[' '*' ']' ;
KEY
: SEGMENT ( '.' SEGMENT | EMPTY_BRACKS | OLD_JSON_BRACKS | '.' DIGIT+ )*
;
WS
: [ \t\r\n]+ -> skip
;
fragment DIGIT : [0-9] ;
// Anything else is left for a predicate's own grammar to judge.
OTHER : . ;

View File

@@ -0,0 +1,49 @@
token literal names:
null
'('
')'
'['
']'
','
'+'
'-'
'*'
'/'
'%'
null
null
null
null
null
null
token symbolic names:
null
LPAREN
RPAREN
LBRACK
RBRACK
COMMA
PLUS
MINUS
STAR
SLASH
PERCENT
OPERATOR
NUMBER
QUOTED_TEXT
KEY
WS
OTHER
rule names:
query
expression
functionCall
argumentList
argument
argumentToken
atn:
[4, 1, 16, 95, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 5, 0, 16, 8, 0, 10, 0, 12, 0, 19, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 32, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 40, 8, 1, 10, 1, 12, 1, 43, 9, 1, 1, 2, 1, 2, 1, 2, 3, 2, 48, 8, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 5, 3, 55, 8, 3, 10, 3, 12, 3, 58, 9, 3, 1, 4, 4, 4, 61, 8, 4, 11, 4, 12, 4, 62, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 77, 8, 5, 10, 5, 12, 5, 80, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 86, 8, 5, 10, 5, 12, 5, 89, 9, 5, 1, 5, 1, 5, 3, 5, 93, 8, 5, 1, 5, 0, 1, 2, 6, 0, 2, 4, 6, 8, 10, 0, 2, 1, 0, 6, 7, 1, 0, 8, 10, 112, 0, 12, 1, 0, 0, 0, 2, 31, 1, 0, 0, 0, 4, 44, 1, 0, 0, 0, 6, 51, 1, 0, 0, 0, 8, 60, 1, 0, 0, 0, 10, 92, 1, 0, 0, 0, 12, 17, 3, 2, 1, 0, 13, 14, 5, 5, 0, 0, 14, 16, 3, 2, 1, 0, 15, 13, 1, 0, 0, 0, 16, 19, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 20, 21, 5, 0, 0, 1, 21, 1, 1, 0, 0, 0, 22, 23, 6, 1, -1, 0, 23, 24, 7, 0, 0, 0, 24, 32, 3, 2, 1, 6, 25, 26, 5, 1, 0, 0, 26, 27, 3, 2, 1, 0, 27, 28, 5, 2, 0, 0, 28, 32, 1, 0, 0, 0, 29, 32, 3, 4, 2, 0, 30, 32, 5, 12, 0, 0, 31, 22, 1, 0, 0, 0, 31, 25, 1, 0, 0, 0, 31, 29, 1, 0, 0, 0, 31, 30, 1, 0, 0, 0, 32, 41, 1, 0, 0, 0, 33, 34, 10, 5, 0, 0, 34, 35, 7, 1, 0, 0, 35, 40, 3, 2, 1, 6, 36, 37, 10, 4, 0, 0, 37, 38, 7, 0, 0, 0, 38, 40, 3, 2, 1, 5, 39, 33, 1, 0, 0, 0, 39, 36, 1, 0, 0, 0, 40, 43, 1, 0, 0, 0, 41, 39, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 3, 1, 0, 0, 0, 43, 41, 1, 0, 0, 0, 44, 45, 5, 14, 0, 0, 45, 47, 5, 1, 0, 0, 46, 48, 3, 6, 3, 0, 47, 46, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 50, 5, 2, 0, 0, 50, 5, 1, 0, 0, 0, 51, 56, 3, 8, 4, 0, 52, 53, 5, 5, 0, 0, 53, 55, 3, 8, 4, 0, 54, 52, 1, 0, 0, 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 7, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 61, 3, 10, 5, 0, 60, 59, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 60, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 9, 1, 0, 0, 0, 64, 93, 5, 14, 0, 0, 65, 93, 5, 12, 0, 0, 66, 93, 5, 13, 0, 0, 67, 93, 5, 11, 0, 0, 68, 93, 5, 6, 0, 0, 69, 93, 5, 7, 0, 0, 70, 93, 5, 8, 0, 0, 71, 93, 5, 9, 0, 0, 72, 93, 5, 10, 0, 0, 73, 78, 5, 1, 0, 0, 74, 77, 3, 10, 5, 0, 75, 77, 5, 5, 0, 0, 76, 74, 1, 0, 0, 0, 76, 75, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 81, 1, 0, 0, 0, 80, 78, 1, 0, 0, 0, 81, 93, 5, 2, 0, 0, 82, 87, 5, 3, 0, 0, 83, 86, 3, 10, 5, 0, 84, 86, 5, 5, 0, 0, 85, 83, 1, 0, 0, 0, 85, 84, 1, 0, 0, 0, 86, 89, 1, 0, 0, 0, 87, 85, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 90, 1, 0, 0, 0, 89, 87, 1, 0, 0, 0, 90, 93, 5, 4, 0, 0, 91, 93, 5, 16, 0, 0, 92, 64, 1, 0, 0, 0, 92, 65, 1, 0, 0, 0, 92, 66, 1, 0, 0, 0, 92, 67, 1, 0, 0, 0, 92, 68, 1, 0, 0, 0, 92, 69, 1, 0, 0, 0, 92, 70, 1, 0, 0, 0, 92, 71, 1, 0, 0, 0, 92, 72, 1, 0, 0, 0, 92, 73, 1, 0, 0, 0, 92, 82, 1, 0, 0, 0, 92, 91, 1, 0, 0, 0, 93, 11, 1, 0, 0, 0, 12, 17, 31, 39, 41, 47, 56, 62, 76, 78, 85, 87, 92]

View File

@@ -0,0 +1,26 @@
LPAREN=1
RPAREN=2
LBRACK=3
RBRACK=4
COMMA=5
PLUS=6
MINUS=7
STAR=8
SLASH=9
PERCENT=10
OPERATOR=11
NUMBER=12
QUOTED_TEXT=13
KEY=14
WS=15
OTHER=16
'('=1
')'=2
'['=3
']'=4
','=5
'+'=6
'-'=7
'*'=8
'/'=9
'%'=10

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,26 @@
LPAREN=1
RPAREN=2
LBRACK=3
RBRACK=4
COMMA=5
PLUS=6
MINUS=7
STAR=8
SLASH=9
PERCENT=10
OPERATOR=11
NUMBER=12
QUOTED_TEXT=13
KEY=14
WS=15
OTHER=16
'('=1
')'=2
'['=3
']'=4
','=5
'+'=6
'-'=7
'*'=8
'/'=9
'%'=10

View File

@@ -0,0 +1,92 @@
// Code generated from grammar/AggregationExpression.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // AggregationExpression
import "github.com/antlr4-go/antlr/v4"
// BaseAggregationExpressionListener is a complete listener for a parse tree produced by AggregationExpressionParser.
type BaseAggregationExpressionListener struct{}
var _ AggregationExpressionListener = &BaseAggregationExpressionListener{}
// VisitTerminal is called when a terminal node is visited.
func (s *BaseAggregationExpressionListener) VisitTerminal(node antlr.TerminalNode) {}
// VisitErrorNode is called when an error node is visited.
func (s *BaseAggregationExpressionListener) VisitErrorNode(node antlr.ErrorNode) {}
// EnterEveryRule is called when any rule is entered.
func (s *BaseAggregationExpressionListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
// ExitEveryRule is called when any rule is exited.
func (s *BaseAggregationExpressionListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
// EnterQuery is called when production query is entered.
func (s *BaseAggregationExpressionListener) EnterQuery(ctx *QueryContext) {}
// ExitQuery is called when production query is exited.
func (s *BaseAggregationExpressionListener) ExitQuery(ctx *QueryContext) {}
// EnterParenthesizedExpression is called when production parenthesizedExpression is entered.
func (s *BaseAggregationExpressionListener) EnterParenthesizedExpression(ctx *ParenthesizedExpressionContext) {
}
// ExitParenthesizedExpression is called when production parenthesizedExpression is exited.
func (s *BaseAggregationExpressionListener) ExitParenthesizedExpression(ctx *ParenthesizedExpressionContext) {
}
// EnterAdditiveExpression is called when production additiveExpression is entered.
func (s *BaseAggregationExpressionListener) EnterAdditiveExpression(ctx *AdditiveExpressionContext) {}
// ExitAdditiveExpression is called when production additiveExpression is exited.
func (s *BaseAggregationExpressionListener) ExitAdditiveExpression(ctx *AdditiveExpressionContext) {}
// EnterNumberExpression is called when production numberExpression is entered.
func (s *BaseAggregationExpressionListener) EnterNumberExpression(ctx *NumberExpressionContext) {}
// ExitNumberExpression is called when production numberExpression is exited.
func (s *BaseAggregationExpressionListener) ExitNumberExpression(ctx *NumberExpressionContext) {}
// EnterMultiplicativeExpression is called when production multiplicativeExpression is entered.
func (s *BaseAggregationExpressionListener) EnterMultiplicativeExpression(ctx *MultiplicativeExpressionContext) {
}
// ExitMultiplicativeExpression is called when production multiplicativeExpression is exited.
func (s *BaseAggregationExpressionListener) ExitMultiplicativeExpression(ctx *MultiplicativeExpressionContext) {
}
// EnterUnaryExpression is called when production unaryExpression is entered.
func (s *BaseAggregationExpressionListener) EnterUnaryExpression(ctx *UnaryExpressionContext) {}
// ExitUnaryExpression is called when production unaryExpression is exited.
func (s *BaseAggregationExpressionListener) ExitUnaryExpression(ctx *UnaryExpressionContext) {}
// EnterFunctionExpression is called when production functionExpression is entered.
func (s *BaseAggregationExpressionListener) EnterFunctionExpression(ctx *FunctionExpressionContext) {}
// ExitFunctionExpression is called when production functionExpression is exited.
func (s *BaseAggregationExpressionListener) ExitFunctionExpression(ctx *FunctionExpressionContext) {}
// EnterFunctionCall is called when production functionCall is entered.
func (s *BaseAggregationExpressionListener) EnterFunctionCall(ctx *FunctionCallContext) {}
// ExitFunctionCall is called when production functionCall is exited.
func (s *BaseAggregationExpressionListener) ExitFunctionCall(ctx *FunctionCallContext) {}
// EnterArgumentList is called when production argumentList is entered.
func (s *BaseAggregationExpressionListener) EnterArgumentList(ctx *ArgumentListContext) {}
// ExitArgumentList is called when production argumentList is exited.
func (s *BaseAggregationExpressionListener) ExitArgumentList(ctx *ArgumentListContext) {}
// EnterArgument is called when production argument is entered.
func (s *BaseAggregationExpressionListener) EnterArgument(ctx *ArgumentContext) {}
// ExitArgument is called when production argument is exited.
func (s *BaseAggregationExpressionListener) ExitArgument(ctx *ArgumentContext) {}
// EnterArgumentToken is called when production argumentToken is entered.
func (s *BaseAggregationExpressionListener) EnterArgumentToken(ctx *ArgumentTokenContext) {}
// ExitArgumentToken is called when production argumentToken is exited.
func (s *BaseAggregationExpressionListener) ExitArgumentToken(ctx *ArgumentTokenContext) {}

View File

@@ -0,0 +1,53 @@
// Code generated from grammar/AggregationExpression.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // AggregationExpression
import "github.com/antlr4-go/antlr/v4"
type BaseAggregationExpressionVisitor struct {
*antlr.BaseParseTreeVisitor
}
func (v *BaseAggregationExpressionVisitor) VisitQuery(ctx *QueryContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitParenthesizedExpression(ctx *ParenthesizedExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitAdditiveExpression(ctx *AdditiveExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitNumberExpression(ctx *NumberExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitMultiplicativeExpression(ctx *MultiplicativeExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitUnaryExpression(ctx *UnaryExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitFunctionExpression(ctx *FunctionExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitFunctionCall(ctx *FunctionCallContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitArgumentList(ctx *ArgumentListContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitArgument(ctx *ArgumentContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAggregationExpressionVisitor) VisitArgumentToken(ctx *ArgumentTokenContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@@ -0,0 +1,204 @@
// Code generated from grammar/AggregationExpression.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser
import (
"fmt"
"github.com/antlr4-go/antlr/v4"
"sync"
"unicode"
)
// Suppress unused import error
var _ = fmt.Printf
var _ = sync.Once{}
var _ = unicode.IsLetter
type AggregationExpressionLexer struct {
*antlr.BaseLexer
channelNames []string
modeNames []string
// TODO: EOF string
}
var AggregationExpressionLexerLexerStaticData struct {
once sync.Once
serializedATN []int32
ChannelNames []string
ModeNames []string
LiteralNames []string
SymbolicNames []string
RuleNames []string
PredictionContextCache *antlr.PredictionContextCache
atn *antlr.ATN
decisionToDFA []*antlr.DFA
}
func aggregationexpressionlexerLexerInit() {
staticData := &AggregationExpressionLexerLexerStaticData
staticData.ChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
staticData.ModeNames = []string{
"DEFAULT_MODE",
}
staticData.LiteralNames = []string{
"", "'('", "')'", "'['", "']'", "','", "'+'", "'-'", "'*'", "'/'", "'%'",
}
staticData.SymbolicNames = []string{
"", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "PLUS", "MINUS",
"STAR", "SLASH", "PERCENT", "OPERATOR", "NUMBER", "QUOTED_TEXT", "KEY",
"WS", "OTHER",
}
staticData.RuleNames = []string{
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "PLUS", "MINUS", "STAR",
"SLASH", "PERCENT", "OPERATOR", "NUMBER", "QUOTED_TEXT", "SEGMENT",
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "OTHER",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 0, 16, 183, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 1, 0, 1,
0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1,
6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10,
1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 75, 8, 10,
1, 11, 4, 11, 78, 8, 11, 11, 11, 12, 11, 79, 1, 11, 1, 11, 5, 11, 84, 8,
11, 10, 11, 12, 11, 87, 9, 11, 3, 11, 89, 8, 11, 1, 11, 1, 11, 3, 11, 93,
8, 11, 1, 11, 4, 11, 96, 8, 11, 11, 11, 12, 11, 97, 3, 11, 100, 8, 11,
1, 11, 1, 11, 4, 11, 104, 8, 11, 11, 11, 12, 11, 105, 1, 11, 1, 11, 3,
11, 110, 8, 11, 1, 11, 4, 11, 113, 8, 11, 11, 11, 12, 11, 114, 3, 11, 117,
8, 11, 3, 11, 119, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 5, 12, 125, 8, 12,
10, 12, 12, 12, 128, 9, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 5, 12, 135,
8, 12, 10, 12, 12, 12, 138, 9, 12, 1, 12, 3, 12, 141, 8, 12, 1, 13, 1,
13, 5, 13, 145, 8, 13, 10, 13, 12, 13, 148, 9, 13, 1, 14, 1, 14, 1, 14,
1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1,
16, 4, 16, 164, 8, 16, 11, 16, 12, 16, 165, 5, 16, 168, 8, 16, 10, 16,
12, 16, 171, 9, 16, 1, 17, 4, 17, 174, 8, 17, 11, 17, 12, 17, 175, 1, 17,
1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 0, 0, 20, 1, 1, 3, 2, 5, 3, 7, 4, 9,
5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 0, 29,
0, 31, 0, 33, 14, 35, 15, 37, 0, 39, 16, 1, 0, 8, 2, 0, 69, 69, 101, 101,
2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 4, 0,
35, 36, 64, 90, 95, 95, 97, 123, 7, 0, 35, 36, 45, 45, 47, 58, 64, 90,
95, 95, 97, 123, 125, 125, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 208,
0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0,
0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0,
0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0,
0, 0, 0, 25, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 39, 1,
0, 0, 0, 1, 41, 1, 0, 0, 0, 3, 43, 1, 0, 0, 0, 5, 45, 1, 0, 0, 0, 7, 47,
1, 0, 0, 0, 9, 49, 1, 0, 0, 0, 11, 51, 1, 0, 0, 0, 13, 53, 1, 0, 0, 0,
15, 55, 1, 0, 0, 0, 17, 57, 1, 0, 0, 0, 19, 59, 1, 0, 0, 0, 21, 74, 1,
0, 0, 0, 23, 118, 1, 0, 0, 0, 25, 140, 1, 0, 0, 0, 27, 142, 1, 0, 0, 0,
29, 149, 1, 0, 0, 0, 31, 152, 1, 0, 0, 0, 33, 156, 1, 0, 0, 0, 35, 173,
1, 0, 0, 0, 37, 179, 1, 0, 0, 0, 39, 181, 1, 0, 0, 0, 41, 42, 5, 40, 0,
0, 42, 2, 1, 0, 0, 0, 43, 44, 5, 41, 0, 0, 44, 4, 1, 0, 0, 0, 45, 46, 5,
91, 0, 0, 46, 6, 1, 0, 0, 0, 47, 48, 5, 93, 0, 0, 48, 8, 1, 0, 0, 0, 49,
50, 5, 44, 0, 0, 50, 10, 1, 0, 0, 0, 51, 52, 5, 43, 0, 0, 52, 12, 1, 0,
0, 0, 53, 54, 5, 45, 0, 0, 54, 14, 1, 0, 0, 0, 55, 56, 5, 42, 0, 0, 56,
16, 1, 0, 0, 0, 57, 58, 5, 47, 0, 0, 58, 18, 1, 0, 0, 0, 59, 60, 5, 37,
0, 0, 60, 20, 1, 0, 0, 0, 61, 75, 5, 61, 0, 0, 62, 63, 5, 61, 0, 0, 63,
75, 5, 61, 0, 0, 64, 65, 5, 33, 0, 0, 65, 75, 5, 61, 0, 0, 66, 67, 5, 60,
0, 0, 67, 75, 5, 62, 0, 0, 68, 75, 5, 60, 0, 0, 69, 70, 5, 60, 0, 0, 70,
75, 5, 61, 0, 0, 71, 75, 5, 62, 0, 0, 72, 73, 5, 62, 0, 0, 73, 75, 5, 61,
0, 0, 74, 61, 1, 0, 0, 0, 74, 62, 1, 0, 0, 0, 74, 64, 1, 0, 0, 0, 74, 66,
1, 0, 0, 0, 74, 68, 1, 0, 0, 0, 74, 69, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0,
74, 72, 1, 0, 0, 0, 75, 22, 1, 0, 0, 0, 76, 78, 3, 37, 18, 0, 77, 76, 1,
0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 77, 1, 0, 0, 0, 79, 80, 1, 0, 0, 0, 80,
88, 1, 0, 0, 0, 81, 85, 5, 46, 0, 0, 82, 84, 3, 37, 18, 0, 83, 82, 1, 0,
0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 89,
1, 0, 0, 0, 87, 85, 1, 0, 0, 0, 88, 81, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0,
89, 99, 1, 0, 0, 0, 90, 92, 7, 0, 0, 0, 91, 93, 7, 1, 0, 0, 92, 91, 1,
0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 95, 1, 0, 0, 0, 94, 96, 3, 37, 18, 0,
95, 94, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 97, 98, 1,
0, 0, 0, 98, 100, 1, 0, 0, 0, 99, 90, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0,
100, 119, 1, 0, 0, 0, 101, 103, 5, 46, 0, 0, 102, 104, 3, 37, 18, 0, 103,
102, 1, 0, 0, 0, 104, 105, 1, 0, 0, 0, 105, 103, 1, 0, 0, 0, 105, 106,
1, 0, 0, 0, 106, 116, 1, 0, 0, 0, 107, 109, 7, 0, 0, 0, 108, 110, 7, 1,
0, 0, 109, 108, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 112, 1, 0, 0, 0,
111, 113, 3, 37, 18, 0, 112, 111, 1, 0, 0, 0, 113, 114, 1, 0, 0, 0, 114,
112, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, 0, 0, 116, 107,
1, 0, 0, 0, 116, 117, 1, 0, 0, 0, 117, 119, 1, 0, 0, 0, 118, 77, 1, 0,
0, 0, 118, 101, 1, 0, 0, 0, 119, 24, 1, 0, 0, 0, 120, 126, 5, 34, 0, 0,
121, 125, 8, 2, 0, 0, 122, 123, 5, 92, 0, 0, 123, 125, 9, 0, 0, 0, 124,
121, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 125, 128, 1, 0, 0, 0, 126, 124,
1, 0, 0, 0, 126, 127, 1, 0, 0, 0, 127, 129, 1, 0, 0, 0, 128, 126, 1, 0,
0, 0, 129, 141, 5, 34, 0, 0, 130, 136, 5, 39, 0, 0, 131, 135, 8, 3, 0,
0, 132, 133, 5, 92, 0, 0, 133, 135, 9, 0, 0, 0, 134, 131, 1, 0, 0, 0, 134,
132, 1, 0, 0, 0, 135, 138, 1, 0, 0, 0, 136, 134, 1, 0, 0, 0, 136, 137,
1, 0, 0, 0, 137, 139, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 139, 141, 5, 39,
0, 0, 140, 120, 1, 0, 0, 0, 140, 130, 1, 0, 0, 0, 141, 26, 1, 0, 0, 0,
142, 146, 7, 4, 0, 0, 143, 145, 7, 5, 0, 0, 144, 143, 1, 0, 0, 0, 145,
148, 1, 0, 0, 0, 146, 144, 1, 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 28, 1,
0, 0, 0, 148, 146, 1, 0, 0, 0, 149, 150, 5, 91, 0, 0, 150, 151, 5, 93,
0, 0, 151, 30, 1, 0, 0, 0, 152, 153, 5, 91, 0, 0, 153, 154, 5, 42, 0, 0,
154, 155, 5, 93, 0, 0, 155, 32, 1, 0, 0, 0, 156, 169, 3, 27, 13, 0, 157,
158, 5, 46, 0, 0, 158, 168, 3, 27, 13, 0, 159, 168, 3, 29, 14, 0, 160,
168, 3, 31, 15, 0, 161, 163, 5, 46, 0, 0, 162, 164, 3, 37, 18, 0, 163,
162, 1, 0, 0, 0, 164, 165, 1, 0, 0, 0, 165, 163, 1, 0, 0, 0, 165, 166,
1, 0, 0, 0, 166, 168, 1, 0, 0, 0, 167, 157, 1, 0, 0, 0, 167, 159, 1, 0,
0, 0, 167, 160, 1, 0, 0, 0, 167, 161, 1, 0, 0, 0, 168, 171, 1, 0, 0, 0,
169, 167, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 34, 1, 0, 0, 0, 171, 169,
1, 0, 0, 0, 172, 174, 7, 6, 0, 0, 173, 172, 1, 0, 0, 0, 174, 175, 1, 0,
0, 0, 175, 173, 1, 0, 0, 0, 175, 176, 1, 0, 0, 0, 176, 177, 1, 0, 0, 0,
177, 178, 6, 17, 0, 0, 178, 36, 1, 0, 0, 0, 179, 180, 7, 7, 0, 0, 180,
38, 1, 0, 0, 0, 181, 182, 9, 0, 0, 0, 182, 40, 1, 0, 0, 0, 23, 0, 74, 79,
85, 88, 92, 97, 99, 105, 109, 114, 116, 118, 124, 126, 134, 136, 140, 146,
165, 167, 169, 175, 1, 6, 0, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
atn := staticData.atn
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
decisionToDFA := staticData.decisionToDFA
for index, state := range atn.DecisionToState {
decisionToDFA[index] = antlr.NewDFA(state, index)
}
}
// AggregationExpressionLexerInit initializes any static state used to implement AggregationExpressionLexer. By default the
// static state used to implement the lexer is lazily initialized during the first call to
// NewAggregationExpressionLexer(). You can call this function if you wish to initialize the static state ahead
// of time.
func AggregationExpressionLexerInit() {
staticData := &AggregationExpressionLexerLexerStaticData
staticData.once.Do(aggregationexpressionlexerLexerInit)
}
// NewAggregationExpressionLexer produces a new lexer instance for the optional input antlr.CharStream.
func NewAggregationExpressionLexer(input antlr.CharStream) *AggregationExpressionLexer {
AggregationExpressionLexerInit()
l := new(AggregationExpressionLexer)
l.BaseLexer = antlr.NewBaseLexer(input)
staticData := &AggregationExpressionLexerLexerStaticData
l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
l.channelNames = staticData.ChannelNames
l.modeNames = staticData.ModeNames
l.RuleNames = staticData.RuleNames
l.LiteralNames = staticData.LiteralNames
l.SymbolicNames = staticData.SymbolicNames
l.GrammarFileName = "AggregationExpression.g4"
// TODO: l.EOF = antlr.TokenEOF
return l
}
// AggregationExpressionLexer tokens.
const (
AggregationExpressionLexerLPAREN = 1
AggregationExpressionLexerRPAREN = 2
AggregationExpressionLexerLBRACK = 3
AggregationExpressionLexerRBRACK = 4
AggregationExpressionLexerCOMMA = 5
AggregationExpressionLexerPLUS = 6
AggregationExpressionLexerMINUS = 7
AggregationExpressionLexerSTAR = 8
AggregationExpressionLexerSLASH = 9
AggregationExpressionLexerPERCENT = 10
AggregationExpressionLexerOPERATOR = 11
AggregationExpressionLexerNUMBER = 12
AggregationExpressionLexerQUOTED_TEXT = 13
AggregationExpressionLexerKEY = 14
AggregationExpressionLexerWS = 15
AggregationExpressionLexerOTHER = 16
)

View File

@@ -0,0 +1,76 @@
// Code generated from grammar/AggregationExpression.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // AggregationExpression
import "github.com/antlr4-go/antlr/v4"
// AggregationExpressionListener is a complete listener for a parse tree produced by AggregationExpressionParser.
type AggregationExpressionListener interface {
antlr.ParseTreeListener
// EnterQuery is called when entering the query production.
EnterQuery(c *QueryContext)
// EnterParenthesizedExpression is called when entering the parenthesizedExpression production.
EnterParenthesizedExpression(c *ParenthesizedExpressionContext)
// EnterAdditiveExpression is called when entering the additiveExpression production.
EnterAdditiveExpression(c *AdditiveExpressionContext)
// EnterNumberExpression is called when entering the numberExpression production.
EnterNumberExpression(c *NumberExpressionContext)
// EnterMultiplicativeExpression is called when entering the multiplicativeExpression production.
EnterMultiplicativeExpression(c *MultiplicativeExpressionContext)
// EnterUnaryExpression is called when entering the unaryExpression production.
EnterUnaryExpression(c *UnaryExpressionContext)
// EnterFunctionExpression is called when entering the functionExpression production.
EnterFunctionExpression(c *FunctionExpressionContext)
// EnterFunctionCall is called when entering the functionCall production.
EnterFunctionCall(c *FunctionCallContext)
// EnterArgumentList is called when entering the argumentList production.
EnterArgumentList(c *ArgumentListContext)
// EnterArgument is called when entering the argument production.
EnterArgument(c *ArgumentContext)
// EnterArgumentToken is called when entering the argumentToken production.
EnterArgumentToken(c *ArgumentTokenContext)
// ExitQuery is called when exiting the query production.
ExitQuery(c *QueryContext)
// ExitParenthesizedExpression is called when exiting the parenthesizedExpression production.
ExitParenthesizedExpression(c *ParenthesizedExpressionContext)
// ExitAdditiveExpression is called when exiting the additiveExpression production.
ExitAdditiveExpression(c *AdditiveExpressionContext)
// ExitNumberExpression is called when exiting the numberExpression production.
ExitNumberExpression(c *NumberExpressionContext)
// ExitMultiplicativeExpression is called when exiting the multiplicativeExpression production.
ExitMultiplicativeExpression(c *MultiplicativeExpressionContext)
// ExitUnaryExpression is called when exiting the unaryExpression production.
ExitUnaryExpression(c *UnaryExpressionContext)
// ExitFunctionExpression is called when exiting the functionExpression production.
ExitFunctionExpression(c *FunctionExpressionContext)
// ExitFunctionCall is called when exiting the functionCall production.
ExitFunctionCall(c *FunctionCallContext)
// ExitArgumentList is called when exiting the argumentList production.
ExitArgumentList(c *ArgumentListContext)
// ExitArgument is called when exiting the argument production.
ExitArgument(c *ArgumentContext)
// ExitArgumentToken is called when exiting the argumentToken production.
ExitArgumentToken(c *ArgumentTokenContext)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
// Code generated from grammar/AggregationExpression.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // AggregationExpression
import "github.com/antlr4-go/antlr/v4"
// A complete Visitor for a parse tree produced by AggregationExpressionParser.
type AggregationExpressionVisitor interface {
antlr.ParseTreeVisitor
// Visit a parse tree produced by AggregationExpressionParser#query.
VisitQuery(ctx *QueryContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#parenthesizedExpression.
VisitParenthesizedExpression(ctx *ParenthesizedExpressionContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#additiveExpression.
VisitAdditiveExpression(ctx *AdditiveExpressionContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#numberExpression.
VisitNumberExpression(ctx *NumberExpressionContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#multiplicativeExpression.
VisitMultiplicativeExpression(ctx *MultiplicativeExpressionContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#unaryExpression.
VisitUnaryExpression(ctx *UnaryExpressionContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#functionExpression.
VisitFunctionExpression(ctx *FunctionExpressionContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#functionCall.
VisitFunctionCall(ctx *FunctionCallContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#argumentList.
VisitArgumentList(ctx *ArgumentListContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#argument.
VisitArgument(ctx *ArgumentContext) interface{}
// Visit a parse tree produced by AggregationExpressionParser#argumentToken.
VisitArgumentToken(ctx *ArgumentTokenContext) interface{}
}

View File

@@ -0,0 +1,101 @@
package querybuilder
import (
"strings"
"github.com/SigNoz/signoz/pkg/errors"
grammar "github.com/SigNoz/signoz/pkg/parser/aggregationexpression/grammar"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/antlr4-go/antlr/v4"
)
// AggregationKeySelectors returns the keys an aggregation expression reads: its
// field arguments and the keys in a combinator's predicate. An expression the
// grammar rejects falls back to every key token in it.
func AggregationKeySelectors(expression string) []*telemetrytypes.FieldKeySelector {
calls, err := parseAggregationExpression(expression)
if err != nil {
return QueryStringToKeysSelectors(expression)
}
var selectors []*telemetrytypes.FieldKeySelector
for _, args := range calls {
for _, arg := range args {
selectors = append(selectors, QueryStringToKeysSelectors(arg)...)
}
}
return selectors
}
// parseAggregationExpression parses an aggregation expression into the
// arguments of each function call, as written. It checks each call as the
// rewriter does: a known function and, for one that requires arguments, an
// argument count in range. Only the first of comma-separated expressions is
// read, since it is the only one the rewriter renders.
func parseAggregationExpression(expression string) ([][]string, error) {
input := antlr.NewInputStream(expression)
lexer := grammar.NewAggregationExpressionLexer(input)
lexerErrors := NewErrorListener()
lexer.RemoveErrorListeners()
lexer.AddErrorListener(lexerErrors)
parser := grammar.NewAggregationExpressionParser(antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel))
parserErrors := NewErrorListener()
parser.RemoveErrorListeners()
parser.AddErrorListener(parserErrors)
query := parser.Query()
if syntaxErrors := append(lexerErrors.SyntaxErrors, parserErrors.SyntaxErrors...); len(syntaxErrors) > 0 {
messages := make([]string, 0, len(syntaxErrors))
for _, syntaxError := range syntaxErrors {
messages = append(messages, syntaxError.Error())
}
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "syntax error in aggregation expression %q", expression).
WithAdditional(messages...)
}
var calls [][]string
for _, functionCall := range collectFunctionCalls(query.Expression(0), nil) {
name := strings.ToLower(functionCall.KEY().GetText())
function, ok := AggreFuncMap[valuer.NewString(name)]
if !ok {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "unrecognized function: %s", name)
}
var args []string
if argumentList := functionCall.ArgumentList(); argumentList != nil {
for _, argument := range argumentList.AllArgument() {
args = append(args, input.GetText(argument.GetStart().GetStart(), argument.GetStop().GetStop()))
}
}
if function.RequireArgs && (len(args) < function.MinArgs || len(args) > function.MaxArgs) {
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid number of arguments for %q: %d", name, len(args))
}
calls = append(calls, args)
}
return calls, nil
}
func collectFunctionCalls(expression grammar.IExpressionContext, calls []grammar.IFunctionCallContext) []grammar.IFunctionCallContext {
switch e := expression.(type) {
case *grammar.FunctionExpressionContext:
calls = append(calls, e.FunctionCall())
case *grammar.UnaryExpressionContext:
calls = collectFunctionCalls(e.Expression(), calls)
case *grammar.ParenthesizedExpressionContext:
calls = collectFunctionCalls(e.Expression(), calls)
case *grammar.AdditiveExpressionContext:
for _, operand := range e.AllExpression() {
calls = collectFunctionCalls(operand, calls)
}
case *grammar.MultiplicativeExpressionContext:
for _, operand := range e.AllExpression() {
calls = collectFunctionCalls(operand, calls)
}
}
return calls
}

View File

@@ -57,6 +57,10 @@ func (r *aggExprRewriter) Rewrite(
keys map[string][]*telemetrytypes.TelemetryFieldKey,
) (string, []any, error) {
if _, err := parseAggregationExpression(expr); err != nil {
r.logger.WarnContext(ctx, "aggregation expression failed grammar validation", slog.String("expression", expr), errors.Attr(err))
}
wrapped := fmt.Sprintf("SELECT %s", expr)
p := chparser.NewParser(wrapped)
stmts, err := p.ParseStmts()

View File

@@ -128,7 +128,7 @@ func getKeySelectors(query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]) []
for idx := range query.Aggregations {
aggExpr := query.Aggregations[idx]
selectors := querybuilder.QueryStringToKeysSelectors(aggExpr.Expression)
selectors := querybuilder.AggregationKeySelectors(aggExpr.Expression)
keySelectors = append(keySelectors, selectors...)
}

View File

@@ -179,7 +179,7 @@ func getKeySelectors(query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation], bo
for idx := range query.Aggregations {
aggExpr := query.Aggregations[idx]
selectors := querybuilder.QueryStringToKeysSelectors(aggExpr.Expression)
selectors := querybuilder.AggregationKeySelectors(aggExpr.Expression)
keySelectors = append(keySelectors, selectors...)
}

View File

@@ -192,7 +192,7 @@ func getKeySelectors(query qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation])
for idx := range query.Aggregations {
aggExpr := query.Aggregations[idx]
selectors := querybuilder.QueryStringToKeysSelectors(aggExpr.Expression)
selectors := querybuilder.AggregationKeySelectors(aggExpr.Expression)
keySelectors = append(keySelectors, selectors...)
}

View File

@@ -582,7 +582,7 @@ func (b *traceOperatorCTEBuilder) getKeySelectors() []*telemetrytypes.FieldKeySe
var keySelectors []*telemetrytypes.FieldKeySelector
for _, agg := range b.operator.Aggregations {
selectors := querybuilder.QueryStringToKeysSelectors(agg.Expression)
selectors := querybuilder.AggregationKeySelectors(agg.Expression)
keySelectors = append(keySelectors, selectors...)
}