1package query_test
2
3import (
4	"testing"
5	"time"
6
7	"github.com/google/go-cmp/cmp"
8	"github.com/influxdata/influxdb/query"
9	"github.com/influxdata/influxql"
10)
11
12// Ensure that a float iterator can be created for a count() call.
13func TestCallIterator_Count_Float(t *testing.T) {
14	itr, _ := query.NewCallIterator(
15		&FloatIterator{Points: []query.FloatPoint{
16			{Name: "cpu", Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
17			{Name: "cpu", Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
18			{Name: "cpu", Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
19			{Name: "cpu", Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
20
21			{Name: "cpu", Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
22			{Name: "cpu", Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
23			{Name: "mem", Time: 23, Value: 10, Tags: ParseTags("region=us-west,host=hostB")},
24		}},
25		query.IteratorOptions{
26			Expr:       MustParseExpr(`count("value")`),
27			Dimensions: []string{"host"},
28			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
29			Ordered:    true,
30			Ascending:  true,
31		},
32	)
33
34	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
35		t.Fatalf("unexpected error: %s", err)
36	} else if diff := cmp.Diff(a, [][]query.Point{
37		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 3, Tags: ParseTags("host=hostA"), Aggregated: 3}},
38		{&query.IntegerPoint{Name: "cpu", Time: 5, Value: 1, Tags: ParseTags("host=hostA"), Aggregated: 1}},
39		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
40		{&query.IntegerPoint{Name: "cpu", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
41		{&query.IntegerPoint{Name: "mem", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
42	}); diff != "" {
43		t.Fatalf("unexpected points:\n%s", diff)
44	}
45}
46
47// Ensure that an integer iterator can be created for a count() call.
48func TestCallIterator_Count_Integer(t *testing.T) {
49	itr, _ := query.NewCallIterator(
50		&IntegerIterator{Points: []query.IntegerPoint{
51			{Name: "cpu", Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
52			{Name: "cpu", Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
53			{Name: "cpu", Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
54			{Name: "cpu", Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
55
56			{Name: "cpu", Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
57			{Name: "cpu", Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
58			{Name: "mem", Time: 23, Value: 10, Tags: ParseTags("region=us-west,host=hostB")},
59		}},
60		query.IteratorOptions{
61			Expr:       MustParseExpr(`count("value")`),
62			Dimensions: []string{"host"},
63			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
64			Ordered:    true,
65			Ascending:  true,
66		},
67	)
68
69	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
70		t.Fatalf("unexpected error: %s", err)
71	} else if diff := cmp.Diff(a, [][]query.Point{
72		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 3, Tags: ParseTags("host=hostA"), Aggregated: 3}},
73		{&query.IntegerPoint{Name: "cpu", Time: 5, Value: 1, Tags: ParseTags("host=hostA"), Aggregated: 1}},
74		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
75		{&query.IntegerPoint{Name: "cpu", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
76		{&query.IntegerPoint{Name: "mem", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
77	}); diff != "" {
78		t.Fatalf("unexpected points:\n%s", diff)
79	}
80}
81
82// Ensure that an unsigned iterator can be created for a count() call.
83func TestCallIterator_Count_Unsigned(t *testing.T) {
84	itr, _ := query.NewCallIterator(
85		&UnsignedIterator{Points: []query.UnsignedPoint{
86			{Name: "cpu", Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
87			{Name: "cpu", Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
88			{Name: "cpu", Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
89			{Name: "cpu", Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
90
91			{Name: "cpu", Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
92			{Name: "cpu", Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
93			{Name: "mem", Time: 23, Value: 10, Tags: ParseTags("region=us-west,host=hostB")},
94		}},
95		query.IteratorOptions{
96			Expr:       MustParseExpr(`count("value")`),
97			Dimensions: []string{"host"},
98			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
99			Ordered:    true,
100			Ascending:  true,
101		},
102	)
103
104	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
105		t.Fatalf("unexpected error: %s", err)
106	} else if diff := cmp.Diff(a, [][]query.Point{
107		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 3, Tags: ParseTags("host=hostA"), Aggregated: 3}},
108		{&query.IntegerPoint{Name: "cpu", Time: 5, Value: 1, Tags: ParseTags("host=hostA"), Aggregated: 1}},
109		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
110		{&query.IntegerPoint{Name: "cpu", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
111		{&query.IntegerPoint{Name: "mem", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
112	}); diff != "" {
113		t.Fatalf("unexpected points:\n%s", diff)
114	}
115}
116
117// Ensure that a string iterator can be created for a count() call.
118func TestCallIterator_Count_String(t *testing.T) {
119	itr, _ := query.NewCallIterator(
120		&StringIterator{Points: []query.StringPoint{
121			{Name: "cpu", Time: 0, Value: "d", Tags: ParseTags("region=us-east,host=hostA")},
122			{Name: "cpu", Time: 2, Value: "b", Tags: ParseTags("region=us-east,host=hostA")},
123			{Name: "cpu", Time: 1, Value: "b", Tags: ParseTags("region=us-west,host=hostA")},
124			{Name: "cpu", Time: 5, Value: "e", Tags: ParseTags("region=us-east,host=hostA")},
125
126			{Name: "cpu", Time: 1, Value: "c", Tags: ParseTags("region=us-west,host=hostB")},
127			{Name: "cpu", Time: 23, Value: "a", Tags: ParseTags("region=us-west,host=hostB")},
128			{Name: "mem", Time: 23, Value: "b", Tags: ParseTags("region=us-west,host=hostB")},
129		}},
130		query.IteratorOptions{
131			Expr:       MustParseExpr(`count("value")`),
132			Dimensions: []string{"host"},
133			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
134			Ordered:    true,
135			Ascending:  true,
136		},
137	)
138
139	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
140		t.Fatalf("unexpected error: %s", err)
141	} else if diff := cmp.Diff(a, [][]query.Point{
142		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 3, Tags: ParseTags("host=hostA"), Aggregated: 3}},
143		{&query.IntegerPoint{Name: "cpu", Time: 5, Value: 1, Tags: ParseTags("host=hostA"), Aggregated: 1}},
144		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
145		{&query.IntegerPoint{Name: "cpu", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
146		{&query.IntegerPoint{Name: "mem", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
147	}); diff != "" {
148		t.Fatalf("unexpected points:\n%s", diff)
149	}
150}
151
152// Ensure that a boolean iterator can be created for a count() call.
153func TestCallIterator_Count_Boolean(t *testing.T) {
154	itr, _ := query.NewCallIterator(
155		&BooleanIterator{Points: []query.BooleanPoint{
156			{Name: "cpu", Time: 0, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
157			{Name: "cpu", Time: 2, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
158			{Name: "cpu", Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostA")},
159			{Name: "cpu", Time: 5, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
160
161			{Name: "cpu", Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
162			{Name: "cpu", Time: 23, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
163			{Name: "mem", Time: 23, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
164		}},
165		query.IteratorOptions{
166			Expr:       MustParseExpr(`count("value")`),
167			Dimensions: []string{"host"},
168			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
169			Ordered:    true,
170			Ascending:  true,
171		},
172	)
173
174	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
175		t.Fatalf("unexpected error: %s", err)
176	} else if diff := cmp.Diff(a, [][]query.Point{
177		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 3, Tags: ParseTags("host=hostA"), Aggregated: 3}},
178		{&query.IntegerPoint{Name: "cpu", Time: 5, Value: 1, Tags: ParseTags("host=hostA"), Aggregated: 1}},
179		{&query.IntegerPoint{Name: "cpu", Time: 0, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
180		{&query.IntegerPoint{Name: "cpu", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
181		{&query.IntegerPoint{Name: "mem", Time: 20, Value: 1, Tags: ParseTags("host=hostB"), Aggregated: 1}},
182	}); diff != "" {
183		t.Fatalf("unexpected points:\n%s", diff)
184	}
185}
186
187// Ensure that a float iterator can be created for a min() call.
188func TestCallIterator_Min_Float(t *testing.T) {
189	itr, _ := query.NewCallIterator(
190		&FloatIterator{Points: []query.FloatPoint{
191			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
192			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
193			{Time: 4, Value: 12, Tags: ParseTags("region=us-east,host=hostA")},
194			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
195			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
196
197			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
198			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
199		}},
200		query.IteratorOptions{
201			Expr:       MustParseExpr(`min("value")`),
202			Dimensions: []string{"host"},
203			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
204			Ordered:    true,
205			Ascending:  true,
206		},
207	)
208
209	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
210		t.Fatalf("unexpected error: %s", err)
211	} else if diff := cmp.Diff(a, [][]query.Point{
212		{&query.FloatPoint{Time: 1, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 4}},
213		{&query.FloatPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
214		{&query.FloatPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
215		{&query.FloatPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
216	}); diff != "" {
217		t.Fatalf("unexpected points:\n%s", diff)
218	}
219}
220
221// Ensure that a integer iterator can be created for a min() call.
222func TestCallIterator_Min_Integer(t *testing.T) {
223	itr, _ := query.NewCallIterator(
224		&IntegerIterator{Points: []query.IntegerPoint{
225			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
226			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
227			{Time: 4, Value: 12, Tags: ParseTags("region=us-east,host=hostA")},
228			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
229			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
230
231			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
232			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
233		}},
234		query.IteratorOptions{
235			Expr:       MustParseExpr(`min("value")`),
236			Dimensions: []string{"host"},
237			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
238			Ordered:    true,
239			Ascending:  true,
240		},
241	)
242
243	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
244		t.Fatalf("unexpected error: %s", err)
245	} else if diff := cmp.Diff(a, [][]query.Point{
246		{&query.IntegerPoint{Time: 1, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 4}},
247		{&query.IntegerPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
248		{&query.IntegerPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
249		{&query.IntegerPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
250	}); diff != "" {
251		t.Fatalf("unexpected points:\n%s", diff)
252	}
253}
254
255// Ensure that a unsigned iterator can be created for a min() call.
256func TestCallIterator_Min_Unsigned(t *testing.T) {
257	itr, _ := query.NewCallIterator(
258		&UnsignedIterator{Points: []query.UnsignedPoint{
259			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
260			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
261			{Time: 4, Value: 12, Tags: ParseTags("region=us-east,host=hostA")},
262			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
263			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
264
265			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
266			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
267		}},
268		query.IteratorOptions{
269			Expr:       MustParseExpr(`min("value")`),
270			Dimensions: []string{"host"},
271			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
272			Ordered:    true,
273			Ascending:  true,
274		},
275	)
276
277	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
278		t.Fatalf("unexpected error: %s", err)
279	} else if diff := cmp.Diff(a, [][]query.Point{
280		{&query.UnsignedPoint{Time: 1, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 4}},
281		{&query.UnsignedPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
282		{&query.UnsignedPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
283		{&query.UnsignedPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
284	}); diff != "" {
285		t.Fatalf("unexpected points:\n%s", diff)
286	}
287}
288
289// Ensure that a boolean iterator can be created for a min() call.
290func TestCallIterator_Min_Boolean(t *testing.T) {
291	itr, _ := query.NewCallIterator(
292		&BooleanIterator{Points: []query.BooleanPoint{
293			{Time: 0, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
294			{Time: 2, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
295			{Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostA")},
296			{Time: 5, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
297
298			{Time: 1, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
299			{Time: 23, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
300		}},
301		query.IteratorOptions{
302			Expr:       MustParseExpr(`min("value")`),
303			Dimensions: []string{"host"},
304			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
305			Ordered:    true,
306			Ascending:  true,
307		},
308	)
309
310	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
311		t.Fatalf("unexpected error: %s", err)
312	} else if diff := cmp.Diff(a, [][]query.Point{
313		{&query.BooleanPoint{Time: 2, Value: false, Tags: ParseTags("host=hostA"), Aggregated: 3}},
314		{&query.BooleanPoint{Time: 5, Value: false, Tags: ParseTags("host=hostA"), Aggregated: 1}},
315		{&query.BooleanPoint{Time: 1, Value: false, Tags: ParseTags("host=hostB"), Aggregated: 1}},
316		{&query.BooleanPoint{Time: 23, Value: true, Tags: ParseTags("host=hostB"), Aggregated: 1}},
317	}); diff != "" {
318		t.Fatalf("unexpected points:\n%s", diff)
319	}
320}
321
322// Ensure that a float iterator can be created for a max() call.
323func TestCallIterator_Max_Float(t *testing.T) {
324	itr, _ := query.NewCallIterator(
325		&FloatIterator{Points: []query.FloatPoint{
326			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
327			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
328			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
329			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
330
331			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
332			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
333		}},
334		query.IteratorOptions{
335			Expr:       MustParseExpr(`max("value")`),
336			Dimensions: []string{"host"},
337			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
338			Ordered:    true,
339			Ascending:  true,
340		},
341	)
342
343	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
344		t.Fatalf("unexpected error: %s", err)
345	} else if diff := cmp.Diff(a, [][]query.Point{
346		{&query.FloatPoint{Time: 0, Value: 15, Tags: ParseTags("host=hostA"), Aggregated: 3}},
347		{&query.FloatPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
348		{&query.FloatPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
349		{&query.FloatPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
350	}); diff != "" {
351		t.Fatalf("unexpected points:\n%s", diff)
352	}
353}
354
355// Ensure that a integer iterator can be created for a max() call.
356func TestCallIterator_Max_Integer(t *testing.T) {
357	itr, _ := query.NewCallIterator(
358		&IntegerIterator{Points: []query.IntegerPoint{
359			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
360			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
361			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
362			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
363
364			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
365			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
366		}},
367		query.IteratorOptions{
368			Expr:       MustParseExpr(`max("value")`),
369			Dimensions: []string{"host"},
370			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
371			Ordered:    true,
372			Ascending:  true,
373		},
374	)
375
376	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
377		t.Fatalf("unexpected error: %s", err)
378	} else if diff := cmp.Diff(a, [][]query.Point{
379		{&query.IntegerPoint{Time: 0, Value: 15, Tags: ParseTags("host=hostA"), Aggregated: 3}},
380		{&query.IntegerPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
381		{&query.IntegerPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
382		{&query.IntegerPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
383	}); diff != "" {
384		t.Fatalf("unexpected points:\n%s", diff)
385	}
386}
387
388// Ensure that a unsigned iterator can be created for a max() call.
389func TestCallIterator_Max_Unsigned(t *testing.T) {
390	itr, _ := query.NewCallIterator(
391		&UnsignedIterator{Points: []query.UnsignedPoint{
392			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
393			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
394			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
395			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
396
397			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
398			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
399		}},
400		query.IteratorOptions{
401			Expr:       MustParseExpr(`max("value")`),
402			Dimensions: []string{"host"},
403			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
404			Ordered:    true,
405			Ascending:  true,
406		},
407	)
408
409	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
410		t.Fatalf("unexpected error: %s", err)
411	} else if diff := cmp.Diff(a, [][]query.Point{
412		{&query.UnsignedPoint{Time: 0, Value: 15, Tags: ParseTags("host=hostA"), Aggregated: 3}},
413		{&query.UnsignedPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
414		{&query.UnsignedPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
415		{&query.UnsignedPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
416	}); diff != "" {
417		t.Fatalf("unexpected points:\n%s", diff)
418	}
419}
420
421// Ensure that a boolean iterator can be created for a max() call.
422func TestCallIterator_Max_Boolean(t *testing.T) {
423	itr, _ := query.NewCallIterator(
424		&BooleanIterator{Points: []query.BooleanPoint{
425			{Time: 0, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
426			{Time: 2, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
427			{Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostA")},
428			{Time: 5, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
429
430			{Time: 1, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
431			{Time: 23, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
432		}},
433		query.IteratorOptions{
434			Expr:       MustParseExpr(`max("value")`),
435			Dimensions: []string{"host"},
436			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
437			Ordered:    true,
438			Ascending:  true,
439		},
440	)
441
442	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
443		t.Fatalf("unexpected error: %s", err)
444	} else if diff := cmp.Diff(a, [][]query.Point{
445		{&query.BooleanPoint{Time: 0, Value: true, Tags: ParseTags("host=hostA"), Aggregated: 3}},
446		{&query.BooleanPoint{Time: 5, Value: false, Tags: ParseTags("host=hostA"), Aggregated: 1}},
447		{&query.BooleanPoint{Time: 1, Value: false, Tags: ParseTags("host=hostB"), Aggregated: 1}},
448		{&query.BooleanPoint{Time: 23, Value: true, Tags: ParseTags("host=hostB"), Aggregated: 1}},
449	}); diff != "" {
450		t.Fatalf("unexpected points:\n%s", diff)
451	}
452}
453
454// Ensure that a float iterator can be created for a sum() call.
455func TestCallIterator_Sum_Float(t *testing.T) {
456	itr, _ := query.NewCallIterator(
457		&FloatIterator{Points: []query.FloatPoint{
458			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
459			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
460			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
461			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
462
463			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
464			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
465		}},
466		query.IteratorOptions{
467			Expr:       MustParseExpr(`sum("value")`),
468			Dimensions: []string{"host"},
469			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
470			Ordered:    true,
471			Ascending:  true,
472		},
473	)
474
475	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
476		t.Fatalf("unexpected error: %s", err)
477	} else if diff := cmp.Diff(a, [][]query.Point{
478		{&query.FloatPoint{Time: 0, Value: 35, Tags: ParseTags("host=hostA"), Aggregated: 3}},
479		{&query.FloatPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
480		{&query.FloatPoint{Time: 0, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
481		{&query.FloatPoint{Time: 20, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
482	}); diff != "" {
483		t.Fatalf("unexpected points:\n%s", diff)
484	}
485}
486
487// Ensure that an integer iterator can be created for a sum() call.
488func TestCallIterator_Sum_Integer(t *testing.T) {
489	itr, _ := query.NewCallIterator(
490		&IntegerIterator{Points: []query.IntegerPoint{
491			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
492			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
493			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
494			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
495
496			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
497			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
498		}},
499		query.IteratorOptions{
500			Expr:       MustParseExpr(`sum("value")`),
501			Dimensions: []string{"host"},
502			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
503			Ordered:    true,
504			Ascending:  true,
505		},
506	)
507
508	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
509		t.Fatalf("unexpected error: %s", err)
510	} else if diff := cmp.Diff(a, [][]query.Point{
511		{&query.IntegerPoint{Time: 0, Value: 35, Tags: ParseTags("host=hostA"), Aggregated: 3}},
512		{&query.IntegerPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
513		{&query.IntegerPoint{Time: 0, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
514		{&query.IntegerPoint{Time: 20, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
515	}); diff != "" {
516		t.Fatalf("unexpected points:\n%s", diff)
517	}
518}
519
520// Ensure that an unsigned iterator can be created for a sum() call.
521func TestCallIterator_Sum_Unsigned(t *testing.T) {
522	itr, _ := query.NewCallIterator(
523		&UnsignedIterator{Points: []query.UnsignedPoint{
524			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
525			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
526			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
527			{Time: 5, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
528
529			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
530			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
531		}},
532		query.IteratorOptions{
533			Expr:       MustParseExpr(`sum("value")`),
534			Dimensions: []string{"host"},
535			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
536			Ordered:    true,
537			Ascending:  true,
538		},
539	)
540
541	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
542		t.Fatalf("unexpected error: %s", err)
543	} else if diff := cmp.Diff(a, [][]query.Point{
544		{&query.UnsignedPoint{Time: 0, Value: 35, Tags: ParseTags("host=hostA"), Aggregated: 3}},
545		{&query.UnsignedPoint{Time: 5, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
546		{&query.UnsignedPoint{Time: 0, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
547		{&query.UnsignedPoint{Time: 20, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
548	}); diff != "" {
549		t.Fatalf("unexpected points:\n%s", diff)
550	}
551}
552
553// Ensure that a float iterator can be created for a first() call.
554func TestCallIterator_First_Float(t *testing.T) {
555	itr, _ := query.NewCallIterator(
556		&FloatIterator{Points: []query.FloatPoint{
557			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
558			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
559			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
560			{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
561
562			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
563			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
564		}},
565		query.IteratorOptions{
566			Expr:       MustParseExpr(`first("value")`),
567			Dimensions: []string{"host"},
568			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
569			Ordered:    true,
570			Ascending:  true,
571		},
572	)
573
574	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
575		t.Fatalf("unexpected error: %s", err)
576	} else if diff := cmp.Diff(a, [][]query.Point{
577		{&query.FloatPoint{Time: 0, Value: 15, Tags: ParseTags("host=hostA"), Aggregated: 3}},
578		{&query.FloatPoint{Time: 6, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
579		{&query.FloatPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
580		{&query.FloatPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
581	}); diff != "" {
582		t.Fatalf("unexpected points:\n%s", diff)
583	}
584}
585
586// Ensure that an integer iterator can be created for a first() call.
587func TestCallIterator_First_Integer(t *testing.T) {
588	itr, _ := query.NewCallIterator(
589		&IntegerIterator{Points: []query.IntegerPoint{
590			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
591			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
592			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
593			{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
594
595			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
596			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
597		}},
598		query.IteratorOptions{
599			Expr:       MustParseExpr(`first("value")`),
600			Dimensions: []string{"host"},
601			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
602			Ordered:    true,
603			Ascending:  true,
604		},
605	)
606
607	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
608		t.Fatalf("unexpected error: %s", err)
609	} else if diff := cmp.Diff(a, [][]query.Point{
610		{&query.IntegerPoint{Time: 0, Value: 15, Tags: ParseTags("host=hostA"), Aggregated: 3}},
611		{&query.IntegerPoint{Time: 6, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
612		{&query.IntegerPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
613		{&query.IntegerPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
614	}); diff != "" {
615		t.Fatalf("unexpected points:\n%s", diff)
616	}
617}
618
619// Ensure that an unsigned iterator can be created for a first() call.
620func TestCallIterator_First_Unsigned(t *testing.T) {
621	itr, _ := query.NewCallIterator(
622		&UnsignedIterator{Points: []query.UnsignedPoint{
623			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
624			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
625			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
626			{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
627
628			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
629			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
630		}},
631		query.IteratorOptions{
632			Expr:       MustParseExpr(`first("value")`),
633			Dimensions: []string{"host"},
634			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
635			Ordered:    true,
636			Ascending:  true,
637		},
638	)
639
640	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
641		t.Fatalf("unexpected error: %s", err)
642	} else if diff := cmp.Diff(a, [][]query.Point{
643		{&query.UnsignedPoint{Time: 0, Value: 15, Tags: ParseTags("host=hostA"), Aggregated: 3}},
644		{&query.UnsignedPoint{Time: 6, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
645		{&query.UnsignedPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
646		{&query.UnsignedPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
647	}); diff != "" {
648		t.Fatalf("unexpected points:\n%s", diff)
649	}
650}
651
652// Ensure that a string iterator can be created for a first() call.
653func TestCallIterator_First_String(t *testing.T) {
654	itr, _ := query.NewCallIterator(
655		&StringIterator{Points: []query.StringPoint{
656			{Time: 2, Value: "b", Tags: ParseTags("region=us-east,host=hostA")},
657			{Time: 0, Value: "d", Tags: ParseTags("region=us-east,host=hostA")},
658			{Time: 1, Value: "b", Tags: ParseTags("region=us-west,host=hostA")},
659			{Time: 6, Value: "e", Tags: ParseTags("region=us-east,host=hostA")},
660
661			{Time: 1, Value: "c", Tags: ParseTags("region=us-west,host=hostB")},
662			{Time: 23, Value: "a", Tags: ParseTags("region=us-west,host=hostB")},
663		}},
664		query.IteratorOptions{
665			Expr:       MustParseExpr(`first("value")`),
666			Dimensions: []string{"host"},
667			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
668			Ordered:    true,
669			Ascending:  true,
670		},
671	)
672
673	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
674		t.Fatalf("unexpected error: %s", err)
675	} else if diff := cmp.Diff(a, [][]query.Point{
676		{&query.StringPoint{Time: 0, Value: "d", Tags: ParseTags("host=hostA"), Aggregated: 3}},
677		{&query.StringPoint{Time: 6, Value: "e", Tags: ParseTags("host=hostA"), Aggregated: 1}},
678		{&query.StringPoint{Time: 1, Value: "c", Tags: ParseTags("host=hostB"), Aggregated: 1}},
679		{&query.StringPoint{Time: 23, Value: "a", Tags: ParseTags("host=hostB"), Aggregated: 1}},
680	}); diff != "" {
681		t.Fatalf("unexpected points:\n%s", diff)
682	}
683}
684
685// Ensure that a boolean iterator can be created for a first() call.
686func TestCallIterator_First_Boolean(t *testing.T) {
687	itr, _ := query.NewCallIterator(
688		&BooleanIterator{Points: []query.BooleanPoint{
689			{Time: 2, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
690			{Time: 0, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
691			{Time: 1, Value: false, Tags: ParseTags("region=us-west,host=hostA")},
692			{Time: 6, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
693
694			{Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
695			{Time: 23, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
696		}},
697		query.IteratorOptions{
698			Expr:       MustParseExpr(`first("value")`),
699			Dimensions: []string{"host"},
700			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
701			Ordered:    true,
702			Ascending:  true,
703		},
704	)
705
706	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
707		t.Fatalf("unexpected error: %s", err)
708	} else if diff := cmp.Diff(a, [][]query.Point{
709		{&query.BooleanPoint{Time: 0, Value: true, Tags: ParseTags("host=hostA"), Aggregated: 3}},
710		{&query.BooleanPoint{Time: 6, Value: false, Tags: ParseTags("host=hostA"), Aggregated: 1}},
711		{&query.BooleanPoint{Time: 1, Value: true, Tags: ParseTags("host=hostB"), Aggregated: 1}},
712		{&query.BooleanPoint{Time: 23, Value: false, Tags: ParseTags("host=hostB"), Aggregated: 1}},
713	}); diff != "" {
714		t.Fatalf("unexpected points:\n%s", diff)
715	}
716}
717
718// Ensure that a float iterator can be created for a last() call.
719func TestCallIterator_Last_Float(t *testing.T) {
720	itr, _ := query.NewCallIterator(
721		&FloatIterator{Points: []query.FloatPoint{
722			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
723			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
724			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
725			{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
726
727			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
728			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
729		}},
730		query.IteratorOptions{
731			Expr:       MustParseExpr(`last("value")`),
732			Dimensions: []string{"host"},
733			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
734			Ordered:    true,
735			Ascending:  true,
736		},
737	)
738
739	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
740		t.Fatalf("unexpected error: %s", err)
741	} else if diff := cmp.Diff(a, [][]query.Point{
742		{&query.FloatPoint{Time: 2, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 3}},
743		{&query.FloatPoint{Time: 6, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
744		{&query.FloatPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
745		{&query.FloatPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
746	}); diff != "" {
747		t.Fatalf("unexpected points:\n%s", diff)
748	}
749}
750
751// Ensure that an integer iterator can be created for a last() call.
752func TestCallIterator_Last_Integer(t *testing.T) {
753	itr, _ := query.NewCallIterator(
754		&IntegerIterator{Points: []query.IntegerPoint{
755			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
756			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
757			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
758			{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
759
760			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
761			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
762		}},
763		query.IteratorOptions{
764			Expr:       MustParseExpr(`last("value")`),
765			Dimensions: []string{"host"},
766			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
767			Ordered:    true,
768			Ascending:  true,
769		},
770	)
771
772	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
773		t.Fatalf("unexpected error: %s", err)
774	} else if diff := cmp.Diff(a, [][]query.Point{
775		{&query.IntegerPoint{Time: 2, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 3}},
776		{&query.IntegerPoint{Time: 6, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
777		{&query.IntegerPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
778		{&query.IntegerPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
779	}); diff != "" {
780		t.Fatalf("unexpected points:\n%s", diff)
781	}
782}
783
784// Ensure that an unsigned iterator can be created for a last() call.
785func TestCallIterator_Last_Unsigned(t *testing.T) {
786	itr, _ := query.NewCallIterator(
787		&UnsignedIterator{Points: []query.UnsignedPoint{
788			{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
789			{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
790			{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
791			{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
792
793			{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
794			{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
795		}},
796		query.IteratorOptions{
797			Expr:       MustParseExpr(`last("value")`),
798			Dimensions: []string{"host"},
799			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
800			Ordered:    true,
801			Ascending:  true,
802		},
803	)
804
805	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
806		t.Fatalf("unexpected error: %s", err)
807	} else if diff := cmp.Diff(a, [][]query.Point{
808		{&query.UnsignedPoint{Time: 2, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 3}},
809		{&query.UnsignedPoint{Time: 6, Value: 20, Tags: ParseTags("host=hostA"), Aggregated: 1}},
810		{&query.UnsignedPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 1}},
811		{&query.UnsignedPoint{Time: 23, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 1}},
812	}); diff != "" {
813		t.Fatalf("unexpected points:\n%s", diff)
814	}
815}
816
817// Ensure that a string iterator can be created for a last() call.
818func TestCallIterator_Last_String(t *testing.T) {
819	itr, _ := query.NewCallIterator(
820		&StringIterator{Points: []query.StringPoint{
821			{Time: 2, Value: "b", Tags: ParseTags("region=us-east,host=hostA")},
822			{Time: 0, Value: "d", Tags: ParseTags("region=us-east,host=hostA")},
823			{Time: 1, Value: "b", Tags: ParseTags("region=us-west,host=hostA")},
824			{Time: 6, Value: "e", Tags: ParseTags("region=us-east,host=hostA")},
825
826			{Time: 1, Value: "c", Tags: ParseTags("region=us-west,host=hostB")},
827			{Time: 23, Value: "a", Tags: ParseTags("region=us-west,host=hostB")},
828		}},
829		query.IteratorOptions{
830			Expr:       MustParseExpr(`last("value")`),
831			Dimensions: []string{"host"},
832			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
833			Ordered:    true,
834			Ascending:  true,
835		},
836	)
837
838	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
839		t.Fatalf("unexpected error: %s", err)
840	} else if diff := cmp.Diff(a, [][]query.Point{
841		{&query.StringPoint{Time: 2, Value: "b", Tags: ParseTags("host=hostA"), Aggregated: 3}},
842		{&query.StringPoint{Time: 6, Value: "e", Tags: ParseTags("host=hostA"), Aggregated: 1}},
843		{&query.StringPoint{Time: 1, Value: "c", Tags: ParseTags("host=hostB"), Aggregated: 1}},
844		{&query.StringPoint{Time: 23, Value: "a", Tags: ParseTags("host=hostB"), Aggregated: 1}},
845	}); diff != "" {
846		t.Fatalf("unexpected points:\n%s", diff)
847	}
848}
849
850// Ensure that a boolean iterator can be created for a last() call.
851func TestCallIterator_Last_Boolean(t *testing.T) {
852	itr, _ := query.NewCallIterator(
853		&BooleanIterator{Points: []query.BooleanPoint{
854			{Time: 2, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
855			{Time: 0, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
856			{Time: 1, Value: false, Tags: ParseTags("region=us-west,host=hostA")},
857			{Time: 6, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
858
859			{Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
860			{Time: 23, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
861		}},
862		query.IteratorOptions{
863			Expr:       MustParseExpr(`last("value")`),
864			Dimensions: []string{"host"},
865			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
866			Ordered:    true,
867			Ascending:  true,
868		},
869	)
870
871	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
872		t.Fatalf("unexpected error: %s", err)
873	} else if diff := cmp.Diff(a, [][]query.Point{
874		{&query.BooleanPoint{Time: 2, Value: false, Tags: ParseTags("host=hostA"), Aggregated: 3}},
875		{&query.BooleanPoint{Time: 6, Value: false, Tags: ParseTags("host=hostA"), Aggregated: 1}},
876		{&query.BooleanPoint{Time: 1, Value: true, Tags: ParseTags("host=hostB"), Aggregated: 1}},
877		{&query.BooleanPoint{Time: 23, Value: false, Tags: ParseTags("host=hostB"), Aggregated: 1}},
878	}); diff != "" {
879		t.Fatalf("unexpected points:\n%s", diff)
880	}
881}
882
883// Ensure that a float iterator can be created for a mode() call.
884func TestCallIterator_Mode_Float(t *testing.T) {
885	itr, _ := query.NewModeIterator(&FloatIterator{Points: []query.FloatPoint{
886		{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
887		{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
888		{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
889		{Time: 3, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
890		{Time: 4, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
891		{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
892		{Time: 7, Value: 21, Tags: ParseTags("region=us-east,host=hostA")},
893		{Time: 8, Value: 21, Tags: ParseTags("region=us-east,host=hostA")},
894
895		{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
896		{Time: 22, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
897		{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
898		{Time: 24, Value: 25, Tags: ParseTags("region=us-west,host=hostB")},
899	}},
900		query.IteratorOptions{
901			Expr:       MustParseExpr(`mode("value")`),
902			Dimensions: []string{"host"},
903			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
904			Ordered:    true,
905			Ascending:  true,
906		},
907	)
908
909	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
910		t.Fatalf("unexpected error: %s", err)
911	} else if diff := cmp.Diff(a, [][]query.Point{
912		{&query.FloatPoint{Time: 0, Value: 10, Tags: ParseTags("host=hostA"), Aggregated: 0}},
913		{&query.FloatPoint{Time: 5, Value: 21, Tags: ParseTags("host=hostA"), Aggregated: 0}},
914		{&query.FloatPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB"), Aggregated: 0}},
915		{&query.FloatPoint{Time: 20, Value: 8, Tags: ParseTags("host=hostB"), Aggregated: 0}},
916	}); diff != "" {
917		t.Fatalf("unexpected points:\n%s", diff)
918	}
919}
920
921// Ensure that a integer iterator can be created for a mode() call.
922func TestCallIterator_Mode_Integer(t *testing.T) {
923	itr, _ := query.NewModeIterator(&IntegerIterator{Points: []query.IntegerPoint{
924		{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
925		{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
926		{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
927		{Time: 3, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
928		{Time: 4, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
929		{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
930		{Time: 7, Value: 21, Tags: ParseTags("region=us-east,host=hostA")},
931		{Time: 8, Value: 21, Tags: ParseTags("region=us-east,host=hostA")},
932		{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
933		{Time: 22, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
934		{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
935		{Time: 24, Value: 25, Tags: ParseTags("region=us-west,host=hostB")},
936	}},
937		query.IteratorOptions{
938			Expr:       MustParseExpr(`mode("value")`),
939			Dimensions: []string{"host"},
940			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
941			Ordered:    true,
942			Ascending:  true,
943		},
944	)
945
946	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
947		t.Fatalf("unexpected error: %s", err)
948	} else if diff := cmp.Diff(a, [][]query.Point{
949		{&query.IntegerPoint{Time: 0, Value: 10, Tags: ParseTags("host=hostA")}},
950		{&query.IntegerPoint{Time: 5, Value: 21, Tags: ParseTags("host=hostA")}},
951		{&query.IntegerPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB")}},
952		{&query.IntegerPoint{Time: 20, Value: 8, Tags: ParseTags("host=hostB")}},
953	}); diff != "" {
954		t.Fatalf("unexpected points:\n%s", diff)
955	}
956}
957
958// Ensure that a unsigned iterator can be created for a mode() call.
959func TestCallIterator_Mode_Unsigned(t *testing.T) {
960	itr, _ := query.NewModeIterator(&UnsignedIterator{Points: []query.UnsignedPoint{
961		{Time: 0, Value: 15, Tags: ParseTags("region=us-east,host=hostA")},
962		{Time: 1, Value: 10, Tags: ParseTags("region=us-west,host=hostA")},
963		{Time: 2, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
964		{Time: 3, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
965		{Time: 4, Value: 10, Tags: ParseTags("region=us-east,host=hostA")},
966		{Time: 6, Value: 20, Tags: ParseTags("region=us-east,host=hostA")},
967		{Time: 7, Value: 21, Tags: ParseTags("region=us-east,host=hostA")},
968		{Time: 8, Value: 21, Tags: ParseTags("region=us-east,host=hostA")},
969		{Time: 1, Value: 11, Tags: ParseTags("region=us-west,host=hostB")},
970		{Time: 22, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
971		{Time: 23, Value: 8, Tags: ParseTags("region=us-west,host=hostB")},
972		{Time: 24, Value: 25, Tags: ParseTags("region=us-west,host=hostB")},
973	}},
974		query.IteratorOptions{
975			Expr:       MustParseExpr(`mode("value")`),
976			Dimensions: []string{"host"},
977			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
978			Ordered:    true,
979			Ascending:  true,
980		},
981	)
982
983	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
984		t.Fatalf("unexpected error: %s", err)
985	} else if diff := cmp.Diff(a, [][]query.Point{
986		{&query.UnsignedPoint{Time: 0, Value: 10, Tags: ParseTags("host=hostA")}},
987		{&query.UnsignedPoint{Time: 5, Value: 21, Tags: ParseTags("host=hostA")}},
988		{&query.UnsignedPoint{Time: 1, Value: 11, Tags: ParseTags("host=hostB")}},
989		{&query.UnsignedPoint{Time: 20, Value: 8, Tags: ParseTags("host=hostB")}},
990	}); diff != "" {
991		t.Fatalf("unexpected points:\n%s", diff)
992	}
993}
994
995// Ensure that a string iterator can be created for a mode() call.
996func TestCallIterator_Mode_String(t *testing.T) {
997	itr, _ := query.NewModeIterator(&StringIterator{Points: []query.StringPoint{
998		{Time: 0, Value: "15", Tags: ParseTags("region=us-east,host=hostA")},
999		{Time: 1, Value: "10", Tags: ParseTags("region=us-west,host=hostA")},
1000		{Time: 2, Value: "10", Tags: ParseTags("region=us-east,host=hostA")},
1001		{Time: 3, Value: "10", Tags: ParseTags("region=us-east,host=hostA")},
1002		{Time: 4, Value: "10", Tags: ParseTags("region=us-east,host=hostA")},
1003		{Time: 6, Value: "20", Tags: ParseTags("region=us-east,host=hostA")},
1004		{Time: 7, Value: "21", Tags: ParseTags("region=us-east,host=hostA")},
1005		{Time: 7, Value: "21", Tags: ParseTags("region=us-east,host=hostA")},
1006
1007		{Time: 1, Value: "11", Tags: ParseTags("region=us-west,host=hostB")},
1008		{Time: 22, Value: "8", Tags: ParseTags("region=us-west,host=hostB")},
1009		{Time: 23, Value: "8", Tags: ParseTags("region=us-west,host=hostB")},
1010		{Time: 24, Value: "25", Tags: ParseTags("region=us-west,host=hostB")},
1011	}},
1012		query.IteratorOptions{
1013			Expr:       MustParseExpr(`mode("value")`),
1014			Dimensions: []string{"host"},
1015			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
1016			Ordered:    true,
1017			Ascending:  true,
1018		},
1019	)
1020
1021	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
1022		t.Fatalf("unexpected error: %s", err)
1023	} else if diff := cmp.Diff(a, [][]query.Point{
1024		{&query.StringPoint{Time: 0, Value: "10", Tags: ParseTags("host=hostA")}},
1025		{&query.StringPoint{Time: 5, Value: "21", Tags: ParseTags("host=hostA")}},
1026		{&query.StringPoint{Time: 1, Value: "11", Tags: ParseTags("host=hostB")}},
1027		{&query.StringPoint{Time: 20, Value: "8", Tags: ParseTags("host=hostB")}},
1028	}); diff != "" {
1029		t.Fatalf("unexpected points:\n%s", diff)
1030	}
1031}
1032
1033// Ensure that a boolean iterator can be created for a modBooleanl.
1034func TestCallIterator_Mode_Boolean(t *testing.T) {
1035	itr, _ := query.NewModeIterator(&BooleanIterator{Points: []query.BooleanPoint{
1036		{Time: 0, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
1037		{Time: 1, Value: true, Tags: ParseTags("region=us-west,host=hostA")},
1038		{Time: 2, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
1039		{Time: 3, Value: true, Tags: ParseTags("region=us-east,host=hostA")},
1040		{Time: 4, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
1041		{Time: 6, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
1042		{Time: 7, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
1043		{Time: 8, Value: false, Tags: ParseTags("region=us-east,host=hostA")},
1044
1045		{Time: 1, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
1046		{Time: 22, Value: false, Tags: ParseTags("region=us-west,host=hostB")},
1047		{Time: 23, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
1048		{Time: 24, Value: true, Tags: ParseTags("region=us-west,host=hostB")},
1049	}},
1050		query.IteratorOptions{
1051			Expr:       MustParseExpr(`mode("value")`),
1052			Dimensions: []string{"host"},
1053			Interval:   query.Interval{Duration: 5 * time.Nanosecond},
1054			Ordered:    true,
1055			Ascending:  true,
1056		},
1057	)
1058
1059	if a, err := Iterators([]query.Iterator{itr}).ReadAll(); err != nil {
1060		t.Fatalf("unexpected error: %s", err)
1061	} else if diff := cmp.Diff(a, [][]query.Point{
1062		{&query.BooleanPoint{Time: 0, Value: true, Tags: ParseTags("host=hostA")}},
1063		{&query.BooleanPoint{Time: 5, Value: false, Tags: ParseTags("host=hostA")}},
1064		{&query.BooleanPoint{Time: 1, Value: false, Tags: ParseTags("host=hostB")}},
1065		{&query.BooleanPoint{Time: 20, Value: true, Tags: ParseTags("host=hostB")}},
1066	}); diff != "" {
1067		t.Fatalf("unexpected points:\n%s", diff)
1068	}
1069}
1070
1071func TestNewCallIterator_UnsupportedExprName(t *testing.T) {
1072	_, err := query.NewCallIterator(
1073		&FloatIterator{},
1074		query.IteratorOptions{
1075			Expr: MustParseExpr(`foobar("value")`),
1076		},
1077	)
1078
1079	if err == nil || err.Error() != "unsupported function call: foobar" {
1080		t.Errorf("unexpected error: %s", err)
1081	}
1082}
1083
1084func BenchmarkCountIterator_1K(b *testing.B)   { benchmarkCountIterator(b, 1000) }
1085func BenchmarkCountIterator_100K(b *testing.B) { benchmarkCountIterator(b, 100000) }
1086func BenchmarkCountIterator_1M(b *testing.B)   { benchmarkCountIterator(b, 1000000) }
1087
1088func benchmarkCountIterator(b *testing.B, pointN int) {
1089	benchmarkCallIterator(b, query.IteratorOptions{
1090		Expr:      MustParseExpr("count(value)"),
1091		StartTime: influxql.MinTime,
1092		EndTime:   influxql.MaxTime,
1093	}, pointN)
1094}
1095
1096func benchmarkCallIterator(b *testing.B, opt query.IteratorOptions, pointN int) {
1097	b.ReportAllocs()
1098
1099	for i := 0; i < b.N; i++ {
1100		// Create a lightweight point generator.
1101		p := query.FloatPoint{Name: "cpu", Value: 100}
1102		input := FloatPointGenerator{
1103			N:  pointN,
1104			Fn: func(i int) *query.FloatPoint { return &p },
1105		}
1106
1107		// Execute call against input.
1108		itr, err := query.NewCallIterator(&input, opt)
1109		if err != nil {
1110			b.Fatal(err)
1111		}
1112		query.DrainIterator(itr)
1113	}
1114}
1115
1116func BenchmarkSampleIterator_1k(b *testing.B)   { benchmarkSampleIterator(b, 1000) }
1117func BenchmarkSampleIterator_100k(b *testing.B) { benchmarkSampleIterator(b, 100000) }
1118func BenchmarkSampleIterator_1M(b *testing.B)   { benchmarkSampleIterator(b, 1000000) }
1119
1120func benchmarkSampleIterator(b *testing.B, pointN int) {
1121	b.ReportAllocs()
1122
1123	// Create a lightweight point generator.
1124	p := query.FloatPoint{Name: "cpu"}
1125	input := FloatPointGenerator{
1126		N: pointN,
1127		Fn: func(i int) *query.FloatPoint {
1128			p.Value = float64(i)
1129			return &p
1130		},
1131	}
1132
1133	for i := 0; i < b.N; i++ {
1134		// Execute call against input.
1135		itr, err := query.NewSampleIterator(&input, query.IteratorOptions{}, 100)
1136		if err != nil {
1137			b.Fatal(err)
1138		}
1139		query.DrainIterator(itr)
1140	}
1141}
1142
1143func BenchmarkDistinctIterator_1K(b *testing.B)   { benchmarkDistinctIterator(b, 1000) }
1144func BenchmarkDistinctIterator_100K(b *testing.B) { benchmarkDistinctIterator(b, 100000) }
1145func BenchmarkDistinctIterator_1M(b *testing.B)   { benchmarkDistinctIterator(b, 1000000) }
1146
1147func benchmarkDistinctIterator(b *testing.B, pointN int) {
1148	b.ReportAllocs()
1149
1150	for i := 0; i < b.N; i++ {
1151		// Create a lightweight point generator.
1152		p := query.FloatPoint{Name: "cpu"}
1153		input := FloatPointGenerator{
1154			N: pointN,
1155			Fn: func(i int) *query.FloatPoint {
1156				p.Value = float64(i % 10)
1157				return &p
1158			},
1159		}
1160
1161		// Execute call against input.
1162		itr, err := query.NewDistinctIterator(&input, query.IteratorOptions{})
1163		if err != nil {
1164			b.Fatal(err)
1165		}
1166		query.DrainIterator(itr)
1167	}
1168}
1169
1170func BenchmarkModeIterator_1K(b *testing.B)   { benchmarkModeIterator(b, 1000) }
1171func BenchmarkModeIterator_100K(b *testing.B) { benchmarkModeIterator(b, 100000) }
1172func BenchmarkModeIterator_1M(b *testing.B)   { benchmarkModeIterator(b, 1000000) }
1173
1174func benchmarkModeIterator(b *testing.B, pointN int) {
1175	b.ReportAllocs()
1176
1177	for i := 0; i < b.N; i++ {
1178		// Create a lightweight point generator.
1179		p := query.FloatPoint{Name: "cpu"}
1180		input := FloatPointGenerator{
1181			N: pointN,
1182			Fn: func(i int) *query.FloatPoint {
1183				p.Value = float64(10)
1184				return &p
1185			},
1186		}
1187
1188		// Execute call against input.
1189		itr, err := query.NewModeIterator(&input, query.IteratorOptions{})
1190		if err != nil {
1191			b.Fatal(err)
1192		}
1193		query.DrainIterator(itr)
1194	}
1195}
1196
1197type FloatPointGenerator struct {
1198	i  int
1199	N  int
1200	Fn func(i int) *query.FloatPoint
1201}
1202
1203func (g *FloatPointGenerator) Close() error               { return nil }
1204func (g *FloatPointGenerator) Stats() query.IteratorStats { return query.IteratorStats{} }
1205
1206func (g *FloatPointGenerator) Next() (*query.FloatPoint, error) {
1207	if g.i == g.N {
1208		return nil, nil
1209	}
1210	p := g.Fn(g.i)
1211	g.i++
1212	return p, nil
1213}
1214