github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/subquery_test.go (about)

     1  package query_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/influxdata/influxdb/v2/influxql/query"
    10  	"github.com/influxdata/influxql"
    11  )
    12  
    13  type CreateIteratorFn func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator
    14  
    15  func TestSubquery(t *testing.T) {
    16  	for _, test := range []struct {
    17  		Name        string
    18  		Statement   string
    19  		Fields      map[string]influxql.DataType
    20  		MapShardsFn func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn
    21  		Rows        []query.Row
    22  	}{
    23  		{
    24  			Name:      "AuxiliaryFields",
    25  			Statement: `SELECT max / 2.0 FROM (SELECT max(value) FROM cpu GROUP BY time(5s)) WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:00:15Z'`,
    26  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
    27  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
    28  				if got, want := tr.MinTimeNano(), 0*Second; got != want {
    29  					t.Errorf("unexpected min time: got=%d want=%d", got, want)
    30  				}
    31  				if got, want := tr.MaxTimeNano(), 15*Second-1; got != want {
    32  					t.Errorf("unexpected max time: got=%d want=%d", got, want)
    33  				}
    34  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
    35  					if got, want := m.Name, "cpu"; got != want {
    36  						t.Errorf("unexpected source: got=%s want=%s", got, want)
    37  					}
    38  					if got, want := opt.Expr.String(), "max(value::float)"; got != want {
    39  						t.Errorf("unexpected expression: got=%s want=%s", got, want)
    40  					}
    41  					return &FloatIterator{Points: []query.FloatPoint{
    42  						{Name: "cpu", Time: 0 * Second, Value: 5},
    43  						{Name: "cpu", Time: 5 * Second, Value: 3},
    44  						{Name: "cpu", Time: 10 * Second, Value: 8},
    45  					}}
    46  				}
    47  			},
    48  			Rows: []query.Row{
    49  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{2.5}},
    50  				{Time: 5 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{1.5}},
    51  				{Time: 10 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{float64(4)}},
    52  			},
    53  		},
    54  		{
    55  			Name:      "AuxiliaryFields_WithWhereClause",
    56  			Statement: `SELECT host FROM (SELECT max(value), host FROM cpu GROUP BY time(5s)) WHERE max > 4 AND time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:00:15Z'`,
    57  			Fields: map[string]influxql.DataType{
    58  				"value": influxql.Float,
    59  				"host":  influxql.Tag,
    60  			},
    61  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
    62  				if got, want := tr.MinTimeNano(), 0*Second; got != want {
    63  					t.Errorf("unexpected min time: got=%d want=%d", got, want)
    64  				}
    65  				if got, want := tr.MaxTimeNano(), 15*Second-1; got != want {
    66  					t.Errorf("unexpected max time: got=%d want=%d", got, want)
    67  				}
    68  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
    69  					if got, want := m.Name, "cpu"; got != want {
    70  						t.Errorf("unexpected source: got=%s want=%s", got, want)
    71  					}
    72  					if got, want := opt.Expr.String(), "max(value::float)"; got != want {
    73  						t.Errorf("unexpected expression: got=%s want=%s", got, want)
    74  					}
    75  					if got, want := opt.Aux, []influxql.VarRef{{Val: "host", Type: influxql.Tag}}; !cmp.Equal(got, want) {
    76  						t.Errorf("unexpected auxiliary fields:\n%s", cmp.Diff(want, got))
    77  					}
    78  					return &FloatIterator{Points: []query.FloatPoint{
    79  						{Name: "cpu", Time: 0 * Second, Value: 5, Aux: []interface{}{"server02"}},
    80  						{Name: "cpu", Time: 5 * Second, Value: 3, Aux: []interface{}{"server01"}},
    81  						{Name: "cpu", Time: 10 * Second, Value: 8, Aux: []interface{}{"server03"}},
    82  					}}
    83  				}
    84  			},
    85  			Rows: []query.Row{
    86  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{"server02"}},
    87  				{Time: 10 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{"server03"}},
    88  			},
    89  		},
    90  		{
    91  			Name:      "AuxiliaryFields_NonExistentField",
    92  			Statement: `SELECT host FROM (SELECT max(value) FROM cpu GROUP BY time(5s)) WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:00:15Z'`,
    93  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
    94  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
    95  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
    96  					return &FloatIterator{Points: []query.FloatPoint{
    97  						{Name: "cpu", Time: 0 * Second, Value: 5},
    98  						{Name: "cpu", Time: 5 * Second, Value: 3},
    99  						{Name: "cpu", Time: 10 * Second, Value: 8},
   100  					}}
   101  				}
   102  			},
   103  			Rows: []query.Row(nil),
   104  		},
   105  		{
   106  			Name:      "AggregateOfMath",
   107  			Statement: `SELECT mean(percentage) FROM (SELECT value * 100.0 AS percentage FROM cpu) WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:00:15Z' GROUP BY time(5s)`,
   108  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
   109  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   110  				if got, want := tr.MinTimeNano(), 0*Second; got != want {
   111  					t.Errorf("unexpected min time: got=%d want=%d", got, want)
   112  				}
   113  				if got, want := tr.MaxTimeNano(), 15*Second-1; got != want {
   114  					t.Errorf("unexpected max time: got=%d want=%d", got, want)
   115  				}
   116  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   117  					if got, want := m.Name, "cpu"; got != want {
   118  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   119  					}
   120  					if got, want := opt.Expr, influxql.Expr(nil); got != want {
   121  						t.Errorf("unexpected expression: got=%s want=%s", got, want)
   122  					}
   123  					if got, want := opt.Aux, []influxql.VarRef{{Val: "value", Type: influxql.Float}}; !cmp.Equal(got, want) {
   124  						t.Errorf("unexpected auxiliary fields:\n%s", cmp.Diff(want, got))
   125  					}
   126  					return &FloatIterator{Points: []query.FloatPoint{
   127  						{Name: "cpu", Time: 0 * Second, Aux: []interface{}{0.5}},
   128  						{Name: "cpu", Time: 2 * Second, Aux: []interface{}{1.0}},
   129  						{Name: "cpu", Time: 5 * Second, Aux: []interface{}{0.05}},
   130  						{Name: "cpu", Time: 8 * Second, Aux: []interface{}{0.45}},
   131  						{Name: "cpu", Time: 12 * Second, Aux: []interface{}{0.34}},
   132  					}}
   133  				}
   134  			},
   135  			Rows: []query.Row{
   136  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{float64(75)}},
   137  				{Time: 5 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{float64(25)}},
   138  				{Time: 10 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{float64(34)}},
   139  			},
   140  		},
   141  		{
   142  			Name:      "Cast",
   143  			Statement: `SELECT value::integer FROM (SELECT mean(value) AS value FROM cpu)`,
   144  			Fields:    map[string]influxql.DataType{"value": influxql.Integer},
   145  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   146  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   147  					if got, want := m.Name, "cpu"; got != want {
   148  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   149  					}
   150  					if got, want := opt.Expr.String(), "mean(value::integer)"; got != want {
   151  						t.Errorf("unexpected expression: got=%s want=%s", got, want)
   152  					}
   153  					return &FloatIterator{Points: []query.FloatPoint{
   154  						{Name: "cpu", Time: 0 * Second, Value: float64(20) / float64(6)},
   155  					}}
   156  				}
   157  			},
   158  			Rows: []query.Row{
   159  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{int64(3)}},
   160  			},
   161  		},
   162  		{
   163  			Name:      "CountTag",
   164  			Statement: `SELECT count(host) FROM (SELECT value, host FROM cpu) WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:00:15Z'`,
   165  			Fields: map[string]influxql.DataType{
   166  				"value": influxql.Float,
   167  				"host":  influxql.Tag,
   168  			},
   169  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   170  				if got, want := tr.MinTimeNano(), 0*Second; got != want {
   171  					t.Errorf("unexpected min time: got=%d want=%d", got, want)
   172  				}
   173  				if got, want := tr.MaxTimeNano(), 15*Second-1; got != want {
   174  					t.Errorf("unexpected max time: got=%d want=%d", got, want)
   175  				}
   176  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   177  					if got, want := m.Name, "cpu"; got != want {
   178  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   179  					}
   180  					if got, want := opt.Aux, []influxql.VarRef{
   181  						{Val: "host", Type: influxql.Tag},
   182  						{Val: "value", Type: influxql.Float},
   183  					}; !cmp.Equal(got, want) {
   184  						t.Errorf("unexpected auxiliary fields:\n%s", cmp.Diff(want, got))
   185  					}
   186  					return &FloatIterator{Points: []query.FloatPoint{
   187  						{Name: "cpu", Aux: []interface{}{"server01", 5.0}},
   188  						{Name: "cpu", Aux: []interface{}{"server02", 3.0}},
   189  						{Name: "cpu", Aux: []interface{}{"server03", 8.0}},
   190  					}}
   191  				}
   192  			},
   193  			Rows: []query.Row{
   194  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{int64(3)}},
   195  			},
   196  		},
   197  		{
   198  			Name:      "StripTags",
   199  			Statement: `SELECT max FROM (SELECT max(value) FROM cpu GROUP BY host) WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:00:15Z'`,
   200  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
   201  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   202  				if got, want := tr.MinTimeNano(), 0*Second; got != want {
   203  					t.Errorf("unexpected min time: got=%d want=%d", got, want)
   204  				}
   205  				if got, want := tr.MaxTimeNano(), 15*Second-1; got != want {
   206  					t.Errorf("unexpected max time: got=%d want=%d", got, want)
   207  				}
   208  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   209  					if got, want := m.Name, "cpu"; got != want {
   210  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   211  					}
   212  					if got, want := opt.Expr.String(), "max(value::float)"; got != want {
   213  						t.Errorf("unexpected expression: got=%s want=%s", got, want)
   214  					}
   215  					return &FloatIterator{Points: []query.FloatPoint{
   216  						{Name: "cpu", Tags: ParseTags("host=server01"), Value: 5},
   217  						{Name: "cpu", Tags: ParseTags("host=server02"), Value: 3},
   218  						{Name: "cpu", Tags: ParseTags("host=server03"), Value: 8},
   219  					}}
   220  				}
   221  			},
   222  			Rows: []query.Row{
   223  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{5.0}},
   224  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{3.0}},
   225  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{8.0}},
   226  			},
   227  		},
   228  		{
   229  			Name: "DifferentDimensionsWithSelectors",
   230  			Statement: `SELECT sum("max_min") FROM (
   231  							SELECT max("value") - min("value") FROM cpu GROUP BY time(30s), host
   232  						) WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-01T00:01:00Z' GROUP BY time(30s)`,
   233  			Fields: map[string]influxql.DataType{"value": influxql.Float},
   234  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   235  				if got, want := tr.MinTimeNano(), 0*Second; got != want {
   236  					t.Errorf("unexpected min time: got=%d want=%d", got, want)
   237  				}
   238  				if got, want := tr.MaxTimeNano(), 60*Second-1; got != want {
   239  					t.Errorf("unexpected max time: got=%d want=%d", got, want)
   240  				}
   241  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   242  					if got, want := m.Name, "cpu"; got != want {
   243  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   244  					}
   245  
   246  					var itr query.Iterator = &FloatIterator{Points: []query.FloatPoint{
   247  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 2},
   248  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: 7},
   249  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 20 * Second, Value: 3},
   250  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: 8},
   251  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 10 * Second, Value: 3},
   252  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 20 * Second, Value: 7},
   253  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 30 * Second, Value: 2},
   254  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 40 * Second, Value: 1},
   255  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 50 * Second, Value: 9},
   256  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 30 * Second, Value: 2},
   257  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 40 * Second, Value: 2},
   258  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 50 * Second, Value: 2},
   259  					}}
   260  					if _, ok := opt.Expr.(*influxql.Call); ok {
   261  						i, err := query.NewCallIterator(itr, opt)
   262  						if err != nil {
   263  							panic(err)
   264  						}
   265  						itr = i
   266  					}
   267  					return itr
   268  				}
   269  			},
   270  			Rows: []query.Row{
   271  				{Time: 0 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{float64(10)}},
   272  				{Time: 30 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{float64(8)}},
   273  			},
   274  		},
   275  		{
   276  			Name:      "TimeOrderingInTheOuterQuery",
   277  			Statement: `select * from (select last(value) from cpu group by host) order by time asc`,
   278  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
   279  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   280  
   281  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   282  					if got, want := m.Name, "cpu"; got != want {
   283  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   284  					}
   285  
   286  					var itr query.Iterator = &FloatIterator{Points: []query.FloatPoint{
   287  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 2},
   288  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: 7},
   289  						{Name: "cpu", Tags: ParseTags("host=A"), Time: 20 * Second, Value: 3},
   290  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: 8},
   291  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 10 * Second, Value: 3},
   292  						{Name: "cpu", Tags: ParseTags("host=B"), Time: 19 * Second, Value: 7},
   293  					}}
   294  					if _, ok := opt.Expr.(*influxql.Call); ok {
   295  						i, err := query.NewCallIterator(itr, opt)
   296  						if err != nil {
   297  							panic(err)
   298  						}
   299  						itr = i
   300  					}
   301  					return itr
   302  				}
   303  			},
   304  			Rows: []query.Row{
   305  				{Time: 19 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{"B", float64(7)}},
   306  				{Time: 20 * Second, Series: query.Series{Name: "cpu"}, Values: []interface{}{"A", float64(3)}},
   307  			},
   308  		},
   309  		{
   310  			Name:      "TimeZone",
   311  			Statement: `SELECT * FROM (SELECT * FROM cpu WHERE time >= '2019-04-17 09:00:00' and time < '2019-04-17 10:00:00' TZ('America/Chicago'))`,
   312  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
   313  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   314  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   315  					if got, want := time.Unix(0, opt.StartTime).UTC(), mustParseTime("2019-04-17T14:00:00Z"); !got.Equal(want) {
   316  						t.Errorf("unexpected min time: got=%q want=%q", got, want)
   317  					}
   318  					if got, want := time.Unix(0, opt.EndTime).UTC(), mustParseTime("2019-04-17T15:00:00Z").Add(-1); !got.Equal(want) {
   319  						t.Errorf("unexpected max time: got=%q want=%q", got, want)
   320  					}
   321  					return &FloatIterator{}
   322  				}
   323  			},
   324  		},
   325  		{
   326  			Name:      "DifferentDimensionsOrderByDesc",
   327  			Statement: `SELECT value, mytag FROM (SELECT last(value) AS value FROM testing GROUP BY mytag) ORDER BY desc`,
   328  			Fields:    map[string]influxql.DataType{"value": influxql.Float},
   329  			MapShardsFn: func(t *testing.T, tr influxql.TimeRange) CreateIteratorFn {
   330  				return func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) query.Iterator {
   331  					if got, want := m.Name, "testing"; got != want {
   332  						t.Errorf("unexpected source: got=%s want=%s", got, want)
   333  					}
   334  
   335  					if opt.Ascending {
   336  						t.Error("expected iterator to be descending, not ascending")
   337  					}
   338  
   339  					var itr query.Iterator = &FloatIterator{Points: []query.FloatPoint{
   340  						{Name: "testing", Tags: ParseTags("mytag=c"), Time: mustParseTime("2019-06-25T22:36:20.93605779Z").UnixNano(), Value: 2},
   341  						{Name: "testing", Tags: ParseTags("mytag=c"), Time: mustParseTime("2019-06-25T22:36:20.671604877Z").UnixNano(), Value: 2},
   342  						{Name: "testing", Tags: ParseTags("mytag=c"), Time: mustParseTime("2019-06-25T22:36:20.255794481Z").UnixNano(), Value: 2},
   343  						{Name: "testing", Tags: ParseTags("mytag=b"), Time: mustParseTime("2019-06-25T22:36:18.176662543Z").UnixNano(), Value: 2},
   344  						{Name: "testing", Tags: ParseTags("mytag=b"), Time: mustParseTime("2019-06-25T22:36:17.815979113Z").UnixNano(), Value: 2},
   345  						{Name: "testing", Tags: ParseTags("mytag=b"), Time: mustParseTime("2019-06-25T22:36:17.265031598Z").UnixNano(), Value: 2},
   346  						{Name: "testing", Tags: ParseTags("mytag=a"), Time: mustParseTime("2019-06-25T22:36:15.144253616Z").UnixNano(), Value: 2},
   347  						{Name: "testing", Tags: ParseTags("mytag=a"), Time: mustParseTime("2019-06-25T22:36:14.719167205Z").UnixNano(), Value: 2},
   348  						{Name: "testing", Tags: ParseTags("mytag=a"), Time: mustParseTime("2019-06-25T22:36:13.711721316Z").UnixNano(), Value: 2},
   349  					}}
   350  					if _, ok := opt.Expr.(*influxql.Call); ok {
   351  						i, err := query.NewCallIterator(itr, opt)
   352  						if err != nil {
   353  							panic(err)
   354  						}
   355  						itr = i
   356  					}
   357  					return itr
   358  				}
   359  			},
   360  			Rows: []query.Row{
   361  				{Time: mustParseTime("2019-06-25T22:36:20.93605779Z").UnixNano(), Series: query.Series{Name: "testing"}, Values: []interface{}{float64(2), "c"}},
   362  				{Time: mustParseTime("2019-06-25T22:36:18.176662543Z").UnixNano(), Series: query.Series{Name: "testing"}, Values: []interface{}{float64(2), "b"}},
   363  				{Time: mustParseTime("2019-06-25T22:36:15.144253616Z").UnixNano(), Series: query.Series{Name: "testing"}, Values: []interface{}{float64(2), "a"}},
   364  			},
   365  		},
   366  	} {
   367  		t.Run(test.Name, func(t *testing.T) {
   368  			shardMapper := ShardMapper{
   369  				MapShardsFn: func(_ context.Context, sources influxql.Sources, tr influxql.TimeRange) query.ShardGroup {
   370  					fn := test.MapShardsFn(t, tr)
   371  					return &ShardGroup{
   372  						Fields: test.Fields,
   373  						CreateIteratorFn: func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) (query.Iterator, error) {
   374  							return fn(ctx, m, opt), nil
   375  						},
   376  					}
   377  				},
   378  			}
   379  
   380  			stmt := MustParseSelectStatement(test.Statement)
   381  			stmt.OmitTime = true
   382  			cur, err := query.Select(context.Background(), stmt, &shardMapper, query.SelectOptions{})
   383  			if err != nil {
   384  				t.Fatalf("unexpected parse error: %s", err)
   385  			} else if a, err := ReadCursor(cur); err != nil {
   386  				t.Fatalf("unexpected error: %s", err)
   387  			} else if diff := cmp.Diff(test.Rows, a); diff != "" {
   388  				t.Fatalf("unexpected points:\n%s", diff)
   389  			}
   390  		})
   391  	}
   392  }
   393  
   394  // Ensure that the subquery gets passed the max series limit.
   395  func TestSubquery_MaxSeriesN(t *testing.T) {
   396  	shardMapper := ShardMapper{
   397  		MapShardsFn: func(_ context.Context, sources influxql.Sources, tr influxql.TimeRange) query.ShardGroup {
   398  			return &ShardGroup{
   399  				Fields: map[string]influxql.DataType{
   400  					"value": influxql.Float,
   401  				},
   402  				CreateIteratorFn: func(ctx context.Context, m *influxql.Measurement, opt query.IteratorOptions) (query.Iterator, error) {
   403  					if opt.MaxSeriesN != 1000 {
   404  						t.Errorf("max series limit has not been set")
   405  					}
   406  					return nil, nil
   407  				},
   408  			}
   409  		},
   410  	}
   411  
   412  	stmt := MustParseSelectStatement(`SELECT max(value) FROM (SELECT value FROM cpu)`)
   413  	cur, err := query.Select(context.Background(), stmt, &shardMapper, query.SelectOptions{
   414  		MaxSeriesN: 1000,
   415  	})
   416  	if err != nil {
   417  		t.Fatalf("unexpected error: %s", err)
   418  	}
   419  	cur.Close()
   420  }