2014-12-06 18:17:58 +00:00
|
|
|
package influxql_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/influxdb/influxdb/influxql"
|
|
|
|
)
|
|
|
|
|
2014-12-09 15:45:29 +00:00
|
|
|
// Ensure a value's data type can be retrieved.
|
|
|
|
func TestInspectDataType(t *testing.T) {
|
|
|
|
for i, tt := range []struct {
|
|
|
|
v interface{}
|
|
|
|
typ influxql.DataType
|
|
|
|
}{
|
|
|
|
{float64(100), influxql.Number},
|
|
|
|
} {
|
|
|
|
if typ := influxql.InspectDataType(tt.v); tt.typ != typ {
|
|
|
|
t.Errorf("%d. %v (%s): unexpected type: %s", i, tt.v, tt.typ, typ)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-06 18:17:58 +00:00
|
|
|
// Ensure the SELECT statement can extract substatements.
|
|
|
|
func TestSelectStatement_Substatement(t *testing.T) {
|
|
|
|
var tests = []struct {
|
|
|
|
stmt string
|
|
|
|
expr *influxql.VarRef
|
|
|
|
sub string
|
|
|
|
err string
|
|
|
|
}{
|
|
|
|
// 0. Single series
|
|
|
|
{
|
|
|
|
stmt: `SELECT value FROM myseries WHERE value > 1`,
|
|
|
|
expr: &influxql.VarRef{Val: "value"},
|
|
|
|
sub: `SELECT value FROM myseries WHERE value > 1.000`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// 1. Simple join
|
|
|
|
{
|
|
|
|
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM aa JOIN bb`,
|
|
|
|
expr: &influxql.VarRef{Val: "aa.value"},
|
|
|
|
sub: `SELECT aa.value FROM aa`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// 2. Simple merge
|
|
|
|
{
|
|
|
|
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM aa MERGE bb`,
|
|
|
|
expr: &influxql.VarRef{Val: "bb.value"},
|
|
|
|
sub: `SELECT bb.value FROM bb`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// 3. Join with condition
|
|
|
|
{
|
|
|
|
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM aa JOIN bb WHERE aa.host = "servera" AND bb.host = "serverb"`,
|
|
|
|
expr: &influxql.VarRef{Val: "bb.value"},
|
|
|
|
sub: `SELECT bb.value FROM bb WHERE bb.host = "serverb"`,
|
|
|
|
},
|
|
|
|
|
2014-12-08 05:08:39 +00:00
|
|
|
// 4. Join with complex condition
|
2014-12-06 18:17:58 +00:00
|
|
|
{
|
|
|
|
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM aa JOIN bb WHERE aa.host = "servera" AND (bb.host = "serverb" OR bb.host = "serverc") AND 1 = 2`,
|
|
|
|
expr: &influxql.VarRef{Val: "bb.value"},
|
|
|
|
sub: `SELECT bb.value FROM bb WHERE (bb.host = "serverb" OR bb.host = "serverc") AND 1.000 = 2.000`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
|
// Parse statement.
|
|
|
|
stmt, err := influxql.NewParser(strings.NewReader(tt.stmt)).ParseStatement()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("invalid statement: %q: %s", tt.stmt, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract substatement.
|
|
|
|
sub, err := stmt.(*influxql.SelectStatement).Substatement(tt.expr)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("%d. %q: unexpected error: %s", i, tt.stmt, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if substr := sub.String(); tt.sub != substr {
|
|
|
|
t.Errorf("%d. %q: unexpected substatement:\n\nexp=%s\n\ngot=%s\n\n", i, tt.stmt, tt.sub, substr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-11 06:32:45 +00:00
|
|
|
|
|
|
|
// Ensure an expression can be folded.
|
|
|
|
func TestFold(t *testing.T) {
|
|
|
|
for i, tt := range []struct {
|
|
|
|
in string
|
|
|
|
out string
|
|
|
|
}{
|
|
|
|
// Number literals.
|
|
|
|
{`1 + 2`, `3.000`},
|
|
|
|
{`(foo*2) + (4/2) + (3 * 5) - 0.5`, `(foo * 2.000) + 16.500`},
|
|
|
|
{`foo(bar(2 + 3), 4)`, `foo(bar(5.000), 4.000)`},
|
|
|
|
{`4 / 0`, `0.000`},
|
|
|
|
{`4 = 4`, `true`},
|
|
|
|
{`4 != 4`, `false`},
|
|
|
|
{`6 > 4`, `true`},
|
|
|
|
{`4 >= 4`, `true`},
|
|
|
|
{`4 < 6`, `true`},
|
|
|
|
{`4 <= 4`, `true`},
|
|
|
|
{`4 AND 5`, `4.000 AND 5.000`},
|
|
|
|
|
|
|
|
// Boolean literals.
|
|
|
|
{`true AND false`, `false`},
|
|
|
|
{`true OR false`, `true`},
|
|
|
|
{`true = false`, `false`},
|
|
|
|
{`true != false`, `true`},
|
|
|
|
{`true + false`, `true + false`},
|
|
|
|
|
|
|
|
// Time literals.
|
|
|
|
{`now() + 2h`, `"2000-01-01 02:00:00"`},
|
|
|
|
{`now() / 2h`, `"2000-01-01 00:00:00" / 2h`},
|
|
|
|
{`4µ + now()`, `"2000-01-01 00:00:00.000004"`},
|
|
|
|
{`now() = now()`, `true`},
|
|
|
|
{`now() != now()`, `false`},
|
|
|
|
{`now() < now() + 1h`, `true`},
|
|
|
|
{`now() <= now() + 1h`, `true`},
|
|
|
|
{`now() >= now() - 1h`, `true`},
|
|
|
|
{`now() > now() - 1h`, `true`},
|
|
|
|
{`now() - (now() - 60s)`, `1m`},
|
|
|
|
{`now() AND now()`, `"2000-01-01 00:00:00" AND "2000-01-01 00:00:00"`},
|
|
|
|
|
|
|
|
// Duration literals.
|
|
|
|
{`10m + 1h - 60s`, `69m`},
|
|
|
|
{`(10m / 2) * 5`, `25m`},
|
|
|
|
{`60s = 1m`, `true`},
|
|
|
|
{`60s != 1m`, `false`},
|
|
|
|
{`60s < 1h`, `true`},
|
|
|
|
{`60s <= 1h`, `true`},
|
|
|
|
{`60s > 12s`, `true`},
|
|
|
|
{`60s >= 1m`, `true`},
|
|
|
|
{`60s AND 1m`, `1m AND 1m`},
|
|
|
|
{`60m / 0`, `0s`},
|
|
|
|
{`60m + 50`, `1h + 50.000`},
|
|
|
|
|
|
|
|
// String literals.
|
|
|
|
{`"foo" + 'bar'`, `"foobar"`},
|
|
|
|
} {
|
|
|
|
// Fold expression.
|
2014-12-11 16:58:33 +00:00
|
|
|
now := mustParseTime("2000-01-01T00:00:00Z")
|
|
|
|
expr := influxql.Fold(MustParseExpr(tt.in), &now)
|
2014-12-11 06:32:45 +00:00
|
|
|
|
|
|
|
// Compare with expected output.
|
|
|
|
if out := expr.String(); tt.out != out {
|
|
|
|
t.Errorf("%d. %s: unexpected expr:\n\nexp=%s\n\ngot=%s\n\n", i, tt.in, tt.out, out)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-11 16:58:33 +00:00
|
|
|
|
|
|
|
// Ensure an a "now()" call is not folded when now is not passed in.
|
|
|
|
func TestFold_WithoutNow(t *testing.T) {
|
|
|
|
expr := influxql.Fold(MustParseExpr(`now()`), nil)
|
|
|
|
if s := expr.String(); s != `now()` {
|
|
|
|
t.Fatalf("unexpected expr: %s", s)
|
|
|
|
}
|
|
|
|
}
|
2014-12-15 15:34:32 +00:00
|
|
|
|
|
|
|
// Ensure the time range of an expression can be extracted.
|
|
|
|
func TestTimeRange(t *testing.T) {
|
|
|
|
for i, tt := range []struct {
|
|
|
|
expr string
|
|
|
|
min, max string
|
|
|
|
}{
|
|
|
|
// LHS VarRef
|
|
|
|
{expr: `time > "2000-01-01 00:00:00"`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`},
|
|
|
|
{expr: `time >= "2000-01-01 00:00:00"`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
|
|
|
|
{expr: `time < "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`},
|
|
|
|
{expr: `time <= "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
|
|
|
|
|
|
|
|
// RHS VarRef
|
|
|
|
{expr: `"2000-01-01 00:00:00" > time`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`},
|
|
|
|
{expr: `"2000-01-01 00:00:00" >= time`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
|
|
|
|
{expr: `"2000-01-01 00:00:00" < time`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`},
|
|
|
|
{expr: `"2000-01-01 00:00:00" <= time`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
|
|
|
|
|
|
|
|
// Equality
|
|
|
|
{expr: `time = "2000-01-01 00:00:00"`, min: `2000-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
|
|
|
|
|
|
|
|
// Multiple time expressions.
|
|
|
|
{expr: `time >= "2000-01-01 00:00:00" AND time < "2000-01-02 00:00:00"`, min: `2000-01-01 00:00:00`, max: `2000-01-01 23:59:59.999999`},
|
|
|
|
|
2014-12-16 15:57:27 +00:00
|
|
|
// Min/max crossover
|
|
|
|
{expr: `time >= "2000-01-01 00:00:00" AND time <= "1999-01-01 00:00:00"`, min: `2000-01-01 00:00:00`, max: `1999-01-01 00:00:00`},
|
|
|
|
|
2014-12-15 15:34:32 +00:00
|
|
|
// Non-comparative expressions.
|
|
|
|
{expr: `time`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
|
|
|
|
{expr: `time + 2`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
|
|
|
|
{expr: `time - "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
|
|
|
|
{expr: `time AND "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
|
|
|
|
} {
|
|
|
|
// Extract time range.
|
|
|
|
expr := MustParseExpr(tt.expr)
|
|
|
|
min, max := influxql.TimeRange(expr)
|
|
|
|
|
|
|
|
// Compare with expected min/max.
|
|
|
|
if min := min.Format(influxql.TimeFormat); tt.min != min {
|
|
|
|
t.Errorf("%d. %s: unexpected min:\n\nexp=%s\n\ngot=%s\n\n", i, tt.expr, tt.min, min)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if max := max.Format(influxql.TimeFormat); tt.max != max {
|
|
|
|
t.Errorf("%d. %s: unexpected max:\n\nexp=%s\n\ngot=%s\n\n", i, tt.expr, tt.max, max)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|