influxdb/query/functions/first.go

171 lines
4.3 KiB
Go
Raw Normal View History

2018-05-21 21:13:54 +00:00
package functions
import (
"fmt"
2018-05-21 21:20:06 +00:00
"github.com/influxdata/platform/query"
"github.com/influxdata/platform/query/execute"
"github.com/influxdata/platform/query/plan"
"github.com/influxdata/platform/query/semantic"
2018-05-21 21:13:54 +00:00
)
const FirstKind = "first"
type FirstOpSpec struct {
execute.SelectorConfig
}
var firstSignature = query.DefaultFunctionSignature()
func init() {
firstSignature.Params["column"] = semantic.String
firstSignature.Params["useRowTime"] = semantic.Bool
query.RegisterFunction(FirstKind, createFirstOpSpec, firstSignature)
query.RegisterOpSpec(FirstKind, newFirstOp)
plan.RegisterProcedureSpec(FirstKind, newFirstProcedure, FirstKind)
execute.RegisterTransformation(FirstKind, createFirstTransformation)
}
func createFirstOpSpec(args query.Arguments, a *query.Administration) (query.OperationSpec, error) {
if err := a.AddParentFromArgs(args); err != nil {
return nil, err
}
spec := new(FirstOpSpec)
if err := spec.SelectorConfig.ReadArgs(args); err != nil {
return nil, err
}
return spec, nil
}
func newFirstOp() query.OperationSpec {
return new(FirstOpSpec)
}
func (s *FirstOpSpec) Kind() query.OperationKind {
return FirstKind
}
type FirstProcedureSpec struct {
execute.SelectorConfig
}
func newFirstProcedure(qs query.OperationSpec, pa plan.Administration) (plan.ProcedureSpec, error) {
spec, ok := qs.(*FirstOpSpec)
if !ok {
return nil, fmt.Errorf("invalid spec type %T", qs)
}
return &FirstProcedureSpec{
SelectorConfig: spec.SelectorConfig,
}, nil
}
func (s *FirstProcedureSpec) Kind() plan.ProcedureKind {
return FirstKind
}
func (s *FirstProcedureSpec) PushDownRules() []plan.PushDownRule {
return []plan.PushDownRule{{
Root: FromKind,
Through: []plan.ProcedureKind{GroupKind, LimitKind, FilterKind},
Match: func(spec plan.ProcedureSpec) bool {
selectSpec := spec.(*FromProcedureSpec)
return !selectSpec.AggregateSet
},
}}
}
func (s *FirstProcedureSpec) PushDown(root *plan.Procedure, dup func() *plan.Procedure) {
selectSpec := root.Spec.(*FromProcedureSpec)
if selectSpec.BoundsSet || selectSpec.LimitSet || selectSpec.DescendingSet {
root = dup()
selectSpec = root.Spec.(*FromProcedureSpec)
selectSpec.BoundsSet = false
selectSpec.Bounds = plan.BoundsSpec{}
selectSpec.LimitSet = false
selectSpec.PointsLimit = 0
selectSpec.SeriesLimit = 0
selectSpec.SeriesOffset = 0
selectSpec.DescendingSet = false
selectSpec.Descending = false
return
}
selectSpec.BoundsSet = true
selectSpec.Bounds = plan.BoundsSpec{
Start: query.MinTime,
Stop: query.Now,
}
selectSpec.LimitSet = true
selectSpec.PointsLimit = 1
selectSpec.DescendingSet = true
selectSpec.Descending = false
}
func (s *FirstProcedureSpec) Copy() plan.ProcedureSpec {
ns := new(FirstProcedureSpec)
*ns = *s
ns.SelectorConfig = s.SelectorConfig
return ns
}
type FirstSelector struct {
selected bool
}
func createFirstTransformation(id execute.DatasetID, mode execute.AccumulationMode, spec plan.ProcedureSpec, a execute.Administration) (execute.Transformation, execute.Dataset, error) {
ps, ok := spec.(*FirstProcedureSpec)
if !ok {
return nil, nil, fmt.Errorf("invalid spec type %T", ps)
}
t, d := execute.NewIndexSelectorTransformationAndDataset(id, mode, new(FirstSelector), ps.SelectorConfig, a.Allocator())
return t, d, nil
}
func (s *FirstSelector) reset() {
s.selected = false
}
func (s *FirstSelector) NewBoolSelector() execute.DoBoolIndexSelector {
s.reset()
return s
}
func (s *FirstSelector) NewIntSelector() execute.DoIntIndexSelector {
s.reset()
return s
}
func (s *FirstSelector) NewUIntSelector() execute.DoUIntIndexSelector {
s.reset()
return s
}
func (s *FirstSelector) NewFloatSelector() execute.DoFloatIndexSelector {
s.reset()
return s
}
func (s *FirstSelector) NewStringSelector() execute.DoStringIndexSelector {
s.reset()
return s
}
func (s *FirstSelector) selectFirst(l int) []int {
if !s.selected && l > 0 {
s.selected = true
return []int{0}
}
return nil
}
func (s *FirstSelector) DoBool(vs []bool) []int {
return s.selectFirst(len(vs))
}
func (s *FirstSelector) DoInt(vs []int64) []int {
return s.selectFirst(len(vs))
}
func (s *FirstSelector) DoUInt(vs []uint64) []int {
return s.selectFirst(len(vs))
}
func (s *FirstSelector) DoFloat(vs []float64) []int {
return s.selectFirst(len(vs))
}
func (s *FirstSelector) DoString(vs []string) []int {
return s.selectFirst(len(vs))
}