@ -5,22 +5,18 @@ import {
AggregateModifier ,
AggregateOp ,
BinaryExpr ,
BinModifiers ,
Expr ,
BoolModifier ,
FunctionCall ,
FunctionCallArgs ,
FunctionCallBody ,
FunctionIdentifier ,
GroupingLabel ,
GroupingLabelList ,
GroupingLabels ,
Identifier ,
LabelMatcher ,
LabelName ,
MatchingModifierClause ,
MatchOp ,
MetricIdentifier ,
NumberLiteral ,
On ,
OnOrIgnoring ,
ParenExpr ,
parser ,
StringLiteral ,
@ -102,6 +98,7 @@ interface Context {
errors : ParsingError [ ] ;
}
// TODO find a better approach for grafana global variables
function isValidPromQLMinusGrafanaGlobalVariables ( expr : string ) {
const context : Context = {
query : {
@ -142,7 +139,7 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
const visQuery = context . query ;
switch ( node . type . id ) {
case Metric Identifier: {
case Identifier : {
// Expectation is that there is only one of those per query.
visQuery . metric = getString ( expr , node ) ;
break ;
@ -183,8 +180,8 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
default : {
if ( node . type . id === ParenExpr ) {
// We don't support parenthesis in the query to group expressions. We just report error but go on with the
// parsing.
// We don't support parenthesis in the query to group expressions.
// We just report error but go on with the parsing.
context . errors . push ( makeError ( expr , node ) ) ;
}
// Any other nodes we just ignore and go to its children. This should be fine as there are lots of wrapper
@ -200,8 +197,9 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
}
}
// TODO check if we still need this
function isIntervalVariableError ( node : SyntaxNode ) {
return node . prevSibling ? . type . id === Expr && node . prevSibling ? . firstChild ? . type . id === VectorSelector ;
return node . prevSibling ? . firstChild ? . type . id === VectorSelector ;
}
function getLabel ( expr : string , node : SyntaxNode ) : QueryBuilderLabelFilter {
@ -229,7 +227,6 @@ function handleFunction(expr: string, node: SyntaxNode, context: Context) {
const funcName = getString ( expr , nameNode ) ;
const body = node . getChild ( FunctionCallBody ) ;
const callArgs = body ! . getChild ( FunctionCallArgs ) ;
const params = [ ] ;
let interval = '' ;
@ -249,13 +246,13 @@ function handleFunction(expr: string, node: SyntaxNode, context: Context) {
// We unshift operations to keep the more natural order that we want to have in the visual query editor.
visQuery . operations . unshift ( op ) ;
if ( callArgs ) {
if ( getString ( expr , callArgs ) === interval + ']' ) {
if ( body ) {
if ( getString ( expr , body ) === '([' + interval + ']) ' ) {
// This is a special case where we have a function with a single argument and it is the interval.
// This happens when you start adding operations in query builder and did not set a metric yet.
return ;
}
updateFunctionArgs ( expr , callArgs , context , op ) ;
updateFunctionArgs ( expr , body , context , op ) ;
}
}
@ -284,25 +281,14 @@ function handleAggregation(expr: string, node: SyntaxNode, context: Context) {
funcName = ` __ ${ funcName } _without ` ;
}
labels . push ( . . . getAllByType ( expr , modifier , Grouping Label) ) ;
labels . push ( . . . getAllByType ( expr , modifier , LabelName ) ) ;
}
const body = node . getChild ( FunctionCallBody ) ;
const callArgs = body ! . getChild ( FunctionCallArgs ) ;
const callArgsExprChild = callArgs ? . getChild ( Expr ) ;
const binaryExpressionWithinAggregationArgs = callArgsExprChild ? . getChild ( BinaryExpr ) ;
if ( binaryExpressionWithinAggregationArgs ) {
context . errors . push ( {
text : 'Query parsing is ambiguous.' ,
from : binaryExpressionWithinAggregationArgs . from ,
to : binaryExpressionWithinAggregationArgs.to ,
} ) ;
}
const op : QueryBuilderOperation = { id : funcName , params : [ ] } ;
visQuery . operations . unshift ( op ) ;
updateFunctionArgs ( expr , callArgs , context , op ) ;
updateFunctionArgs ( expr , body , context , op ) ;
// We add labels after params in the visual query editor.
op . params . push ( . . . labels ) ;
}
@ -310,8 +296,7 @@ function handleAggregation(expr: string, node: SyntaxNode, context: Context) {
/ * *
* Handle ( probably ) all types of arguments that function or aggregation can have .
*
* FunctionCallArgs are nested bit weirdly basically its [ firstArg , . . . rest ] where rest is again FunctionCallArgs so
* we cannot just get all the children and iterate them as arguments we have to again recursively traverse through
* We cannot just get all the children and iterate them as arguments we have to again recursively traverse through
* them .
*
* @param expr
@ -324,15 +309,16 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont
return ;
}
switch ( node . type . id ) {
// In case we have an expression we don't know what kind so we have to look at the child as it can be anything.
case Expr :
// FunctionCallArgs are nested bit weirdly as mentioned so we have to go one deeper in this case.
case FunctionCallArgs : {
case FunctionCallBody : {
let child = node . firstChild ;
while ( child ) {
const callArgsExprChild = child . getChild ( Expr ) ;
const binaryExpressionWithinFunctionArgs = callArgsExprChild ? . getChild ( BinaryExpr ) ;
let binaryExpressionWithinFunctionArgs : SyntaxNode | null ;
if ( child . type . id === BinaryExpr ) {
binaryExpressionWithinFunctionArgs = child ;
} else {
binaryExpressionWithinFunctionArgs = child . getChild ( BinaryExpr ) ;
}
if ( binaryExpressionWithinFunctionArgs ) {
context . errors . push ( {
@ -345,7 +331,6 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont
updateFunctionArgs ( expr , child , context , op ) ;
child = child . nextSibling ;
}
break ;
}
@ -378,16 +363,16 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) {
const visQuery = context . query ;
const left = node . firstChild ! ;
const op = getString ( expr , left . nextSibling ) ;
const binModifier = getBinaryModifier ( expr , node . getChild ( Bin Modifiers ) ) ;
const binModifier = getBinaryModifier ( expr , node . getChild ( BoolMod ifier ) ? ? node . getChild ( Matching ModifierClau se ) ) ;
const right = node . lastChild ! ;
const opDef = binaryScalarOperatorToOperatorName [ op ] ;
const leftNumber = left . getChild ( NumberLiteral ) ;
const rightNumber = right . getChild ( NumberLiteral ) ;
const leftNumber = left . type . id === NumberLiteral ;
const rightNumber = right . type . id === NumberLiteral ;
const rightBinary = right . getChild ( BinaryExpr ) ;
const rightBinary = right . type . id === BinaryExpr ;
if ( leftNumber ) {
// TODO: this should be already handled in case parent is binary expression as it has to be added to parent
@ -433,6 +418,7 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) {
}
}
// TODO revisit this function.
function getBinaryModifier (
expr : string ,
node : SyntaxNode | null
@ -446,17 +432,17 @@ function getBinaryModifier(
if ( node . getChild ( 'Bool' ) ) {
return { isBool : true , isMatcher : false } ;
} else {
const matcher = node . getChild ( OnOrIgnoring ) ;
if ( ! matcher ) {
// Not sure what this could be, maybe should be an error.
return undefined ;
let labels = '' ;
const groupingLabels = node . getChild ( GroupingLabels ) ;
if ( groupingLabels ) {
labels = getAllByType ( expr , groupingLabels , LabelName ) . join ( ', ' ) ;
}
const labels = getString ( expr , matcher . getChild ( GroupingLabels ) ? . getChild ( GroupingLabelList ) ) ;
return {
isMatcher : true ,
isBool : false ,
matches : labels ,
matchType : matcher .getChild( On ) ? 'on' : 'ignoring' ,
matchType : node .getChild( On ) ? 'on' : 'ignoring' ,
} ;
}
}