2014-11-22 04:12:48 +00:00
package influxql_test
import (
"reflect"
"strings"
"testing"
"time"
"github.com/influxdb/influxdb/influxql"
)
2014-11-22 23:33:21 +00:00
// Ensure the parser can parse a multi-statement query.
func TestParser_ParseQuery ( t * testing . T ) {
s := ` SELECT a FROM b; SELECT c FROM d `
q , err := influxql . NewParser ( strings . NewReader ( s ) ) . ParseQuery ( )
if err != nil {
t . Fatalf ( "unexpected error: %s" , err )
} else if len ( q . Statements ) != 2 {
t . Fatalf ( "unexpected statement count: %d" , len ( q . Statements ) )
}
}
// Ensure the parser can parse an empty query.
func TestParser_ParseQuery_Empty ( t * testing . T ) {
q , err := influxql . NewParser ( strings . NewReader ( ` ` ) ) . ParseQuery ( )
if err != nil {
t . Fatalf ( "unexpected error: %s" , err )
} else if len ( q . Statements ) != 0 {
t . Fatalf ( "unexpected statement count: %d" , len ( q . Statements ) )
}
}
// Ensure the parser can return an error from an malformed statement.
func TestParser_ParseQuery_ParseError ( t * testing . T ) {
_ , err := influxql . NewParser ( strings . NewReader ( ` SELECT ` ) ) . ParseQuery ( )
if err == nil || err . Error ( ) != ` found EOF, expected identifier, string, number, bool at line 1, char 8 ` {
t . Fatalf ( "unexpected error: %s" , err )
}
}
2014-11-22 04:12:48 +00:00
// Ensure the parser can parse strings into Statement ASTs.
func TestParser_ParseStatement ( t * testing . T ) {
var tests = [ ] struct {
s string
stmt influxql . Statement
err string
} {
2014-11-25 23:23:10 +00:00
// SELECT * statement
{
s : ` SELECT * FROM myseries ` ,
stmt : & influxql . SelectStatement {
Fields : influxql . Fields {
& influxql . Field { Expr : & influxql . Wildcard { } } ,
} ,
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "myseries" } ,
2014-11-25 23:23:10 +00:00
} ,
} ,
2014-11-22 23:33:21 +00:00
// SELECT statement
2014-11-22 04:12:48 +00:00
{
2014-12-15 00:54:51 +00:00
s : ` SELECT field1, field2 ,field3 AS field_x FROM myseries WHERE host = 'hosta.influxdb.org' GROUP BY 10h ORDER BY ASC LIMIT 20; ` ,
2014-11-22 04:12:48 +00:00
stmt : & influxql . SelectStatement {
Fields : influxql . Fields {
& influxql . Field { Expr : & influxql . VarRef { Val : "field1" } } ,
& influxql . Field { Expr : & influxql . VarRef { Val : "field2" } } ,
& influxql . Field { Expr : & influxql . VarRef { Val : "field3" } , Alias : "field_x" } ,
} ,
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "myseries" } ,
2014-11-22 04:12:48 +00:00
Condition : & influxql . BinaryExpr {
Op : influxql . EQ ,
LHS : & influxql . VarRef { Val : "host" } ,
RHS : & influxql . StringLiteral { Val : "hosta.influxdb.org" } ,
} ,
Dimensions : influxql . Dimensions {
& influxql . Dimension { Expr : & influxql . DurationLiteral { Val : 10 * time . Hour } } ,
} ,
2014-12-16 14:06:28 +00:00
Limit : 20 ,
2014-12-15 00:54:51 +00:00
SortFields : influxql . SortFields {
& influxql . SortField { Ascending : true } ,
} ,
2014-11-22 04:12:48 +00:00
} ,
} ,
2014-12-06 18:17:58 +00:00
// SELECT statement with JOIN
{
2014-12-20 04:36:52 +00:00
s : ` SELECT field1 FROM join(aa,"bb", cc) JOIN cc ` ,
2014-12-06 18:17:58 +00:00
stmt : & influxql . SelectStatement {
Fields : influxql . Fields { & influxql . Field { Expr : & influxql . VarRef { Val : "field1" } } } ,
Source : & influxql . Join {
2014-12-20 04:36:52 +00:00
Measurements : influxql . Measurements {
{ Name : "aa" } ,
{ Name : "bb" } ,
{ Name : "cc" } ,
2014-12-06 18:17:58 +00:00
} ,
} ,
} ,
} ,
// SELECT statement with MERGE
{
2014-12-20 04:36:52 +00:00
s : ` SELECT field1 FROM merge(aa,b.b) ` ,
2014-12-06 18:17:58 +00:00
stmt : & influxql . SelectStatement {
Fields : influxql . Fields { & influxql . Field { Expr : & influxql . VarRef { Val : "field1" } } } ,
Source : & influxql . Merge {
2014-12-20 04:36:52 +00:00
Measurements : influxql . Measurements {
{ Name : "aa" } ,
{ Name : "b.b" } ,
2014-12-06 18:17:58 +00:00
} ,
} ,
} ,
} ,
2014-11-25 01:14:03 +00:00
// SELECT statement (lowercase)
{
s : ` select my_field from myseries ` ,
stmt : & influxql . SelectStatement {
Fields : influxql . Fields { & influxql . Field { Expr : & influxql . VarRef { Val : "my_field" } } } ,
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "myseries" } ,
2014-11-25 01:14:03 +00:00
} ,
} ,
2014-12-15 00:54:51 +00:00
// SELECT statement with multiple ORDER BY fields
{
s : ` SELECT field1 FROM myseries ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
stmt : & influxql . SelectStatement {
Fields : influxql . Fields { & influxql . Field { Expr : & influxql . VarRef { Val : "field1" } } } ,
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "myseries" } ,
2014-12-15 00:54:51 +00:00
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-15 00:54:51 +00:00
} ,
Limit : 10 ,
} ,
} ,
2014-11-22 23:33:21 +00:00
// DELETE statement
{
s : ` DELETE FROM myseries WHERE host = 'hosta.influxdb.org' ` ,
stmt : & influxql . DeleteStatement {
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "myseries" } ,
2014-11-22 23:33:21 +00:00
Condition : & influxql . BinaryExpr {
Op : influxql . EQ ,
LHS : & influxql . VarRef { Val : "host" } ,
RHS : & influxql . StringLiteral { Val : "hosta.influxdb.org" } ,
} ,
} ,
} ,
// LIST SERIES statement
{
s : ` LIST SERIES ` ,
stmt : & influxql . ListSeriesStatement { } ,
} ,
2014-12-15 00:54:51 +00:00
// LIST SERIES WHERE with ORDER BY and LIMIT
2014-12-10 14:37:15 +00:00
{
2014-12-16 14:06:28 +00:00
s : ` LIST SERIES WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
2014-12-10 14:37:15 +00:00
stmt : & influxql . ListSeriesStatement {
Condition : & influxql . BinaryExpr {
2014-12-16 14:06:28 +00:00
Op : influxql . EQ ,
2014-12-16 02:48:22 +00:00
LHS : & influxql . VarRef { Val : "region" } ,
RHS : & influxql . StringLiteral { Val : "uswest" } ,
} ,
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-16 02:48:22 +00:00
} ,
Limit : 10 ,
} ,
} ,
// LIST MEASUREMENTS WHERE with ORDER BY and LIMIT
{
2014-12-16 14:06:28 +00:00
s : ` LIST MEASUREMENTS WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
2014-12-16 02:48:22 +00:00
stmt : & influxql . ListMeasurementsStatement {
Condition : & influxql . BinaryExpr {
2014-12-16 14:06:28 +00:00
Op : influxql . EQ ,
2014-12-16 02:48:22 +00:00
LHS : & influxql . VarRef { Val : "region" } ,
RHS : & influxql . StringLiteral { Val : "uswest" } ,
} ,
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-16 02:48:22 +00:00
} ,
Limit : 10 ,
} ,
} ,
// LIST TAG KEYS
{
2014-12-16 14:06:28 +00:00
s : ` LIST TAG KEYS FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
2014-12-16 02:48:22 +00:00
stmt : & influxql . ListTagKeysStatement {
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "src" } ,
2014-12-16 02:48:22 +00:00
Condition : & influxql . BinaryExpr {
2014-12-16 14:06:28 +00:00
Op : influxql . EQ ,
2014-12-16 02:48:22 +00:00
LHS : & influxql . VarRef { Val : "region" } ,
RHS : & influxql . StringLiteral { Val : "uswest" } ,
} ,
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-16 02:48:22 +00:00
} ,
Limit : 10 ,
} ,
} ,
// LIST TAG VALUES
{
2014-12-16 14:06:28 +00:00
s : ` LIST TAG VALUES FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
2014-12-16 02:48:22 +00:00
stmt : & influxql . ListTagValuesStatement {
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "src" } ,
2014-12-16 02:48:22 +00:00
Condition : & influxql . BinaryExpr {
2014-12-16 14:06:28 +00:00
Op : influxql . EQ ,
2014-12-16 02:48:22 +00:00
LHS : & influxql . VarRef { Val : "region" } ,
RHS : & influxql . StringLiteral { Val : "uswest" } ,
} ,
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-16 02:48:22 +00:00
} ,
Limit : 10 ,
} ,
} ,
// LIST FIELD KEYS
{
2014-12-16 14:06:28 +00:00
s : ` LIST FIELD KEYS FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
2014-12-16 02:48:22 +00:00
stmt : & influxql . ListFieldKeysStatement {
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "src" } ,
2014-12-16 02:48:22 +00:00
Condition : & influxql . BinaryExpr {
2014-12-16 14:06:28 +00:00
Op : influxql . EQ ,
2014-12-16 02:48:22 +00:00
LHS : & influxql . VarRef { Val : "region" } ,
RHS : & influxql . StringLiteral { Val : "uswest" } ,
} ,
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-16 02:48:22 +00:00
} ,
Limit : 10 ,
} ,
} ,
// LIST FIELD VALUES
{
2014-12-16 14:06:28 +00:00
s : ` LIST FIELD VALUES FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10 ` ,
2014-12-16 02:48:22 +00:00
stmt : & influxql . ListFieldValuesStatement {
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "src" } ,
2014-12-16 02:48:22 +00:00
Condition : & influxql . BinaryExpr {
2014-12-16 14:06:28 +00:00
Op : influxql . EQ ,
2014-12-10 14:37:15 +00:00
LHS : & influxql . VarRef { Val : "region" } ,
RHS : & influxql . StringLiteral { Val : "uswest" } ,
} ,
2014-12-15 00:54:51 +00:00
SortFields : influxql . SortFields {
2014-12-16 14:06:28 +00:00
& influxql . SortField { Ascending : true } ,
& influxql . SortField { Name : "field1" } ,
& influxql . SortField { Name : "field2" } ,
2014-12-15 00:54:51 +00:00
} ,
2014-12-10 14:37:15 +00:00
Limit : 10 ,
} ,
} ,
2014-11-22 23:33:21 +00:00
// DROP SERIES statement
{
s : ` DROP SERIES myseries ` ,
stmt : & influxql . DropSeriesStatement { Name : "myseries" } ,
} ,
// LIST CONTINUOUS QUERIES statement
{
s : ` LIST CONTINUOUS QUERIES ` ,
stmt : & influxql . ListContinuousQueriesStatement { } ,
} ,
2014-11-25 04:49:09 +00:00
// CREATE CONTINUOUS QUERY statement
{
s : ` CREATE CONTINUOUS QUERY myquery AS SELECT count() FROM myseries INTO foo ` ,
stmt : & influxql . CreateContinuousQueryStatement {
Name : "myquery" ,
Source : & influxql . SelectStatement {
Fields : influxql . Fields { & influxql . Field { Expr : & influxql . Call { Name : "count" } } } ,
2014-12-20 04:36:52 +00:00
Source : & influxql . Measurement { Name : "myseries" } ,
2014-11-25 04:49:09 +00:00
} ,
Target : "foo" ,
} ,
} ,
2014-12-31 13:47:21 +00:00
// CREATE DATABASE statement
{
2014-12-31 14:01:01 +00:00
s : ` CREATE DATABASE testdb ` ,
2014-12-31 13:47:21 +00:00
stmt : & influxql . CreateDatabaseStatement {
Name : "testdb" ,
} ,
} ,
2014-12-31 16:22:07 +00:00
// CREATE USER statement
{
s : ` CREATE USER testuser WITH PASSWORD pwd1337 ` ,
stmt : & influxql . CreateUserStatement {
Name : "testuser" ,
Password : "pwd1337" ,
} ,
} ,
2014-11-22 23:33:21 +00:00
// DROP CONTINUOUS QUERY statement
{
2014-11-25 00:58:21 +00:00
s : ` DROP CONTINUOUS QUERY myquery ` ,
stmt : & influxql . DropContinuousQueryStatement { Name : "myquery" } ,
2014-11-22 23:33:21 +00:00
} ,
2015-01-03 03:56:26 +00:00
// GRANT READ statement
{
s : ` GRANT READ ON testdb TO jdoe ` ,
stmt : & influxql . GrantStatement {
Privilege : influxql . ReadPrivilege ,
On : "testdb" ,
User : "jdoe" ,
} ,
} ,
// GRANT WRITE statement
{
s : ` GRANT WRITE ON testdb TO jdoe ` ,
stmt : & influxql . GrantStatement {
Privilege : influxql . WritePrivilege ,
On : "testdb" ,
User : "jdoe" ,
} ,
} ,
// GRANT ALL statement
{
s : ` GRANT ALL ON testdb TO jdoe ` ,
stmt : & influxql . GrantStatement {
Privilege : influxql . AllPrivileges ,
On : "testdb" ,
User : "jdoe" ,
} ,
} ,
// GRANT ALL PRIVILEGES statement
{
s : ` GRANT ALL PRIVILEGES ON testdb TO jdoe ` ,
stmt : & influxql . GrantStatement {
Privilege : influxql . AllPrivileges ,
On : "testdb" ,
User : "jdoe" ,
} ,
} ,
2014-11-22 04:12:48 +00:00
// Errors
{ s : ` ` , err : ` found EOF, expected SELECT at line 1, char 1 ` } ,
{ s : ` SELECT ` , err : ` found EOF, expected identifier, string, number, bool at line 1, char 8 ` } ,
2014-11-25 01:23:33 +00:00
{ s : ` blah blah ` , err : ` found blah, expected SELECT at line 1, char 1 ` } ,
2014-12-15 00:54:51 +00:00
{ s : ` SELECT field1 X ` , err : ` found X, expected FROM at line 1, char 15 ` } ,
{ s : ` SELECT field1 FROM "series" WHERE X +; ` , err : ` found ;, expected identifier, string, number, bool at line 1, char 38 ` } ,
{ s : ` SELECT field1 FROM myseries GROUP ` , err : ` found EOF, expected BY at line 1, char 35 ` } ,
{ s : ` SELECT field1 FROM myseries LIMIT ` , err : ` found EOF, expected number at line 1, char 35 ` } ,
{ s : ` SELECT field1 FROM myseries LIMIT 10.5 ` , err : ` fractional parts not allowed in limit at line 1, char 35 ` } ,
2014-12-16 17:32:08 +00:00
{ s : ` SELECT field1 FROM myseries LIMIT 0 ` , err : ` LIMIT must be > 0 at line 1, char 35 ` } ,
2014-12-15 00:54:51 +00:00
{ s : ` SELECT field1 FROM myseries ORDER ` , err : ` found EOF, expected BY at line 1, char 35 ` } ,
{ s : ` SELECT field1 FROM myseries ORDER BY / ` , err : ` found /, expected identifier, ASC, or DESC at line 1, char 38 ` } ,
{ s : ` SELECT field1 FROM myseries ORDER BY 1 ` , err : ` found 1, expected identifier, ASC, or DESC at line 1, char 38 ` } ,
{ s : ` SELECT field1 AS ` , err : ` found EOF, expected identifier, string at line 1, char 18 ` } ,
{ s : ` SELECT field1 FROM 12 ` , err : ` found 12, expected identifier, string at line 1, char 20 ` } ,
{ s : ` SELECT field1 FROM myseries GROUP BY * ` , err : ` found *, expected identifier, string, number, bool at line 1, char 38 ` } ,
2014-11-22 23:33:21 +00:00
{ s : ` SELECT 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 FROM myseries ` , err : ` unable to parse number at line 1, char 8 ` } ,
{ s : ` SELECT 10.5h FROM myseries ` , err : ` found h, expected FROM at line 1, char 12 ` } ,
{ s : ` DELETE ` , err : ` found EOF, expected FROM at line 1, char 8 ` } ,
{ s : ` DELETE FROM ` , err : ` found EOF, expected identifier, string at line 1, char 13 ` } ,
{ s : ` DELETE FROM myseries WHERE ` , err : ` found EOF, expected identifier, string, number, bool at line 1, char 28 ` } ,
{ s : ` DROP SERIES ` , err : ` found EOF, expected identifier, string at line 1, char 13 ` } ,
{ s : ` LIST CONTINUOUS ` , err : ` found EOF, expected QUERIES at line 1, char 17 ` } ,
{ s : ` LIST FOO ` , err : ` found FOO, expected SERIES, CONTINUOUS at line 1, char 6 ` } ,
{ s : ` DROP CONTINUOUS ` , err : ` found EOF, expected QUERY at line 1, char 17 ` } ,
2014-11-25 00:58:21 +00:00
{ s : ` DROP CONTINUOUS QUERY ` , err : ` found EOF, expected identifier, string at line 1, char 23 ` } ,
2014-11-22 23:33:21 +00:00
{ s : ` DROP FOO ` , err : ` found FOO, expected SERIES, CONTINUOUS at line 1, char 6 ` } ,
2014-12-31 16:22:07 +00:00
{ s : ` CREATE USER testuser ` , err : ` found EOF, expected WITH at line 1, char 22 ` } ,
2015-01-03 03:56:26 +00:00
{ s : ` GRANT ` , err : ` found EOF, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7 ` } ,
{ s : ` GRANT BOGUS ` , err : ` found BOGUS, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7 ` } ,
{ s : ` GRANT READ ` , err : ` found EOF, expected ON at line 1, char 12 ` } ,
{ s : ` GRANT READ ON ` , err : ` found EOF, expected identifier, string at line 1, char 15 ` } ,
{ s : ` GRANT READ ON testdb ` , err : ` found EOF, expected TO at line 1, char 22 ` } ,
{ s : ` GRANT READ ON testdb TO ` , err : ` found EOF, expected identifier, string at line 1, char 25 ` } ,
2014-11-22 04:12:48 +00:00
}
for i , tt := range tests {
stmt , err := influxql . NewParser ( strings . NewReader ( tt . s ) ) . ParseStatement ( )
if ! reflect . DeepEqual ( tt . err , errstring ( err ) ) {
t . Errorf ( "%d. %q: error mismatch:\n exp=%s\n got=%s\n\n" , i , tt . s , tt . err , err )
} else if tt . err == "" && ! reflect . DeepEqual ( tt . stmt , stmt ) {
t . Errorf ( "%d. %q\n\nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n" , i , tt . s , tt . stmt , stmt )
}
}
}
// Ensure the parser can parse expressions into an AST.
func TestParser_ParseExpr ( t * testing . T ) {
var tests = [ ] struct {
s string
expr influxql . Expr
err string
} {
2014-12-15 15:34:32 +00:00
// Primitives
2014-11-22 04:12:48 +00:00
{ s : ` 100 ` , expr : & influxql . NumberLiteral { Val : 100 } } ,
{ s : ` "foo bar" ` , expr : & influxql . StringLiteral { Val : "foo bar" } } ,
{ s : ` true ` , expr : & influxql . BooleanLiteral { Val : true } } ,
{ s : ` false ` , expr : & influxql . BooleanLiteral { Val : false } } ,
{ s : ` my_ident ` , expr : & influxql . VarRef { Val : "my_ident" } } ,
2014-12-15 15:34:32 +00:00
{ s : ` "2000-01-01 00:00:00" ` , expr : & influxql . TimeLiteral { Val : mustParseTime ( "2000-01-01T00:00:00Z" ) } } ,
2014-12-21 17:59:36 +00:00
{ s : ` "2000-01-32 00:00:00" ` , err : ` unable to parse datetime at line 1, char 1 ` } ,
{ s : ` "2000-01-01" ` , expr : & influxql . TimeLiteral { Val : mustParseTime ( "2000-01-01T00:00:00Z" ) } } ,
{ s : ` "2000-01-99" ` , err : ` unable to parse date at line 1, char 1 ` } ,
2014-11-22 04:12:48 +00:00
2014-12-15 15:34:32 +00:00
// Simple binary expression
2014-11-22 04:12:48 +00:00
{
s : ` 1 + 2 ` ,
expr : & influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . NumberLiteral { Val : 1 } ,
RHS : & influxql . NumberLiteral { Val : 2 } ,
} ,
} ,
2014-12-15 15:34:32 +00:00
// Binary expression with LHS precedence
2014-11-22 04:12:48 +00:00
{
s : ` 1 * 2 + 3 ` ,
expr : & influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . BinaryExpr {
Op : influxql . MUL ,
LHS : & influxql . NumberLiteral { Val : 1 } ,
RHS : & influxql . NumberLiteral { Val : 2 } ,
} ,
RHS : & influxql . NumberLiteral { Val : 3 } ,
} ,
} ,
2014-12-15 15:34:32 +00:00
// Binary expression with RHS precedence
2014-11-22 04:12:48 +00:00
{
s : ` 1 + 2 * 3 ` ,
expr : & influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . NumberLiteral { Val : 1 } ,
RHS : & influxql . BinaryExpr {
Op : influxql . MUL ,
LHS : & influxql . NumberLiteral { Val : 2 } ,
RHS : & influxql . NumberLiteral { Val : 3 } ,
} ,
} ,
} ,
2014-12-15 15:34:32 +00:00
// Binary expression with LHS paren group.
2014-11-25 06:12:32 +00:00
{
s : ` (1 + 2) * 3 ` ,
expr : & influxql . BinaryExpr {
Op : influxql . MUL ,
LHS : & influxql . ParenExpr {
Expr : & influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . NumberLiteral { Val : 1 } ,
RHS : & influxql . NumberLiteral { Val : 2 } ,
} ,
} ,
RHS : & influxql . NumberLiteral { Val : 3 } ,
} ,
} ,
2014-12-31 23:50:10 +00:00
// Binary expression with no precedence, tests left associativity.
{
s : ` 1 * 2 * 3 ` ,
expr : & influxql . BinaryExpr {
Op : influxql . MUL ,
LHS : & influxql . BinaryExpr {
Op : influxql . MUL ,
LHS : & influxql . NumberLiteral { Val : 1 } ,
RHS : & influxql . NumberLiteral { Val : 2 } ,
} ,
RHS : & influxql . NumberLiteral { Val : 3 } ,
} ,
} ,
2014-12-15 15:34:32 +00:00
// Complex binary expression.
2014-11-22 04:12:48 +00:00
{
s : ` value + 3 < 30 AND 1 + 2 OR true ` ,
expr : & influxql . BinaryExpr {
Op : influxql . OR ,
LHS : & influxql . BinaryExpr {
Op : influxql . AND ,
LHS : & influxql . BinaryExpr {
Op : influxql . LT ,
LHS : & influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . VarRef { Val : "value" } ,
RHS : & influxql . NumberLiteral { Val : 3 } ,
} ,
RHS : & influxql . NumberLiteral { Val : 30 } ,
} ,
RHS : & influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . NumberLiteral { Val : 1 } ,
RHS : & influxql . NumberLiteral { Val : 2 } ,
} ,
} ,
RHS : & influxql . BooleanLiteral { Val : true } ,
} ,
} ,
2014-11-25 03:43:23 +00:00
2014-12-15 15:34:32 +00:00
// Function call (empty)
2014-11-25 03:43:23 +00:00
{
s : ` my_func() ` ,
expr : & influxql . Call {
Name : "my_func" ,
} ,
} ,
2014-12-15 15:34:32 +00:00
// Function call (multi-arg)
2014-11-25 03:43:23 +00:00
{
s : ` my_func(1, 2 + 3) ` ,
expr : & influxql . Call {
Name : "my_func" ,
Args : [ ] influxql . Expr {
& influxql . NumberLiteral { Val : 1 } ,
& influxql . BinaryExpr {
Op : influxql . ADD ,
LHS : & influxql . NumberLiteral { Val : 2 } ,
RHS : & influxql . NumberLiteral { Val : 3 } ,
} ,
} ,
} ,
} ,
2014-11-22 04:12:48 +00:00
}
for i , tt := range tests {
expr , err := influxql . NewParser ( strings . NewReader ( tt . s ) ) . ParseExpr ( )
if ! reflect . DeepEqual ( tt . err , errstring ( err ) ) {
t . Errorf ( "%d. %q: error mismatch:\n exp=%s\n got=%s\n\n" , i , tt . s , tt . err , err )
} else if tt . err == "" && ! reflect . DeepEqual ( tt . expr , expr ) {
t . Errorf ( "%d. %q\n\nexpr mismatch:\n\nexp=%#v\n\ngot=%#v\n\n" , i , tt . s , tt . expr , expr )
}
}
}
// Ensure a time duration can be parsed.
func TestParseDuration ( t * testing . T ) {
var tests = [ ] struct {
s string
d time . Duration
err string
} {
{ s : ` 3 ` , d : 3 * time . Microsecond } ,
{ s : ` 1000 ` , d : 1000 * time . Microsecond } ,
{ s : ` 10u ` , d : 10 * time . Microsecond } ,
2014-11-25 06:43:23 +00:00
{ s : ` 10µ ` , d : 10 * time . Microsecond } ,
2014-11-22 04:12:48 +00:00
{ s : ` 15ms ` , d : 15 * time . Millisecond } ,
{ s : ` 100s ` , d : 100 * time . Second } ,
{ s : ` 2m ` , d : 2 * time . Minute } ,
{ s : ` 2h ` , d : 2 * time . Hour } ,
{ s : ` 2d ` , d : 2 * 24 * time . Hour } ,
{ s : ` 2w ` , d : 2 * 7 * 24 * time . Hour } ,
{ s : ` ` , err : "invalid duration" } ,
{ s : ` w ` , err : "invalid duration" } ,
{ s : ` 1.2w ` , err : "invalid duration" } ,
{ s : ` 10x ` , err : "invalid duration" } ,
}
for i , tt := range tests {
d , err := influxql . ParseDuration ( tt . s )
if ! reflect . DeepEqual ( tt . err , errstring ( err ) ) {
t . Errorf ( "%d. %q: error mismatch:\n exp=%s\n got=%s\n\n" , i , tt . s , tt . err , err )
} else if tt . d != d {
t . Errorf ( "%d. %q\n\nduration mismatch:\n\nexp=%#v\n\ngot=%#v\n\n" , i , tt . s , tt . d , d )
}
}
}
2014-12-06 18:17:58 +00:00
// Ensure a time duration can be formatted.
func TestFormatDuration ( t * testing . T ) {
var tests = [ ] struct {
d time . Duration
s string
} {
{ d : 3 * time . Microsecond , s : ` 3 ` } ,
{ d : 1001 * time . Microsecond , s : ` 1001 ` } ,
{ d : 15 * time . Millisecond , s : ` 15ms ` } ,
{ d : 100 * time . Second , s : ` 100s ` } ,
{ d : 2 * time . Minute , s : ` 2m ` } ,
{ d : 2 * time . Hour , s : ` 2h ` } ,
{ d : 2 * 24 * time . Hour , s : ` 2d ` } ,
{ d : 2 * 7 * 24 * time . Hour , s : ` 2w ` } ,
}
for i , tt := range tests {
s := influxql . FormatDuration ( tt . d )
if tt . s != s {
t . Errorf ( "%d. %v: mismatch: %s != %s" , i , tt . d , tt . s , s )
}
}
}
// Ensure a string can be quoted.
func TestQuote ( t * testing . T ) {
for i , tt := range [ ] struct {
in string
out string
} {
{ ` ` , ` "" ` } ,
{ ` foo ` , ` "foo" ` } ,
{ "foo\nbar" , ` "foo\nbar" ` } ,
{ ` foo bar\\ ` , ` "foo bar\\\\" ` } ,
{ ` "foo" ` , ` "\"foo\"" ` } ,
} {
if out := influxql . Quote ( tt . in ) ; tt . out != out {
t . Errorf ( "%d. %s: mismatch: %s != %s" , i , tt . in , tt . out , out )
}
}
}
// Ensure an identifier can be quoted when appropriate.
func TestQuoteIdent ( t * testing . T ) {
for i , tt := range [ ] struct {
ident string
s string
} {
{ ` ` , ` "" ` } ,
{ ` foo ` , ` foo ` } ,
{ ` foo.bar.baz ` , ` foo.bar.baz ` } ,
{ ` my var ` , ` "my var" ` } ,
{ ` my-var ` , ` "my-var" ` } ,
{ ` my_var ` , ` my_var ` } ,
} {
if s := influxql . QuoteIdent ( tt . ident ) ; tt . s != s {
t . Errorf ( "%d. %s: mismatch: %s != %s" , i , tt . ident , tt . s , s )
}
}
}
2014-11-25 00:58:21 +00:00
func BenchmarkParserParseStatement ( b * testing . B ) {
b . ReportAllocs ( )
s := ` SELECT field FROM "series" WHERE value > 10 `
for i := 0 ; i < b . N ; i ++ {
if stmt , err := influxql . NewParser ( strings . NewReader ( s ) ) . ParseStatement ( ) ; err != nil {
b . Fatalf ( "unexpected error: %s" , err )
} else if stmt == nil {
b . Fatalf ( "expected statement" , stmt )
}
}
b . SetBytes ( int64 ( len ( s ) ) )
}
2014-12-08 05:08:39 +00:00
// MustParseSelectStatement parses a select statement. Panic on error.
func MustParseSelectStatement ( s string ) * influxql . SelectStatement {
stmt , err := influxql . NewParser ( strings . NewReader ( s ) ) . ParseStatement ( )
if err != nil {
panic ( err . Error ( ) )
}
return stmt . ( * influxql . SelectStatement )
}
2014-12-11 16:58:33 +00:00
// MustParseExpr parses an expression. Panic on error.
func MustParseExpr ( s string ) influxql . Expr {
expr , err := influxql . NewParser ( strings . NewReader ( s ) ) . ParseExpr ( )
if err != nil {
panic ( err . Error ( ) )
}
return expr
}
2014-11-22 04:12:48 +00:00
// errstring converts an error to its string representation.
func errstring ( err error ) string {
if err != nil {
return err . Error ( )
}
return ""
}