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  }