influxdb/query/parser/parser_test.go

1696 lines
44 KiB
Go

package parser_test
import (
"regexp"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/platform/query/ast"
"github.com/influxdata/platform/query/ast/asttest"
"github.com/influxdata/platform/query/parser"
)
func TestParse(t *testing.T) {
tests := []struct {
name string
raw string
want *ast.Program
wantErr bool
}{
{
name: "optional query metadata",
raw: `option task = {
name: "foo",
every: 1h,
delay: 10m,
cron: "0 2 * * *",
retry: 5,
}`,
want: &ast.Program{
Body: []ast.Statement{
&ast.OptionStatement{
Declaration: &ast.VariableDeclarator{
ID: &ast.Identifier{Name: "task"},
Init: &ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "name"},
Value: &ast.StringLiteral{Value: "foo"},
},
{
Key: &ast.Identifier{Name: "every"},
Value: &ast.DurationLiteral{Value: 1 * time.Hour},
},
{
Key: &ast.Identifier{Name: "delay"},
Value: &ast.DurationLiteral{Value: 10 * time.Minute},
},
{
Key: &ast.Identifier{Name: "cron"},
Value: &ast.StringLiteral{Value: "0 2 * * *"},
},
{
Key: &ast.Identifier{Name: "retry"},
Value: &ast.IntegerLiteral{Value: 5},
},
},
},
},
},
},
},
},
{
name: "optional query metadata preceding query text",
raw: `option task = {
name: "foo", // Name of task
every: 1h, // Execution frequency of task
}
// Task will execute the following query
from() |> count()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.OptionStatement{
Declaration: &ast.VariableDeclarator{
ID: &ast.Identifier{Name: "task"},
Init: &ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "name"},
Value: &ast.StringLiteral{Value: "foo"},
},
{
Key: &ast.Identifier{Name: "every"},
Value: &ast.DurationLiteral{Value: 1 * time.Hour},
},
},
},
},
},
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: nil,
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "count"},
Arguments: nil,
},
},
},
},
},
},
{
name: "from",
raw: `from()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
},
},
},
},
},
{
name: "comment",
raw: `// Comment
from()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
},
},
},
},
},
{
name: "identifier with number",
raw: `tan2()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "tan2",
},
},
},
},
},
},
{
name: "regex literal",
raw: `/.*/`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.RegexpLiteral{
Value: regexp.MustCompile(".*"),
},
},
},
},
},
{
name: "regex literal with escape sequence",
raw: `/a\/b\\c\d/`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.RegexpLiteral{
Value: regexp.MustCompile(`a/b\\c\d`),
},
},
},
},
},
{
name: "regex match operators",
raw: `"a" =~ /.*/ and "b" !~ /c/`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.LogicalExpression{
Operator: ast.AndOperator,
Left: &ast.BinaryExpression{
Operator: ast.RegexpMatchOperator,
Left: &ast.StringLiteral{Value: "a"},
Right: &ast.RegexpLiteral{Value: regexp.MustCompile(".*")},
},
Right: &ast.BinaryExpression{
Operator: ast.NotRegexpMatchOperator,
Left: &ast.StringLiteral{Value: "b"},
Right: &ast.RegexpLiteral{Value: regexp.MustCompile("c")},
},
},
},
},
},
},
{
name: "declare variable as an int",
raw: `howdy = 1`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{Name: "howdy"},
Init: &ast.IntegerLiteral{Value: 1},
}},
},
},
},
},
{
name: "declare variable as a float",
raw: `howdy = 1.1`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{Name: "howdy"},
Init: &ast.FloatLiteral{Value: 1.1},
}},
},
},
},
},
{
name: "declare variable as an array",
raw: `howdy = [1, 2, 3, 4]`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{Name: "howdy"},
Init: &ast.ArrayExpression{
Elements: []ast.Expression{
&ast.IntegerLiteral{Value: 1},
&ast.IntegerLiteral{Value: 2},
&ast.IntegerLiteral{Value: 3},
&ast.IntegerLiteral{Value: 4},
},
},
}},
},
},
},
},
{
name: "use variable to declare something",
raw: `howdy = 1
from()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{Name: "howdy"},
Init: &ast.IntegerLiteral{Value: 1},
}},
},
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
},
},
},
},
},
{
name: "variable is from statement",
raw: `howdy = from()
howdy.count()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "howdy",
},
Init: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.MemberExpression{
Object: &ast.Identifier{
Name: "howdy",
},
Property: &ast.Identifier{
Name: "count",
},
},
},
},
},
},
},
{
name: "pipe expression",
raw: `from() |> count()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: nil,
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "count"},
Arguments: nil,
},
},
},
},
},
},
{
name: "literal pipe expression",
raw: `5 |> pow2()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.IntegerLiteral{Value: 5},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "pow2"},
Arguments: nil,
},
},
},
},
},
},
{
name: "member expression pipe expression",
raw: `foo.bar |> baz()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.MemberExpression{
Object: &ast.Identifier{Name: "foo"},
Property: &ast.Identifier{Name: "bar"},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "baz"},
Arguments: nil,
},
},
},
},
},
},
{
name: "multiple pipe expressions",
raw: `from() |> range() |> filter() |> count()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "filter"},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "count"},
},
},
},
},
},
},
{
name: "two variables for two froms",
raw: `howdy = from()
doody = from()
howdy|>count()
doody|>sum()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "howdy",
},
Init: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "doody",
},
Init: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.Identifier{Name: "howdy"},
Call: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "count",
},
},
},
},
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.Identifier{Name: "doody"},
Call: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "sum",
},
},
},
},
},
},
},
{
name: "from with database",
raw: `from(db:"telegraf")`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{
Name: "db",
},
Value: &ast.StringLiteral{
Value: "telegraf",
},
},
},
},
},
},
},
},
},
},
{
name: "map member expressions",
raw: `m = {key1: 1, key2:"value2"}
m.key1
m["key2"]
`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "m",
},
Init: &ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "key1"},
Value: &ast.IntegerLiteral{Value: 1},
},
{
Key: &ast.Identifier{Name: "key2"},
Value: &ast.StringLiteral{Value: "value2"},
},
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.MemberExpression{
Object: &ast.Identifier{Name: "m"},
Property: &ast.Identifier{Name: "key1"},
},
},
&ast.ExpressionStatement{
Expression: &ast.MemberExpression{
Object: &ast.Identifier{Name: "m"},
Property: &ast.StringLiteral{Value: "key2"},
},
},
},
},
},
{
name: "var as binary expression of other vars",
raw: `a = 1
b = 2
c = a + b
d = a`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.IntegerLiteral{Value: 1},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "b",
},
Init: &ast.IntegerLiteral{Value: 2},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "c",
},
Init: &ast.BinaryExpression{
Operator: ast.AdditionOperator,
Left: &ast.Identifier{Name: "a"},
Right: &ast.Identifier{Name: "b"},
},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "d",
},
Init: &ast.Identifier{Name: "a"},
}},
},
},
},
},
{
name: "var as unary expression of other vars",
raw: `a = 5
c = -a`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.IntegerLiteral{Value: 5},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "c",
},
Init: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.Identifier{Name: "a"},
},
}},
},
},
},
},
{
name: "var as both binary and unary expressions",
raw: `a = 5
c = 10 * -a`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.IntegerLiteral{Value: 5},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "c",
},
Init: &ast.BinaryExpression{
Operator: ast.MultiplicationOperator,
Left: &ast.IntegerLiteral{Value: 10},
Right: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.Identifier{Name: "a"},
},
},
}},
},
},
},
},
{
name: "unary expressions within logical expression",
raw: `a = 5.0
10.0 * -a == -0.5 or a == 6.0`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.FloatLiteral{Value: 5},
}},
},
&ast.ExpressionStatement{
Expression: &ast.LogicalExpression{
Operator: ast.OrOperator,
Left: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.BinaryExpression{
Operator: ast.MultiplicationOperator,
Left: &ast.FloatLiteral{Value: 10},
Right: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.Identifier{Name: "a"},
},
},
Right: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.FloatLiteral{Value: 0.5},
},
},
Right: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.Identifier{Name: "a"},
Right: &ast.FloatLiteral{Value: 6},
},
},
},
},
},
},
{
name: "unary expressions with too many comments",
raw: `// define a
a = 5.0
// eval this
10.0 * -a == -0.5
// or this
or a == 6.0`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.FloatLiteral{Value: 5},
}},
},
&ast.ExpressionStatement{
Expression: &ast.LogicalExpression{
Operator: ast.OrOperator,
Left: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.BinaryExpression{
Operator: ast.MultiplicationOperator,
Left: &ast.FloatLiteral{Value: 10},
Right: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.Identifier{Name: "a"},
},
},
Right: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.FloatLiteral{Value: 0.5},
},
},
Right: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.Identifier{Name: "a"},
Right: &ast.FloatLiteral{Value: 6},
},
},
},
},
},
},
{
name: "expressions with function calls",
raw: `a = foo() == 10`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.CallExpression{
Callee: &ast.Identifier{Name: "foo"},
},
Right: &ast.IntegerLiteral{Value: 10},
},
}},
},
},
},
},
{
name: "mix unary logical and binary expressions",
raw: `
not (f() == 6.0 * x) or fail()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.LogicalExpression{
Operator: ast.OrOperator,
Left: &ast.UnaryExpression{
Operator: ast.NotOperator,
Argument: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.CallExpression{
Callee: &ast.Identifier{Name: "f"},
},
Right: &ast.BinaryExpression{
Operator: ast.MultiplicationOperator,
Left: &ast.FloatLiteral{Value: 6},
Right: &ast.Identifier{Name: "x"},
},
},
},
Right: &ast.CallExpression{
Callee: &ast.Identifier{Name: "fail"},
},
},
},
},
},
},
{
name: "mix unary logical and binary expressions with extra parens",
raw: `
(not (f() == 6.0 * x) or fail())`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.LogicalExpression{
Operator: ast.OrOperator,
Left: &ast.UnaryExpression{
Operator: ast.NotOperator,
Argument: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.CallExpression{
Callee: &ast.Identifier{Name: "f"},
},
Right: &ast.BinaryExpression{
Operator: ast.MultiplicationOperator,
Left: &ast.FloatLiteral{Value: 6},
Right: &ast.Identifier{Name: "x"},
},
},
},
Right: &ast.CallExpression{
Callee: &ast.Identifier{Name: "fail"},
},
},
},
},
},
},
{
name: "arrow function called",
raw: `plusOne = (r) => r + 1
plusOne(r:5)
`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "plusOne",
},
Init: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.BinaryExpression{
Operator: ast.AdditionOperator,
Left: &ast.Identifier{Name: "r"},
Right: &ast.IntegerLiteral{Value: 1},
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{Name: "plusOne"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{
Name: "r",
},
Value: &ast.IntegerLiteral{
Value: 5,
},
},
},
},
},
},
},
},
},
},
{
name: "arrow function return map",
raw: `toMap = (r) =>({r:r})`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "toMap",
},
Init: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.ObjectExpression{
Properties: []*ast.Property{{
Key: &ast.Identifier{Name: "r"},
Value: &ast.Identifier{Name: "r"},
}},
},
},
}},
},
},
},
},
{
name: "arrow function with default arg",
raw: `addN = (r, n=5) => r + n`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "addN",
},
Init: &ast.ArrowFunctionExpression{
Params: []*ast.Property{
{Key: &ast.Identifier{Name: "r"}},
{Key: &ast.Identifier{Name: "n"}, Value: &ast.IntegerLiteral{Value: 5}},
},
Body: &ast.BinaryExpression{
Operator: ast.AdditionOperator,
Left: &ast.Identifier{Name: "r"},
Right: &ast.Identifier{Name: "n"},
},
},
}},
},
},
},
},
{
name: "arrow function called in binary expression",
raw: `
plusOne = (r) => r + 1
plusOne(r:5) == 6 or die()
`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "plusOne",
},
Init: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.BinaryExpression{
Operator: ast.AdditionOperator,
Left: &ast.Identifier{Name: "r"},
Right: &ast.IntegerLiteral{Value: 1},
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.LogicalExpression{
Operator: ast.OrOperator,
Left: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.CallExpression{
Callee: &ast.Identifier{Name: "plusOne"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{
Name: "r",
},
Value: &ast.IntegerLiteral{
Value: 5,
},
},
},
},
},
},
Right: &ast.IntegerLiteral{Value: 6},
},
Right: &ast.CallExpression{
Callee: &ast.Identifier{Name: "die"},
},
},
},
},
},
},
{
name: "arrow function as single expression",
raw: `f = (r) => r["_measurement"] == "cpu"`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "f",
},
Init: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "_measurement"},
},
Right: &ast.StringLiteral{Value: "cpu"},
},
},
}},
},
},
},
},
{
name: "arrow function as block",
raw: `f = (r) => {
m = r["_measurement"]
return m == "cpu"
}`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "f",
},
Init: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.BlockStatement{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "m",
},
Init: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "_measurement"},
},
}},
},
&ast.ReturnStatement{
Argument: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.Identifier{Name: "m"},
Right: &ast.StringLiteral{Value: "cpu"},
},
},
},
},
},
}},
},
},
},
},
{
name: "from with filter with no parens",
raw: `from(db:"telegraf").filter(fn: (r) => r["other"]=="mem" and r["this"]=="that" or r["these"]!="those")`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.MemberExpression{
Property: &ast.Identifier{Name: "filter"},
Object: &ast.CallExpression{
Callee: &ast.Identifier{
Name: "from",
},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "telegraf"},
},
},
},
},
},
},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "fn"},
Value: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.LogicalExpression{
Operator: ast.OrOperator,
Left: &ast.LogicalExpression{
Operator: ast.AndOperator,
Left: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "other"},
},
Right: &ast.StringLiteral{Value: "mem"},
},
Right: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "this"},
},
Right: &ast.StringLiteral{Value: "that"},
},
},
Right: &ast.BinaryExpression{
Operator: ast.NotEqualOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "these"},
},
Right: &ast.StringLiteral{Value: "those"},
},
},
},
},
},
},
},
},
},
},
},
},
{
name: "from with range",
raw: `from(db:"telegraf")|>range(start:-1h, end:10m)`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "telegraf"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: time.Hour},
},
},
{
Key: &ast.Identifier{Name: "end"},
Value: &ast.DurationLiteral{Value: 10 * time.Minute},
},
},
},
},
},
},
},
},
},
},
{
name: "from with limit",
raw: `from(db:"telegraf")|>limit(limit:100, offset:10)`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "telegraf"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "limit"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "limit"},
Value: &ast.IntegerLiteral{Value: 100},
},
{
Key: &ast.Identifier{Name: "offset"},
Value: &ast.IntegerLiteral{Value: 10},
},
},
},
},
},
},
},
},
},
},
{
name: "from with range and count",
raw: `from(db:"mydb")
|> range(start:-4h, stop:-2h)
|> count()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "mydb"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 4 * time.Hour},
},
},
{
Key: &ast.Identifier{Name: "stop"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 2 * time.Hour},
},
},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "count"},
},
},
},
},
},
},
{
name: "from with range, limit and count",
raw: `from(db:"mydb")
|> range(start:-4h, stop:-2h)
|> limit(n:10)
|> count()`,
want: &ast.Program{
Body: []ast.Statement{
&ast.ExpressionStatement{
Expression: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "mydb"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 4 * time.Hour},
},
},
{
Key: &ast.Identifier{Name: "stop"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 2 * time.Hour},
},
},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "limit"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{{
Key: &ast.Identifier{Name: "n"},
Value: &ast.IntegerLiteral{Value: 10},
}},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "count"},
},
},
},
},
},
},
{
name: "from with join",
raw: `
a = from(db:"dbA") |> range(start:-1h)
b = from(db:"dbB") |> range(start:-1h)
join(tables:[a,b], on:["host"], fn: (a,b) => a["_field"] + b["_field"])`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "dbA"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 1 * time.Hour},
},
},
},
},
},
},
},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "b",
},
Init: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "dbB"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 1 * time.Hour},
},
},
},
},
},
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{Name: "join"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "tables"},
Value: &ast.ArrayExpression{
Elements: []ast.Expression{
&ast.Identifier{Name: "a"},
&ast.Identifier{Name: "b"},
},
},
},
{
Key: &ast.Identifier{Name: "on"},
Value: &ast.ArrayExpression{
Elements: []ast.Expression{&ast.StringLiteral{Value: "host"}},
},
},
{
Key: &ast.Identifier{Name: "fn"},
Value: &ast.ArrowFunctionExpression{
Params: []*ast.Property{
{Key: &ast.Identifier{Name: "a"}},
{Key: &ast.Identifier{Name: "b"}},
},
Body: &ast.BinaryExpression{
Operator: ast.AdditionOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "a"},
Property: &ast.StringLiteral{Value: "_field"},
},
Right: &ast.MemberExpression{
Object: &ast.Identifier{Name: "b"},
Property: &ast.StringLiteral{Value: "_field"},
},
},
},
},
},
},
},
},
},
},
},
},
{
name: "from with join with complex expression",
raw: `
a = from(db:"Flux")
|> filter(fn: (r) => r["_measurement"] == "a")
|> range(start:-1h)
b = from(db:"Flux")
|> filter(fn: (r) => r["_measurement"] == "b")
|> range(start:-1h)
join(tables:[a,b], on:["t1"], fn: (a,b) => (a["_field"] - b["_field"]) / b["_field"])
`,
want: &ast.Program{
Body: []ast.Statement{
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "a",
},
Init: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "Flux"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "filter"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "fn"},
Value: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "_measurement"},
},
Right: &ast.StringLiteral{Value: "a"},
},
},
},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 1 * time.Hour},
},
},
},
},
},
},
},
}},
},
&ast.VariableDeclaration{
Declarations: []*ast.VariableDeclarator{{
ID: &ast.Identifier{
Name: "b",
},
Init: &ast.PipeExpression{
Argument: &ast.PipeExpression{
Argument: &ast.CallExpression{
Callee: &ast.Identifier{Name: "from"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "db"},
Value: &ast.StringLiteral{Value: "Flux"},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "filter"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "fn"},
Value: &ast.ArrowFunctionExpression{
Params: []*ast.Property{{Key: &ast.Identifier{Name: "r"}}},
Body: &ast.BinaryExpression{
Operator: ast.EqualOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "r"},
Property: &ast.StringLiteral{Value: "_measurement"},
},
Right: &ast.StringLiteral{Value: "b"},
},
},
},
},
},
},
},
},
Call: &ast.CallExpression{
Callee: &ast.Identifier{Name: "range"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "start"},
Value: &ast.UnaryExpression{
Operator: ast.SubtractionOperator,
Argument: &ast.DurationLiteral{Value: 1 * time.Hour},
},
},
},
},
},
},
},
}},
},
&ast.ExpressionStatement{
Expression: &ast.CallExpression{
Callee: &ast.Identifier{Name: "join"},
Arguments: []ast.Expression{
&ast.ObjectExpression{
Properties: []*ast.Property{
{
Key: &ast.Identifier{Name: "tables"},
Value: &ast.ArrayExpression{
Elements: []ast.Expression{
&ast.Identifier{Name: "a"},
&ast.Identifier{Name: "b"},
},
},
},
{
Key: &ast.Identifier{Name: "on"},
Value: &ast.ArrayExpression{
Elements: []ast.Expression{
&ast.StringLiteral{
Value: "t1",
},
},
},
},
{
Key: &ast.Identifier{Name: "fn"},
Value: &ast.ArrowFunctionExpression{
Params: []*ast.Property{
{Key: &ast.Identifier{Name: "a"}},
{Key: &ast.Identifier{Name: "b"}},
},
Body: &ast.BinaryExpression{
Operator: ast.DivisionOperator,
Left: &ast.BinaryExpression{
Operator: ast.SubtractionOperator,
Left: &ast.MemberExpression{
Object: &ast.Identifier{Name: "a"},
Property: &ast.StringLiteral{Value: "_field"},
},
Right: &ast.MemberExpression{
Object: &ast.Identifier{Name: "b"},
Property: &ast.StringLiteral{Value: "_field"},
},
},
Right: &ast.MemberExpression{
Object: &ast.Identifier{Name: "b"},
Property: &ast.StringLiteral{Value: "_field"},
},
},
},
},
},
},
},
},
},
},
},
},
{
name: "parse error extra gibberish",
raw: `from(db:"Flux") &^*&H#IUJBN`,
wantErr: true,
},
{
name: "parse error extra gibberish and valid content",
raw: `from(db:"Flux") &^*&H#IUJBN from(db:"other")`,
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
// Set the env var`GO_TAGS=parser_debug` in order
// to turn on parser debugging as it is turned off by default.
got, err := parser.NewAST(tt.raw)
if (err != nil) != tt.wantErr {
t.Errorf("parser.NewAST() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
return
}
if !cmp.Equal(tt.want, got, asttest.CompareOptions...) {
t.Errorf("parser.NewAST() = -want/+got %s", cmp.Diff(tt.want, got, asttest.CompareOptions...))
}
})
}
}
var benchmarkQuery = []byte(`
start = -10s
do = (cpu) =>
from(db:"telegraf")
.filter(fn: (r) =>
r["_measurement"] == "cpu"
and
r["cpu"] == cpu)
.range(start:start)
cpu0 = do(cpu:"cpu0")
cpu1 = do(cpu:"cpu1")
join(
tables:[cpu0, cpu1],
on:["_measurement","_field","host"],
fn: (a,b) => a["_value"] - b["_value"],
)
`)
var benchmarkProgram interface{}
func BenchmarkParse(b *testing.B) {
b.ReportAllocs()
var err error
for n := 0; n < b.N; n++ {
benchmarkProgram, err = parser.Parse("", benchmarkQuery)
if err != nil {
b.Fatal(err)
}
}
}