func TestIteratorOptions_ElapsedInterval_Default(t *testing.T) { opt := influxql.IteratorOptions{} expected := influxql.Interval{Duration: time.Nanosecond} actual := opt.ElapsedInterval() if actual != expected { t.Errorf("expected elapsed interval to be %v, got %v", expected, actual) } }
func TestIteratorOptions_DerivativeInterval_Default(t *testing.T) { opt := influxql.IteratorOptions{} expected := influxql.Interval{Duration: time.Second} actual := opt.DerivativeInterval() if actual != expected { t.Errorf("expected derivative interval to be %v, got %v", expected, actual) } }
func TestIteratorOptions_SeekTime_Descending(t *testing.T) { opt := influxql.IteratorOptions{ StartTime: 30, EndTime: 60, Ascending: false, } time := opt.SeekTime() if time != 60 { t.Errorf("expected time to be 60, got %d", time) } }
func TestIteratorOptions_DerivativeInterval_GroupBy(t *testing.T) { opt := influxql.IteratorOptions{ Interval: influxql.Interval{ Duration: 10, Offset: 2, }, } expected := influxql.Interval{Duration: 10} actual := opt.DerivativeInterval() if actual != expected { t.Errorf("expected derivative interval to be %v, got %v", expected, actual) } }
func TestIteratorOptions_Window_Default(t *testing.T) { opt := influxql.IteratorOptions{ StartTime: 0, EndTime: 60, } start, end := opt.Window(34) if start != 0 { t.Errorf("expected start to be 0, got %d", start) } if end != 61 { t.Errorf("expected end to be 61, got %d", end) } }
func TestIteratorOptions_Window_Interval(t *testing.T) { opt := influxql.IteratorOptions{ Interval: influxql.Interval{ Duration: 10, }, } start, end := opt.Window(4) if start != 0 { t.Errorf("expected start to be 0, got %d", start) } if end != 10 { t.Errorf("expected end to be 10, got %d", end) } }
func TestIteratorOptions_Window_Offset(t *testing.T) { opt := influxql.IteratorOptions{ Interval: influxql.Interval{ Duration: 10, Offset: 8, }, } start, end := opt.Window(14) if start != 8 { t.Errorf("expected start to be 8, got %d", start) } if end != 18 { t.Errorf("expected end to be 18, got %d", end) } }
// createVarRefIterator creates an iterator for a variable reference. func (e *Engine) createVarRefIterator(opt influxql.IteratorOptions) ([]influxql.Iterator, error) { ref, _ := opt.Expr.(*influxql.VarRef) var itrs []influxql.Iterator if err := func() error { mms := tsdb.Measurements(e.index.MeasurementsByName(influxql.Sources(opt.Sources).Names())) for _, mm := range mms { // Determine tagsets for this measurement based on dimensions and filters. tagSets, err := mm.TagSets(opt.Dimensions, opt.Condition) if err != nil { return err } // Calculate tag sets and apply SLIMIT/SOFFSET. tagSets = influxql.LimitTagSets(tagSets, opt.SLimit, opt.SOffset) for _, t := range tagSets { inputs, err := e.createTagSetIterators(ref, mm, t, opt) if err != nil { return err } if len(inputs) > 0 && (opt.Limit > 0 || opt.Offset > 0) { var itr influxql.Iterator if opt.MergeSorted() { itr = influxql.NewSortedMergeIterator(inputs, opt) } else { itr = influxql.NewMergeIterator(inputs, opt) } itrs = append(itrs, newLimitIterator(itr, opt)) } else { itrs = append(itrs, inputs...) } } } return nil }(); err != nil { influxql.Iterators(itrs).Close() return nil, err } return itrs, nil }
// CreateIterator returns an iterator for the data in the shard. func (s *Shard) CreateIterator(opt influxql.IteratorOptions) (influxql.Iterator, error) { if err := s.ready(); err != nil { return nil, err } if influxql.Sources(opt.Sources).HasSystemSource() { return s.createSystemIterator(opt) } opt.Sources = influxql.Sources(opt.Sources).Filter(s.database, s.retentionPolicy) return s.engine.CreateIterator(opt) }
func TestIteratorOptions_DerivativeInterval_Call(t *testing.T) { opt := influxql.IteratorOptions{ Expr: &influxql.Call{ Name: "mean", Args: []influxql.Expr{ &influxql.VarRef{Val: "value"}, &influxql.DurationLiteral{Val: 2 * time.Second}, }, }, Interval: influxql.Interval{ Duration: 10, Offset: 2, }, } expected := influxql.Interval{Duration: 2 * time.Second} actual := opt.DerivativeInterval() if actual != expected { t.Errorf("expected derivative interval to be %v, got %v", expected, actual) } }
// CreateIterator returns an iterator for the data in the shard. func (s *Shard) CreateIterator(opt influxql.IteratorOptions) (influxql.Iterator, error) { if s.closed() { return nil, ErrEngineClosed } if influxql.Sources(opt.Sources).HasSystemSource() { return s.createSystemIterator(opt) } opt.Sources = influxql.Sources(opt.Sources).Filter(s.database, s.retentionPolicy) return s.engine.CreateIterator(opt) }
// Ensure iterator options with a regex measurement can be marshaled. func TestIteratorOptions_MarshalBinary_Measurement_Regex(t *testing.T) { opt := &influxql.IteratorOptions{ Sources: []influxql.Source{ &influxql.Measurement{Database: "db1", RetentionPolicy: "rp2", Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`series.+`)}}, }, } // Marshal to binary. buf, err := opt.MarshalBinary() if err != nil { t.Fatal(err) } // Unmarshal back to an object. var other influxql.IteratorOptions if err := other.UnmarshalBinary(buf); err != nil { t.Fatal(err) } else if v := other.Sources[0].(*influxql.Measurement).Regex.Val.String(); v != `/series.+/` { t.Fatalf("unexpected measurement regex: %s", v) } }
// Ensure iterator options can be marshaled to and from a binary format. func TestIteratorOptions_MarshalBinary(t *testing.T) { opt := &influxql.IteratorOptions{ Expr: MustParseExpr("count(value)"), Aux: []string{"a", "b", "c"}, Sources: []influxql.Source{ &influxql.Measurement{Database: "db0", RetentionPolicy: "rp0", Name: "mm0"}, }, Interval: influxql.Interval{ Duration: 1 * time.Hour, Offset: 20 * time.Minute, }, Dimensions: []string{"region", "host"}, Fill: influxql.NumberFill, FillValue: float64(100), Condition: MustParseExpr(`foo = 'bar'`), StartTime: 1000, EndTime: 2000, Ascending: true, Limit: 100, Offset: 200, SLimit: 300, SOffset: 400, Dedupe: true, } // Marshal to binary. buf, err := opt.MarshalBinary() if err != nil { t.Fatal(err) } // Unmarshal back to an object. var other influxql.IteratorOptions if err := other.UnmarshalBinary(buf); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(&other, opt) { t.Fatalf("unexpected options: %s", spew.Sdump(other)) } }
// CreateIterator returns a single combined iterator for the shards. func (a Shards) CreateIterator(opt influxql.IteratorOptions) (influxql.Iterator, error) { if influxql.Sources(opt.Sources).HasSystemSource() { return a.createSystemIterator(opt) } // Create iterators for each shard. // Ensure that they are closed if an error occurs. itrs := make([]influxql.Iterator, 0, len(a)) if err := func() error { for _, sh := range a { itr, err := sh.CreateIterator(opt) if err != nil { return err } itrs = append(itrs, itr) } return nil }(); err != nil { influxql.Iterators(itrs).Close() return nil, err } // Merge into a single iterator. if opt.MergeSorted() { return influxql.NewSortedMergeIterator(itrs, opt), nil } itr := influxql.NewMergeIterator(itrs, opt) if opt.Expr != nil { if expr, ok := opt.Expr.(*influxql.Call); ok && expr.Name == "count" { opt.Expr = &influxql.Call{ Name: "sum", Args: expr.Args, } } } return influxql.NewCallIterator(itr, opt), nil }
func TestIteratorOptions_MergeSorted(t *testing.T) { opt := influxql.IteratorOptions{} sorted := opt.MergeSorted() if !sorted { t.Error("expected no expression to be sorted, got unsorted") } opt.Expr = &influxql.VarRef{} sorted = opt.MergeSorted() if !sorted { t.Error("expected expression with varref to be sorted, got unsorted") } opt.Expr = &influxql.Call{} sorted = opt.MergeSorted() if sorted { t.Error("expected expression without varref to be unsorted, got sorted") } }
// buildIntegerCursor creates a cursor for an integer field. func (e *Engine) buildIntegerCursor(measurement, seriesKey, field string, opt influxql.IteratorOptions) integerCursor { cacheValues := e.Cache.Values(SeriesFieldKey(seriesKey, field)) keyCursor := e.KeyCursor(SeriesFieldKey(seriesKey, field), time.Unix(0, opt.SeekTime()).UTC(), opt.Ascending) return newIntegerCursor(opt.SeekTime(), opt.Ascending, cacheValues, keyCursor) }
// buildBooleanCursor creates a cursor for a boolean field. func (e *Engine) buildBooleanCursor(measurement, seriesKey, field string, opt influxql.IteratorOptions) booleanCursor { cacheValues := e.Cache.Values(SeriesFieldKey(seriesKey, field)) keyCursor := e.KeyCursor(SeriesFieldKey(seriesKey, field), opt.SeekTime(), opt.Ascending) return newBooleanCursor(opt.SeekTime(), opt.Ascending, cacheValues, keyCursor) }
// createVarRefIterator creates an iterator for a variable reference. func (e *Engine) createVarRefIterator(opt influxql.IteratorOptions) ([]influxql.Iterator, error) { ref, _ := opt.Expr.(*influxql.VarRef) var itrs []influxql.Iterator if err := func() error { mms := tsdb.Measurements(e.index.MeasurementsByName(influxql.Sources(opt.Sources).Names())) // Retrieve the maximum number of fields (without time). conditionFields := make([]string, len(influxql.ExprNames(opt.Condition))) for _, mm := range mms { // Determine tagsets for this measurement based on dimensions and filters. tagSets, err := mm.TagSets(opt.Dimensions, opt.Condition) if err != nil { return err } // Calculate tag sets and apply SLIMIT/SOFFSET. tagSets = influxql.LimitTagSets(tagSets, opt.SLimit, opt.SOffset) for _, t := range tagSets { inputs := make([]influxql.Iterator, 0, len(t.SeriesKeys)) for i, seriesKey := range t.SeriesKeys { fields := 0 if t.Filters[i] != nil { // Retrieve non-time fields from this series filter and filter out tags. for _, f := range influxql.ExprNames(t.Filters[i]) { conditionFields[fields] = f fields++ } } input, err := e.createVarRefSeriesIterator(ref, mm, seriesKey, t, t.Filters[i], conditionFields[:fields], opt) if err != nil { return err } else if input == nil { continue } inputs = append(inputs, input) } if len(inputs) > 0 && (opt.Limit > 0 || opt.Offset > 0) { var itr influxql.Iterator if opt.MergeSorted() { itr = influxql.NewSortedMergeIterator(inputs, opt) } else { itr = influxql.NewMergeIterator(inputs, opt) } itrs = append(itrs, newLimitIterator(itr, opt)) } else { itrs = append(itrs, inputs...) } } } return nil }(); err != nil { influxql.Iterators(itrs).Close() return nil, err } return itrs, nil }