github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/executor_test.go (about) 1 package query_test 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/golang/mock/gomock" 10 iql "github.com/influxdata/influxdb/v2/influxql" 11 "github.com/influxdata/influxdb/v2/influxql/control" 12 "github.com/influxdata/influxdb/v2/influxql/query" 13 "github.com/influxdata/influxdb/v2/influxql/query/mocks" 14 "github.com/influxdata/influxql" 15 "github.com/stretchr/testify/assert" 16 "go.uber.org/zap/zaptest" 17 ) 18 19 var errUnexpected = errors.New("unexpected error") 20 21 type StatementExecutor struct { 22 ExecuteStatementFn func(ctx context.Context, stmt influxql.Statement, ectx *query.ExecutionContext) error 23 } 24 25 func (e *StatementExecutor) ExecuteStatement(ctx context.Context, stmt influxql.Statement, ectx *query.ExecutionContext) error { 26 return e.ExecuteStatementFn(ctx, stmt, ectx) 27 } 28 29 func NewQueryExecutor(t *testing.T) *query.Executor { 30 return query.NewExecutor(zaptest.NewLogger(t), control.NewControllerMetrics([]string{})) 31 } 32 33 func TestQueryExecutor_Interrupt(t *testing.T) { 34 q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`) 35 if err != nil { 36 t.Fatal(err) 37 } 38 39 e := NewQueryExecutor(t) 40 e.StatementExecutor = &StatementExecutor{ 41 ExecuteStatementFn: func(ctx context.Context, stmt influxql.Statement, ectx *query.ExecutionContext) error { 42 select { 43 case <-ctx.Done(): 44 return nil 45 case <-time.After(100 * time.Millisecond): 46 t.Error("killing the query did not close the channel after 100 milliseconds") 47 return errUnexpected 48 } 49 }, 50 } 51 52 ctx, cancel := context.WithCancel(context.Background()) 53 results, _ := e.ExecuteQuery(ctx, q, query.ExecutionOptions{}) 54 cancel() 55 56 result := <-results 57 if result != nil && result.Err != query.ErrQueryInterrupted { 58 t.Errorf("unexpected error: %s", result.Err) 59 } 60 } 61 62 func TestQueryExecutor_Abort(t *testing.T) { 63 q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`) 64 if err != nil { 65 t.Fatal(err) 66 } 67 68 ch1 := make(chan struct{}) 69 ch2 := make(chan struct{}) 70 71 e := NewQueryExecutor(t) 72 e.StatementExecutor = &StatementExecutor{ 73 ExecuteStatementFn: func(ctx context.Context, stmt influxql.Statement, ectx *query.ExecutionContext) error { 74 <-ch1 75 if err := ectx.Send(ctx, &query.Result{Err: errUnexpected}); err == nil { 76 t.Errorf("expected error") 77 } 78 close(ch2) 79 return nil 80 }, 81 } 82 83 ctx, cancel := context.WithCancel(context.Background()) 84 cancel() 85 86 results, _ := e.ExecuteQuery(ctx, q, query.ExecutionOptions{}) 87 close(ch1) 88 89 <-ch2 90 discardOutput(results) 91 } 92 93 func TestQueryExecutor_Panic(t *testing.T) { 94 q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`) 95 if err != nil { 96 t.Fatal(err) 97 } 98 99 e := NewQueryExecutor(t) 100 e.StatementExecutor = &StatementExecutor{ 101 ExecuteStatementFn: func(ctx context.Context, stmt influxql.Statement, ectx *query.ExecutionContext) error { 102 panic("test error") 103 }, 104 } 105 106 results, _ := e.ExecuteQuery(context.Background(), q, query.ExecutionOptions{}) 107 result := <-results 108 if len(result.Series) != 0 { 109 t.Errorf("expected %d rows, got %d", 0, len(result.Series)) 110 } 111 if result.Err == nil || result.Err.Error() != "SELECT count(value) FROM cpu [panic:test error]" { 112 t.Errorf("unexpected error: %s", result.Err) 113 } 114 } 115 116 func TestQueryExecutor_InvalidSource(t *testing.T) { 117 e := NewQueryExecutor(t) 118 e.StatementExecutor = &StatementExecutor{ 119 ExecuteStatementFn: func(ctx context.Context, stmt influxql.Statement, ectx *query.ExecutionContext) error { 120 return errors.New("statement executed unexpectedly") 121 }, 122 } 123 124 for i, tt := range []struct { 125 q string 126 err string 127 }{ 128 { 129 q: `SELECT fieldKey, fieldType FROM _fieldKeys`, 130 err: `unable to use system source '_fieldKeys': use SHOW FIELD KEYS instead`, 131 }, 132 { 133 q: `SELECT "name" FROM _measurements`, 134 err: `unable to use system source '_measurements': use SHOW MEASUREMENTS instead`, 135 }, 136 { 137 q: `SELECT "key" FROM _series`, 138 err: `unable to use system source '_series': use SHOW SERIES instead`, 139 }, 140 { 141 q: `SELECT tagKey FROM _tagKeys`, 142 err: `unable to use system source '_tagKeys': use SHOW TAG KEYS instead`, 143 }, 144 { 145 q: `SELECT "key", value FROM _tags`, 146 err: `unable to use system source '_tags': use SHOW TAG VALUES instead`, 147 }, 148 } { 149 q, err := influxql.ParseQuery(tt.q) 150 if err != nil { 151 t.Errorf("%d. unable to parse: %s", i, tt.q) 152 continue 153 } 154 155 results, _ := e.ExecuteQuery(context.Background(), q, query.ExecutionOptions{}) 156 result := <-results 157 if len(result.Series) != 0 { 158 t.Errorf("%d. expected %d rows, got %d", 0, i, len(result.Series)) 159 } 160 if result.Err == nil || result.Err.Error() != tt.err { 161 t.Errorf("%d. unexpected error: %s", i, result.Err) 162 } 163 } 164 } 165 166 // This test verifies Statistics are gathered 167 // and that ExecuteDuration accounts for PlanDuration 168 func TestExecutor_ExecuteQuery_Statistics(t *testing.T) { 169 ctl := gomock.NewController(t) 170 defer ctl.Finish() 171 172 stmt := influxql.MustParseStatement("SELECT f0 FROM m0") 173 q := &influxql.Query{Statements: influxql.Statements{stmt, stmt}} 174 175 se := mocks.NewMockStatementExecutor(ctl) 176 se.EXPECT().ExecuteStatement(gomock.Any(), stmt, gomock.Any()). 177 Times(2). 178 DoAndReturn(func(ctx context.Context, statement influxql.Statement, ectx *query.ExecutionContext) error { 179 time.Sleep(10 * time.Millisecond) 180 ectx.StatisticsGatherer.Append(iql.NewImmutableCollector(iql.Statistics{PlanDuration: 5 * time.Millisecond})) 181 return nil 182 }) 183 184 e := NewQueryExecutor(t) 185 e.StatementExecutor = se 186 187 ctx := context.Background() 188 results, stats := e.ExecuteQuery(ctx, q, query.ExecutionOptions{Quiet: true}) 189 <-results 190 assert.GreaterOrEqual(t, int64(stats.ExecuteDuration), int64(10*time.Millisecond)) 191 assert.Equal(t, 10*time.Millisecond, stats.PlanDuration) 192 assert.Equal(t, 2, stats.StatementCount) 193 } 194 195 func discardOutput(results <-chan *query.Result) { 196 for range results { 197 // Read all results and discard. 198 } 199 }