func TestRewriteStatement(t *testing.T) {
	tests := []struct {
		stmt string
		s    string
	}{
		{
			stmt: `SHOW FIELD KEYS`,
			s:    `SELECT fieldKey FROM _fieldKeys`,
		},
		{
			stmt: `SHOW FIELD KEYS FROM cpu`,
			s:    `SELECT fieldKey FROM _fieldKeys WHERE _name = 'cpu'`,
		},
		{
			stmt: `SHOW FIELD KEYS FROM /c.*/`,
			s:    `SELECT fieldKey FROM _fieldKeys WHERE _name =~ /c.*/`,
		},
		{
			stmt: `SHOW MEASUREMENTS`,
			s:    `SELECT _name AS "name" FROM _measurements`,
		},
		{
			stmt: `SHOW MEASUREMENTS WITH MEASUREMENT = cpu`,
			s:    `SELECT _name AS "name" FROM _measurements WHERE _name = 'cpu'`,
		},
		{
			stmt: `SHOW MEASUREMENTS WITH MEASUREMENT =~ /c.*/`,
			s:    `SELECT _name AS "name" FROM _measurements WHERE _name =~ /c.*/`,
		},
		{
			stmt: `SHOW MEASUREMENTS WHERE region = 'uswest'`,
			s:    `SELECT _name AS "name" FROM _measurements WHERE region = 'uswest'`,
		},
		{
			stmt: `SHOW MEASUREMENTS WITH MEASUREMENT = cpu WHERE region = 'uswest'`,
			s:    `SELECT _name AS "name" FROM _measurements WHERE (_name = 'cpu') AND (region = 'uswest')`,
		},
		{
			stmt: `SHOW TAG KEYS`,
			s:    `SELECT tagKey FROM _tagKeys`,
		},
		{
			stmt: `SHOW TAG KEYS FROM cpu`,
			s:    `SELECT tagKey FROM _tagKeys WHERE _name = 'cpu'`,
		},
		{
			stmt: `SHOW TAG KEYS FROM /c.*/`,
			s:    `SELECT tagKey FROM _tagKeys WHERE _name =~ /c.*/`,
		},
		{
			stmt: `SHOW TAG KEYS FROM cpu WHERE region = 'uswest'`,
			s:    `SELECT tagKey FROM _tagKeys WHERE (_name = 'cpu') AND (region = 'uswest')`,
		},
		{
			stmt: `SHOW TAG VALUES WITH KEY = region`,
			s:    `SELECT _tagKey AS "key", value FROM _tags WHERE _tagKey = 'region'`,
		},
		{
			stmt: `SHOW TAG VALUES FROM cpu WITH KEY = region`,
			s:    `SELECT _tagKey AS "key", value FROM _tags WHERE (_name = 'cpu') AND (_tagKey = 'region')`,
		},
		{
			stmt: `SHOW TAG VALUES FROM cpu WITH KEY IN (region, host)`,
			s:    `SELECT _tagKey AS "key", value FROM _tags WHERE (_name = 'cpu') AND (_tagKey = 'region' OR _tagKey = 'host')`,
		},
		{
			stmt: `SELECT value FROM cpu`,
			s:    `SELECT value FROM cpu`,
		},
	}

	for _, test := range tests {
		stmt, err := influxql.ParseStatement(test.stmt)
		if err != nil {
			t.Errorf("error parsing statement: %s", err)
		} else {
			stmt, err = influxql.RewriteStatement(stmt)
			if err != nil {
				t.Errorf("error rewriting statement: %s", err)
			} else if s := stmt.String(); s != test.s {
				t.Errorf("error rendering string. expected %s, actual: %s", test.s, s)
			}
		}
	}
}
Beispiel #2
0
func (e *QueryExecutor) executeQuery(query *influxql.Query, database string, chunkSize int, closing <-chan struct{}, results chan *influxql.Result) {
	defer close(results)
	defer e.recover(query, results)

	e.statMap.Add(statQueriesActive, 1)
	defer func(start time.Time) {
		e.statMap.Add(statQueriesActive, -1)
		e.statMap.Add(statQueryExecutionDuration, time.Since(start).Nanoseconds())
	}(time.Now())

	qerr := &influxql.QueryError{}
	var qid uint64
	if e.QueryManager != nil {
		var err error
		qid, closing, err = e.QueryManager.AttachQuery(&influxql.QueryParams{
			Query:       query,
			Database:    database,
			Timeout:     e.QueryTimeout,
			InterruptCh: closing,
			Error:       qerr,
		})
		if err != nil {
			results <- &influxql.Result{Err: err}
			return
		}

		defer e.QueryManager.KillQuery(qid)
	}

	logger := e.logger()

	var i int
	for ; i < len(query.Statements); i++ {
		stmt := query.Statements[i]

		// If a default database wasn't passed in by the caller, check the statement.
		defaultDB := database
		if defaultDB == "" {
			if s, ok := stmt.(influxql.HasDefaultDatabase); ok {
				defaultDB = s.DefaultDatabase()
			}
		}

		// Rewrite statements, if necessary.
		// This can occur on meta read statements which convert to SELECT statements.
		newStmt, err := influxql.RewriteStatement(stmt)
		if err != nil {
			results <- &influxql.Result{Err: err}
			break
		}
		stmt = newStmt

		// Normalize each statement.
		if err := e.normalizeStatement(stmt, defaultDB); err != nil {
			results <- &influxql.Result{Err: err}
			break
		}

		// Log each normalized statement.
		logger.Println(stmt.String())

		// Select statements are handled separately so that they can be streamed.
		if stmt, ok := stmt.(*influxql.SelectStatement); ok {
			if err := e.executeSelectStatement(stmt, chunkSize, i, qid, results, closing); err != nil {
				if err == influxql.ErrQueryInterrupted {
					err = qerr.Error()
				}
				results <- &influxql.Result{StatementID: i, Err: err}
				break
			}
			continue
		}

		var rows models.Rows
		switch stmt := stmt.(type) {
		case *influxql.AlterRetentionPolicyStatement:
			err = e.executeAlterRetentionPolicyStatement(stmt)
		case *influxql.CreateContinuousQueryStatement:
			err = e.executeCreateContinuousQueryStatement(stmt)
		case *influxql.CreateDatabaseStatement:
			err = e.executeCreateDatabaseStatement(stmt)
		case *influxql.CreateRetentionPolicyStatement:
			err = e.executeCreateRetentionPolicyStatement(stmt)
		case *influxql.CreateSubscriptionStatement:
			err = e.executeCreateSubscriptionStatement(stmt)
		case *influxql.CreateUserStatement:
			err = e.executeCreateUserStatement(stmt)
		case *influxql.DropContinuousQueryStatement:
			err = e.executeDropContinuousQueryStatement(stmt)
		case *influxql.DropDatabaseStatement:
			err = e.executeDropDatabaseStatement(stmt)
		case *influxql.DropMeasurementStatement:
			err = e.executeDropMeasurementStatement(stmt, database)
		case *influxql.DropSeriesStatement:
			err = e.executeDropSeriesStatement(stmt, database)
		case *influxql.DropRetentionPolicyStatement:
			err = e.executeDropRetentionPolicyStatement(stmt)
		case *influxql.DropServerStatement:
			err = influxql.ErrInvalidQuery
		case *influxql.DropShardStatement:
			err = e.executeDropShardStatement(stmt)
		case *influxql.DropSubscriptionStatement:
			err = e.executeDropSubscriptionStatement(stmt)
		case *influxql.DropUserStatement:
			err = e.executeDropUserStatement(stmt)
		case *influxql.GrantStatement:
			err = e.executeGrantStatement(stmt)
		case *influxql.GrantAdminStatement:
			err = e.executeGrantAdminStatement(stmt)
		case *influxql.KillQueryStatement:
			err = e.executeKillQueryStatement(stmt)
		case *influxql.RevokeStatement:
			err = e.executeRevokeStatement(stmt)
		case *influxql.RevokeAdminStatement:
			err = e.executeRevokeAdminStatement(stmt)
		case *influxql.ShowContinuousQueriesStatement:
			rows, err = e.executeShowContinuousQueriesStatement(stmt)
		case *influxql.ShowDatabasesStatement:
			rows, err = e.executeShowDatabasesStatement(stmt)
		case *influxql.ShowDiagnosticsStatement:
			rows, err = e.executeShowDiagnosticsStatement(stmt)
		case *influxql.ShowGrantsForUserStatement:
			rows, err = e.executeShowGrantsForUserStatement(stmt)
		case *influxql.ShowQueriesStatement:
			rows, err = e.executeShowQueriesStatement(stmt)
		case *influxql.ShowRetentionPoliciesStatement:
			rows, err = e.executeShowRetentionPoliciesStatement(stmt)
		case *influxql.ShowServersStatement:
			// TODO: corylanou add this back for single node
			err = influxql.ErrInvalidQuery
		case *influxql.ShowShardsStatement:
			rows, err = e.executeShowShardsStatement(stmt)
		case *influxql.ShowShardGroupsStatement:
			rows, err = e.executeShowShardGroupsStatement(stmt)
		case *influxql.ShowStatsStatement:
			rows, err = e.executeShowStatsStatement(stmt)
		case *influxql.ShowSubscriptionsStatement:
			rows, err = e.executeShowSubscriptionsStatement(stmt)
		case *influxql.ShowTagValuesStatement:
			rows, err = e.executeShowTagValuesStatement(stmt, database)
		case *influxql.ShowUsersStatement:
			rows, err = e.executeShowUsersStatement(stmt)
		case *influxql.SetPasswordUserStatement:
			err = e.executeSetPasswordUserStatement(stmt)
		default:
			err = influxql.ErrInvalidQuery
		}

		// Send results for each statement.
		results <- &influxql.Result{
			StatementID: i,
			Series:      rows,
			Err:         err,
		}

		// Stop after the first error.
		if err != nil {
			break
		}
	}

	// Send error results for any statements which were not executed.
	for ; i < len(query.Statements)-1; i++ {
		results <- &influxql.Result{
			StatementID: i,
			Err:         influxql.ErrNotExecuted,
		}
	}
}