influxdb/query/execute/executor_test.go

431 lines
13 KiB
Go

package execute_test
import (
"context"
"math"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/platform"
"github.com/influxdata/platform/query"
"github.com/influxdata/platform/query/ast"
_ "github.com/influxdata/platform/query/builtin"
"github.com/influxdata/platform/query/execute"
"github.com/influxdata/platform/query/execute/executetest"
"github.com/influxdata/platform/query/functions"
"github.com/influxdata/platform/query/plan"
"github.com/influxdata/platform/query/semantic"
uuid "github.com/satori/go.uuid"
)
var epoch = time.Unix(0, 0)
var orgID platform.ID
func init() {
orgID.DecodeFromString("aaaa")
}
func TestExecutor_Execute(t *testing.T) {
testCases := []struct {
name string
plan *plan.PlanSpec
want map[string][]*executetest.Table
}{
{
name: "simple aggregate",
plan: &plan.PlanSpec{
Now: epoch.Add(5),
Resources: query.ResourceManagement{
ConcurrencyQuota: 1,
MemoryBytesQuota: math.MaxInt64,
},
Bounds: plan.BoundsSpec{
Start: query.Time{Absolute: time.Unix(0, 1)},
Stop: query.Time{Absolute: time.Unix(0, 5)},
},
Procedures: map[plan.ProcedureID]*plan.Procedure{
plan.ProcedureIDFromOperationID("from"): {
ID: plan.ProcedureIDFromOperationID("from"),
Spec: &testFromProcedureSource{
data: []query.Table{&executetest.Table{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TFloat},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(0), 1.0},
{execute.Time(0), execute.Time(5), execute.Time(1), 2.0},
{execute.Time(0), execute.Time(5), execute.Time(2), 3.0},
{execute.Time(0), execute.Time(5), execute.Time(3), 4.0},
{execute.Time(0), execute.Time(5), execute.Time(4), 5.0},
},
}},
},
Parents: nil,
Children: []plan.ProcedureID{plan.ProcedureIDFromOperationID("sum")},
},
plan.ProcedureIDFromOperationID("sum"): {
ID: plan.ProcedureIDFromOperationID("sum"),
Spec: &functions.SumProcedureSpec{
AggregateConfig: execute.DefaultAggregateConfig,
},
Parents: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("from"),
},
Children: nil,
},
},
Results: map[string]plan.YieldSpec{
plan.DefaultYieldName: {ID: plan.ProcedureIDFromOperationID("sum")},
},
},
want: map[string][]*executetest.Table{
plan.DefaultYieldName: []*executetest.Table{{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TFloat},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(5), 15.0},
},
}},
},
},
{
name: "simple join",
plan: &plan.PlanSpec{
Now: epoch.Add(5),
Resources: query.ResourceManagement{
ConcurrencyQuota: 1,
MemoryBytesQuota: math.MaxInt64,
},
Bounds: plan.BoundsSpec{
Start: query.Time{Absolute: time.Unix(0, 1)},
Stop: query.Time{Absolute: time.Unix(0, 5)},
},
Procedures: map[plan.ProcedureID]*plan.Procedure{
plan.ProcedureIDFromOperationID("from"): {
ID: plan.ProcedureIDFromOperationID("from"),
Spec: &testFromProcedureSource{
data: []query.Table{&executetest.Table{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TInt},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(0), int64(1)},
{execute.Time(0), execute.Time(5), execute.Time(1), int64(2)},
{execute.Time(0), execute.Time(5), execute.Time(2), int64(3)},
{execute.Time(0), execute.Time(5), execute.Time(3), int64(4)},
{execute.Time(0), execute.Time(5), execute.Time(4), int64(5)},
},
}},
},
Parents: nil,
Children: []plan.ProcedureID{plan.ProcedureIDFromOperationID("sum")},
},
plan.ProcedureIDFromOperationID("sum"): {
ID: plan.ProcedureIDFromOperationID("sum"),
Spec: &functions.SumProcedureSpec{
AggregateConfig: execute.DefaultAggregateConfig,
},
Parents: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("from"),
},
Children: []plan.ProcedureID{plan.ProcedureIDFromOperationID("join")},
},
plan.ProcedureIDFromOperationID("count"): {
ID: plan.ProcedureIDFromOperationID("count"),
Spec: &functions.CountProcedureSpec{
AggregateConfig: execute.DefaultAggregateConfig,
},
Parents: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("from"),
},
Children: []plan.ProcedureID{plan.ProcedureIDFromOperationID("join")},
},
plan.ProcedureIDFromOperationID("join"): {
ID: plan.ProcedureIDFromOperationID("join"),
Spec: &functions.MergeJoinProcedureSpec{
TableNames: map[plan.ProcedureID]string{
plan.ProcedureIDFromOperationID("sum"): "sum",
plan.ProcedureIDFromOperationID("count"): "count",
},
On: []string{"_time", "_start", "_stop"},
Fn: &semantic.FunctionExpression{
Params: []*semantic.FunctionParam{{Key: &semantic.Identifier{Name: "t"}}},
Body: &semantic.ObjectExpression{
Properties: []*semantic.Property{
{
Key: &semantic.Identifier{Name: "_time"},
Value: &semantic.MemberExpression{
Object: &semantic.MemberExpression{
Object: &semantic.IdentifierExpression{
Name: "t",
},
Property: "sum",
},
Property: "_time",
},
},
{
Key: &semantic.Identifier{Name: "_start"},
Value: &semantic.MemberExpression{
Object: &semantic.MemberExpression{
Object: &semantic.IdentifierExpression{
Name: "t",
},
Property: "sum",
},
Property: "_start",
},
},
{
Key: &semantic.Identifier{Name: "_stop"},
Value: &semantic.MemberExpression{
Object: &semantic.MemberExpression{
Object: &semantic.IdentifierExpression{
Name: "t",
},
Property: "sum",
},
Property: "_stop",
},
},
{
Key: &semantic.Identifier{Name: "_value"},
Value: &semantic.BinaryExpression{
Operator: ast.DivisionOperator,
Left: &semantic.MemberExpression{
Object: &semantic.MemberExpression{
Object: &semantic.IdentifierExpression{
Name: "t",
},
Property: "sum",
},
Property: "_value",
},
Right: &semantic.MemberExpression{
Object: &semantic.MemberExpression{
Object: &semantic.IdentifierExpression{
Name: "t",
},
Property: "count",
},
Property: "_value",
},
},
},
},
},
},
},
Parents: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("sum"),
plan.ProcedureIDFromOperationID("count"),
},
Children: nil,
},
},
Results: map[string]plan.YieldSpec{
plan.DefaultYieldName: {ID: plan.ProcedureIDFromOperationID("join")},
},
},
want: map[string][]*executetest.Table{
plan.DefaultYieldName: []*executetest.Table{{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TInt},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(5), int64(3)},
},
}},
},
},
{
name: "multiple aggregates",
plan: &plan.PlanSpec{
Now: epoch.Add(5),
Resources: query.ResourceManagement{
ConcurrencyQuota: 1,
MemoryBytesQuota: math.MaxInt64,
},
Bounds: plan.BoundsSpec{
Start: query.Time{Absolute: time.Unix(0, 1)},
Stop: query.Time{Absolute: time.Unix(0, 5)},
},
Procedures: map[plan.ProcedureID]*plan.Procedure{
plan.ProcedureIDFromOperationID("from"): {
ID: plan.ProcedureIDFromOperationID("from"),
Spec: &testFromProcedureSource{
data: []query.Table{&executetest.Table{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TFloat},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(0), 1.0},
{execute.Time(0), execute.Time(5), execute.Time(1), 2.0},
{execute.Time(0), execute.Time(5), execute.Time(2), 3.0},
{execute.Time(0), execute.Time(5), execute.Time(3), 4.0},
{execute.Time(0), execute.Time(5), execute.Time(4), 5.0},
},
}},
},
Parents: nil,
Children: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("sum"),
plan.ProcedureIDFromOperationID("mean"),
},
},
plan.ProcedureIDFromOperationID("sum"): {
ID: plan.ProcedureIDFromOperationID("sum"),
Spec: &functions.SumProcedureSpec{
AggregateConfig: execute.DefaultAggregateConfig,
},
Parents: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("from"),
},
Children: nil,
},
plan.ProcedureIDFromOperationID("mean"): {
ID: plan.ProcedureIDFromOperationID("mean"),
Spec: &functions.MeanProcedureSpec{
AggregateConfig: execute.DefaultAggregateConfig,
},
Parents: []plan.ProcedureID{
plan.ProcedureIDFromOperationID("from"),
},
Children: nil,
},
},
Results: map[string]plan.YieldSpec{
"sum": {ID: plan.ProcedureIDFromOperationID("sum")},
"mean": {ID: plan.ProcedureIDFromOperationID("mean")},
},
},
want: map[string][]*executetest.Table{
"sum": []*executetest.Table{{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TFloat},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(5), 15.0},
},
}},
"mean": []*executetest.Table{{
KeyCols: []string{"_start", "_stop"},
ColMeta: []query.ColMeta{
{Label: "_start", Type: query.TTime},
{Label: "_stop", Type: query.TTime},
{Label: "_time", Type: query.TTime},
{Label: "_value", Type: query.TFloat},
},
Data: [][]interface{}{
{execute.Time(0), execute.Time(5), execute.Time(5), 3.0},
},
}},
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
exe := execute.NewExecutor(nil)
results, err := exe.Execute(context.Background(), orgID, tc.plan, executetest.UnlimitedAllocator)
if err != nil {
t.Fatal(err)
}
got := make(map[string][]*executetest.Table, len(results))
for name, r := range results {
if err := r.Tables().Do(func(tbl query.Table) error {
cb, err := executetest.ConvertTable(tbl)
if err != nil {
return err
}
got[name] = append(got[name], cb)
return nil
}); err != nil {
t.Fatal(err)
}
}
for _, g := range got {
executetest.NormalizeTables(g)
}
for _, w := range tc.want {
executetest.NormalizeTables(w)
}
if !cmp.Equal(got, tc.want) {
t.Error("unexpected results -want/+got", cmp.Diff(tc.want, got))
}
})
}
}
type testFromProcedureSource struct {
data []query.Table
ts []execute.Transformation
}
func (p *testFromProcedureSource) Kind() plan.ProcedureKind {
return "from-test"
}
func (p *testFromProcedureSource) Copy() plan.ProcedureSpec {
return p
}
func (p *testFromProcedureSource) AddTransformation(t execute.Transformation) {
p.ts = append(p.ts, t)
}
func (p *testFromProcedureSource) Run(ctx context.Context) {
id := execute.DatasetID(uuid.NewV4())
for _, t := range p.ts {
var max execute.Time
for _, b := range p.data {
t.Process(id, b)
stopIdx := execute.ColIdx(execute.DefaultStopColLabel, b.Cols())
if stopIdx >= 0 {
if s := b.Key().ValueTime(stopIdx); s > max {
max = s
}
}
}
t.UpdateWatermark(id, max)
t.Finish(id, nil)
}
}
func init() {
execute.RegisterSource("from-test", createTestFromSource)
}
func createTestFromSource(prSpec plan.ProcedureSpec, id execute.DatasetID, a execute.Administration) (execute.Source, error) {
return prSpec.(*testFromProcedureSource), nil
}