package meta_test import ( "errors" "reflect" "testing" "time" "github.com/davecgh/go-spew/spew" "github.com/influxdb/influxdb/influxql" "github.com/influxdb/influxdb/meta" "github.com/influxdb/influxdb/models" ) // Ensure a CREATE DATABASE statement can be executed. func TestStatementExecutor_ExecuteStatement_CreateDatabase(t *testing.T) { e := NewStatementExecutor() e.Store.CreateDatabaseFn = func(name string) (*meta.DatabaseInfo, error) { if name != "foo" { t.Fatalf("unexpected name: %s", name) } return &meta.DatabaseInfo{Name: name}, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`CREATE DATABASE foo`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a DROP DATABASE statement can be executed. func TestStatementExecutor_ExecuteStatement_DropDatabase(t *testing.T) { e := NewStatementExecutor() e.Store.DropDatabaseFn = func(name string) error { if name != "foo" { t.Fatalf("unexpected name: %s", name) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`DROP DATABASE foo`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure an ALTER DATABASE ... RENAME TO ... statement can be executed. func TestStatementExecutor_ExecuteStatement_AlterDatabaseRename(t *testing.T) { e := NewStatementExecutor() e.Store.RenameDatabaseFn = func(oldName, newName string) error { if oldName != "old_foo" { t.Fatalf("unexpected name: %s", oldName) } if newName != "new_foo" { t.Fatalf("unexpected name: %s", newName) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`ALTER DATABASE old_foo RENAME TO new_foo`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a SHOW DATABASES statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowDatabases(t *testing.T) { e := NewStatementExecutor() e.Store.DatabasesFn = func() ([]meta.DatabaseInfo, error) { return []meta.DatabaseInfo{ {Name: "foo"}, {Name: "bar"}, }, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW DATABASES`)); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Name: "databases", Columns: []string{"name"}, Values: [][]interface{}{ {"foo"}, {"bar"}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // Ensure a SHOW DATABASES statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_ShowDatabases_Err(t *testing.T) { e := NewStatementExecutor() e.Store.DatabasesFn = func() ([]meta.DatabaseInfo, error) { return nil, errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW DATABASES`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SHOW GRANTS FOR statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowGrantsFor(t *testing.T) { t.Skip("Intermittent test failure: issue 3028") e := NewStatementExecutor() e.Store.UserPrivilegesFn = func(username string) (map[string]influxql.Privilege, error) { if username != "dejan" { t.Fatalf("unexpected username: %s", username) } return map[string]influxql.Privilege{ "dejan": influxql.ReadPrivilege, "golja": influxql.WritePrivilege, }, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW GRANTS FOR dejan`)); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Columns: []string{"database", "privilege"}, Values: [][]interface{}{ {"dejan", "READ"}, {"golja", "WRITE"}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // Ensure a SHOW SERVERS statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowServers(t *testing.T) { e := NewStatementExecutor() e.Store.NodesFn = func() ([]meta.NodeInfo, error) { return []meta.NodeInfo{ {ID: 1, Host: "node0"}, {ID: 2, Host: "node1"}, }, nil } e.Store.PeersFn = func() ([]string, error) { return []string{"node0"}, nil } e.Store.LeaderFn = func() string { return "node0" } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW SERVERS`)); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Columns: []string{"id", "cluster_addr", "raft", "raft-leader"}, Values: [][]interface{}{ {uint64(1), "node0", true, true}, {uint64(2), "node1", false, false}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // Ensure a DROP SERVER statement can be executed. func TestStatementExecutor_ExecuteStatement_DropServer(t *testing.T) { e := NewStatementExecutor() e.Store.PeersFn = func() ([]string, error) { return []string{"node1"}, nil } // Ensure non-existent nodes do not cause a problem. e.Store.NodeFn = func(id uint64) (*meta.NodeInfo, error) { return nil, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`DROP SERVER 666`)); res.Err != meta.ErrNodeNotFound { t.Fatalf("unexpected error: %s", res.Err) } // Make a node exist. e.Store.NodeFn = func(id uint64) (*meta.NodeInfo, error) { return &meta.NodeInfo{ ID: 1, Host: "node1", }, nil } // Ensure Raft nodes cannot be dropped. if res := e.ExecuteStatement(influxql.MustParseStatement(`DROP SERVER 1`)); res.Err != meta.ErrNodeRaft { t.Fatalf("unexpected error: %s", res.Err) } // Ensure non-Raft nodes can be dropped. e.Store.PeersFn = func() ([]string, error) { return []string{"node2"}, nil } e.Store.DeleteNodeFn = func(id uint64, force bool) error { return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`DROP SERVER 1`)); res.Err != nil { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SHOW SERVERS statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_ShowServers_Err(t *testing.T) { e := NewStatementExecutor() e.Store.NodesFn = func() ([]meta.NodeInfo, error) { return nil, errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW SERVERS`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a CREATE USER statement can be executed. func TestStatementExecutor_ExecuteStatement_CreateUser(t *testing.T) { e := NewStatementExecutor() e.Store.CreateUserFn = func(name, password string, admin bool) (*meta.UserInfo, error) { if name != "susy" { t.Fatalf("unexpected name: %s", name) } else if password != "pass" { t.Fatalf("unexpected password: %s", password) } else if admin != true { t.Fatalf("unexpected admin: %v", admin) } return &meta.UserInfo{Name: name, Admin: admin}, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`CREATE USER susy WITH PASSWORD 'pass' WITH ALL PRIVILEGES`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a CREATE USER statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_CreateUser_Err(t *testing.T) { e := NewStatementExecutor() e.Store.CreateUserFn = func(name, password string, admin bool) (*meta.UserInfo, error) { return nil, errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`CREATE USER susy WITH PASSWORD 'pass'`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SET PASSWORD statement can be executed. func TestStatementExecutor_ExecuteStatement_SetPassword(t *testing.T) { e := NewStatementExecutor() e.Store.UpdateUserFn = func(name, password string) error { if name != "susy" { t.Fatalf("unexpected name: %s", name) } else if password != "pass" { t.Fatalf("unexpected password: %s", password) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SET PASSWORD FOR susy = 'pass' WITH ALL PRIVILEGES`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a SET PASSWORD statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_SetPassword_Err(t *testing.T) { e := NewStatementExecutor() e.Store.UpdateUserFn = func(name, password string) error { return errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`SET PASSWORD FOR susy = 'pass'`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a DROP USER statement can be executed. func TestStatementExecutor_ExecuteStatement_DropUser(t *testing.T) { e := NewStatementExecutor() e.Store.DropUserFn = func(name string) error { if name != "susy" { t.Fatalf("unexpected name: %s", name) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`DROP USER susy`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a DROP USER statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_DropUser_Err(t *testing.T) { e := NewStatementExecutor() e.Store.DropUserFn = func(name string) error { return errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`DROP USER susy`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SHOW USERS statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowUsers(t *testing.T) { e := NewStatementExecutor() e.Store.UsersFn = func() ([]meta.UserInfo, error) { return []meta.UserInfo{ {Name: "susy", Admin: true}, {Name: "bob", Admin: false}, }, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW USERS`)); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Columns: []string{"user", "admin"}, Values: [][]interface{}{ {"susy", true}, {"bob", false}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // Ensure a SHOW USERS statement returns an error from the store. func TestStatementExecutor_ExecuteStatement_ShowUsers_Err(t *testing.T) { e := NewStatementExecutor() e.Store.UsersFn = func() ([]meta.UserInfo, error) { return nil, errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW USERS`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a GRANT statement can be executed. func TestStatementExecutor_ExecuteStatement_Grant(t *testing.T) { e := NewStatementExecutor() e.Store.SetPrivilegeFn = func(username, database string, p influxql.Privilege) error { if username != "susy" { t.Fatalf("unexpected username: %s", username) } else if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if p != influxql.WritePrivilege { t.Fatalf("unexpected privilege: %s", p) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`GRANT WRITE ON foo TO susy`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a GRANT statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_Grant_Err(t *testing.T) { e := NewStatementExecutor() e.Store.SetPrivilegeFn = func(username, database string, p influxql.Privilege) error { return errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`GRANT READ ON foo TO susy`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a GRANT statement for admin privilege can be executed. func TestStatementExecutor_ExecuteStatement_GrantAdmin(t *testing.T) { e := NewStatementExecutor() e.Store.SetAdminPrivilegeFn = func(username string, admin bool) error { if username != "susy" { t.Fatalf("unexpected username: %s", username) } else if admin != true { t.Fatalf("unexpected admin privilege: %t", admin) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`GRANT ALL TO susy`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a GRANT statement for admin privilege returns errors from the store. func TestStatementExecutor_ExecuteStatement_GrantAdmin_Err(t *testing.T) { e := NewStatementExecutor() e.Store.SetAdminPrivilegeFn = func(username string, admin bool) error { return errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`GRANT ALL PRIVILEGES TO susy`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a REVOKE statement can be executed. func TestStatementExecutor_ExecuteStatement_Revoke(t *testing.T) { e := NewStatementExecutor() e.Store.SetPrivilegeFn = func(username, database string, p influxql.Privilege) error { if username != "susy" { t.Fatalf("unexpected username: %s", username) } else if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if p != influxql.NoPrivileges { t.Fatalf("unexpected privilege: %s", p) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`REVOKE ALL PRIVILEGES ON foo FROM susy`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a REVOKE statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_Revoke_Err(t *testing.T) { e := NewStatementExecutor() e.Store.SetPrivilegeFn = func(username, database string, p influxql.Privilege) error { return errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`REVOKE ALL PRIVILEGES ON foo FROM susy`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a REVOKE statement for admin privilege can be executed. func TestStatementExecutor_ExecuteStatement_RevokeAdmin(t *testing.T) { e := NewStatementExecutor() e.Store.SetAdminPrivilegeFn = func(username string, admin bool) error { if username != "susy" { t.Fatalf("unexpected username: %s", username) } else if admin != false { t.Fatalf("unexpected admin privilege: %t", admin) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`REVOKE ALL PRIVILEGES FROM susy`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a REVOKE statement for admin privilege returns errors from the store. func TestStatementExecutor_ExecuteStatement_RevokeAdmin_Err(t *testing.T) { e := NewStatementExecutor() e.Store.SetAdminPrivilegeFn = func(username string, admin bool) error { return errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`REVOKE ALL PRIVILEGES FROM susy`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a CREATE RETENTION POLICY statement can be executed. func TestStatementExecutor_ExecuteStatement_CreateRetentionPolicy(t *testing.T) { e := NewStatementExecutor() e.Store.CreateRetentionPolicyFn = func(database string, rpi *meta.RetentionPolicyInfo) (*meta.RetentionPolicyInfo, error) { if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if rpi.Name != "rp0" { t.Fatalf("unexpected name: %s", rpi.Name) } else if rpi.Duration != 2*time.Hour { t.Fatalf("unexpected duration: %v", rpi.Duration) } else if rpi.ReplicaN != 3 { t.Fatalf("unexpected replication factor: %v", rpi.ReplicaN) } return nil, nil } e.Store.SetDefaultRetentionPolicyFn = func(database, name string) error { if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if name != "rp0" { t.Fatalf("unexpected name: %s", name) } return nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`CREATE RETENTION POLICY rp0 ON foo DURATION 2h REPLICATION 3 DEFAULT`)); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a CREATE RETENTION POLICY statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_CreateRetentionPolicy_Err(t *testing.T) { e := NewStatementExecutor() e.Store.CreateRetentionPolicyFn = func(database string, rpi *meta.RetentionPolicyInfo) (*meta.RetentionPolicyInfo, error) { return nil, errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`CREATE RETENTION POLICY rp0 ON foo DURATION 2h REPLICATION 1`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure an ALTER RETENTION POLICY statement can execute. func TestStatementExecutor_ExecuteStatement_AlterRetentionPolicy(t *testing.T) { e := NewStatementExecutor() e.Store.UpdateRetentionPolicyFn = func(database, name string, rpu *meta.RetentionPolicyUpdate) error { if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if name != "rp0" { t.Fatalf("unexpected name: %s", name) } else if rpu.Duration != nil && *rpu.Duration != 7*24*time.Hour { t.Fatalf("unexpected duration: %v", *rpu.Duration) } else if rpu.ReplicaN != nil && *rpu.ReplicaN != 2 { t.Fatalf("unexpected replication factor: %v", *rpu.ReplicaN) } return nil } e.Store.SetDefaultRetentionPolicyFn = func(database, name string) error { if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if name != "rp0" { t.Fatalf("unexpected name: %s", name) } return nil } stmt := influxql.MustParseStatement(`ALTER RETENTION POLICY rp0 ON foo DURATION 7d REPLICATION 2 DEFAULT`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatalf("unexpected error: %s", res.Err) } stmt = influxql.MustParseStatement(`ALTER RETENTION POLICY rp0 ON foo DURATION 7d`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatalf("unexpected error: %s", res.Err) } stmt = influxql.MustParseStatement(`ALTER RETENTION POLICY rp0 ON foo REPLICATION 2`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a ALTER RETENTION POLICY statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_AlterRetentionPolicy_Err(t *testing.T) { e := NewStatementExecutor() e.Store.UpdateRetentionPolicyFn = func(database, name string, rpu *meta.RetentionPolicyUpdate) error { return errors.New("marker") } stmt := influxql.MustParseStatement(`ALTER RETENTION POLICY rp0 ON foo DURATION 1m REPLICATION 4 DEFAULT`) if res := e.ExecuteStatement(stmt); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a ALTER RETENTION POLICY statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_AlterRetentionPolicy_ErrSetDefault(t *testing.T) { e := NewStatementExecutor() e.Store.UpdateRetentionPolicyFn = func(database, name string, rpu *meta.RetentionPolicyUpdate) error { return nil } e.Store.SetDefaultRetentionPolicyFn = func(database, name string) error { return errors.New("marker") } stmt := influxql.MustParseStatement(`ALTER RETENTION POLICY rp0 ON foo DURATION 1m REPLICATION 4 DEFAULT`) if res := e.ExecuteStatement(stmt); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a DROP RETENTION POLICY statement can execute. func TestStatementExecutor_ExecuteStatement_DropRetentionPolicy(t *testing.T) { e := NewStatementExecutor() e.Store.DropRetentionPolicyFn = func(database, name string) error { if database != "foo" { t.Fatalf("unexpected database: %s", database) } else if name != "rp0" { t.Fatalf("unexpected name: %s", name) } return nil } stmt := influxql.MustParseStatement(`DROP RETENTION POLICY rp0 ON foo`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a DROP RETENTION POLICY statement returns errors from the store. func TestStatementExecutor_ExecuteStatement_DropRetentionPolicy_Err(t *testing.T) { e := NewStatementExecutor() e.Store.DropRetentionPolicyFn = func(database, name string) error { return errors.New("marker") } stmt := influxql.MustParseStatement(`DROP RETENTION POLICY rp0 ON foo`) if res := e.ExecuteStatement(stmt); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SHOW RETENTION POLICIES statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowRetentionPolicies(t *testing.T) { e := NewStatementExecutor() e.Store.DatabaseFn = func(name string) (*meta.DatabaseInfo, error) { if name != "db0" { t.Fatalf("unexpected name: %s", name) } return &meta.DatabaseInfo{ Name: name, DefaultRetentionPolicy: "rp1", RetentionPolicies: []meta.RetentionPolicyInfo{ { Name: "rp0", Duration: 2 * time.Hour, ReplicaN: 3, }, { Name: "rp1", Duration: 24 * time.Hour, ReplicaN: 1, }, }, }, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW RETENTION POLICIES ON db0`)); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Columns: []string{"name", "duration", "replicaN", "default"}, Values: [][]interface{}{ {"rp0", "2h0m0s", 3, false}, {"rp1", "24h0m0s", 1, true}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // Ensure a SHOW RETENTION POLICIES statement can return an error from the store. func TestStatementExecutor_ExecuteStatement_ShowRetentionPolicies_Err(t *testing.T) { e := NewStatementExecutor() e.Store.DatabaseFn = func(name string) (*meta.DatabaseInfo, error) { return nil, errors.New("marker") } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW RETENTION POLICIES ON db0`)); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SHOW RETENTION POLICIES statement can return an error if the database doesn't exist. func TestStatementExecutor_ExecuteStatement_ShowRetentionPolicies_ErrDatabaseNotFound(t *testing.T) { e := NewStatementExecutor() e.Store.DatabaseFn = func(name string) (*meta.DatabaseInfo, error) { return nil, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW RETENTION POLICIES ON db0`)); res.Err != meta.ErrDatabaseNotFound { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a CREATE CONTINUOUS QUERY statement can be executed. func TestStatementExecutor_ExecuteStatement_CreateContinuousQuery(t *testing.T) { e := NewStatementExecutor() e.Store.CreateContinuousQueryFn = func(database, name, query string) error { if database != "db0" { t.Fatalf("unexpected database: %s", database) } else if name != "cq0" { t.Fatalf("unexpected name: %s", name) } else if query != `CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT count(field1) INTO db1 FROM db0 GROUP BY time(1h) END` { t.Fatalf("unexpected query: %s", query) } return nil } stmt := influxql.MustParseStatement(`CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT count(field1) INTO db1 FROM db0 GROUP BY time(1h) END`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a CREATE CONTINUOUS QUERY statement can return an error from the store. func TestStatementExecutor_ExecuteStatement_CreateContinuousQuery_Err(t *testing.T) { e := NewStatementExecutor() e.Store.CreateContinuousQueryFn = func(database, name, query string) error { return errors.New("marker") } stmt := influxql.MustParseStatement(`CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT count(field1) INTO db1 FROM db0 GROUP BY time(1h) END`) if res := e.ExecuteStatement(stmt); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a DROP CONTINUOUS QUERY statement can be executed. func TestStatementExecutor_ExecuteStatement_DropContinuousQuery(t *testing.T) { e := NewStatementExecutor() e.Store.DropContinuousQueryFn = func(database, name string) error { if database != "db0" { t.Fatalf("unexpected database: %s", database) } else if name != "cq0" { t.Fatalf("unexpected name: %s", name) } return nil } stmt := influxql.MustParseStatement(`DROP CONTINUOUS QUERY cq0 ON db0`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatal(res.Err) } else if res.Series != nil { t.Fatalf("unexpected rows: %#v", res.Series) } } // Ensure a DROP CONTINUOUS QUERY statement can return an error from the store. func TestStatementExecutor_ExecuteStatement_DropContinuousQuery_Err(t *testing.T) { e := NewStatementExecutor() e.Store.DropContinuousQueryFn = func(database, name string) error { return errors.New("marker") } stmt := influxql.MustParseStatement(`DROP CONTINUOUS QUERY cq0 ON db0`) if res := e.ExecuteStatement(stmt); res.Err == nil || res.Err.Error() != "marker" { t.Fatalf("unexpected error: %s", res.Err) } } // Ensure a SHOW CONTINUOUS QUERIES statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowContinuousQueries(t *testing.T) { e := NewStatementExecutor() e.Store.DatabasesFn = func() ([]meta.DatabaseInfo, error) { return []meta.DatabaseInfo{ { Name: "db0", ContinuousQueries: []meta.ContinuousQueryInfo{ {Name: "cq0", Query: "SELECT count(field1) INTO db1 FROM db0"}, {Name: "cq1", Query: "SELECT count(field1) INTO db2 FROM db0"}, }, }, { Name: "db1", ContinuousQueries: []meta.ContinuousQueryInfo{ {Name: "cq2", Query: "SELECT count(field1) INTO db3 FROM db1"}, }, }, }, nil } stmt := influxql.MustParseStatement(`SHOW CONTINUOUS QUERIES`) if res := e.ExecuteStatement(stmt); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Name: "db0", Columns: []string{"name", "query"}, Values: [][]interface{}{ {"cq0", "SELECT count(field1) INTO db1 FROM db0"}, {"cq1", "SELECT count(field1) INTO db2 FROM db0"}, }, }, { Name: "db1", Columns: []string{"name", "query"}, Values: [][]interface{}{ {"cq2", "SELECT count(field1) INTO db3 FROM db1"}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // Ensure a SHOW CONTINUOUS QUERIES statement can return an error from the store. func TestStatementExecutor_ExecuteStatement_ShowContinuousQueries_Err(t *testing.T) { e := NewStatementExecutor() e.Store.DatabasesFn = func() ([]meta.DatabaseInfo, error) { return nil, errors.New("marker") } stmt := influxql.MustParseStatement(`SHOW CONTINUOUS QUERIES`) if res := e.ExecuteStatement(stmt); res.Err == nil || res.Err.Error() != "marker" { t.Fatal(res.Err) } } // Ensure that executing an unsupported statement will panic. func TestStatementExecutor_ExecuteStatement_Unsupported(t *testing.T) { var panicked bool func() { defer func() { if r := recover(); r != nil { panicked = true } }() // Execute a SELECT statement. NewStatementExecutor().ExecuteStatement( influxql.MustParseStatement(`SELECT count(field1) FROM db0`), ) }() // Ensure that the executor panicked. if !panicked { t.Fatal("executor did not panic") } } // Ensure a SHOW SHARDS statement can be executed. func TestStatementExecutor_ExecuteStatement_ShowShards(t *testing.T) { e := NewStatementExecutor() e.Store.DatabasesFn = func() ([]meta.DatabaseInfo, error) { return []meta.DatabaseInfo{ { Name: "foo", RetentionPolicies: []meta.RetentionPolicyInfo{ { Duration: time.Second, ShardGroups: []meta.ShardGroupInfo{ { StartTime: time.Unix(0, 0), EndTime: time.Unix(1, 0), Shards: []meta.ShardInfo{ { ID: 1, Owners: []meta.ShardOwner{ {NodeID: 1}, {NodeID: 2}, {NodeID: 3}, }, }, { ID: 2, }, }, }, }, }, }, }, }, nil } if res := e.ExecuteStatement(influxql.MustParseStatement(`SHOW SHARDS`)); res.Err != nil { t.Fatal(res.Err) } else if !reflect.DeepEqual(res.Series, models.Rows{ { Name: "foo", Columns: []string{"id", "start_time", "end_time", "expiry_time", "owners"}, Values: [][]interface{}{ {uint64(1), "1970-01-01T00:00:00Z", "1970-01-01T00:00:01Z", "1970-01-01T00:00:02Z", "1,2,3"}, {uint64(2), "1970-01-01T00:00:00Z", "1970-01-01T00:00:01Z", "1970-01-01T00:00:02Z", ""}, }, }, }) { t.Fatalf("unexpected rows: %s", spew.Sdump(res.Series)) } } // StatementExecutor represents a test wrapper for meta.StatementExecutor. type StatementExecutor struct { *meta.StatementExecutor Store StatementExecutorStore } // NewStatementExecutor returns a new instance of StatementExecutor with a mock store. func NewStatementExecutor() *StatementExecutor { e := &StatementExecutor{} e.StatementExecutor = &meta.StatementExecutor{Store: &e.Store} return e } // StatementExecutorStore represents a mock implementation of StatementExecutor.Store. type StatementExecutorStore struct { NodeFn func(id uint64) (*meta.NodeInfo, error) NodesFn func() ([]meta.NodeInfo, error) PeersFn func() ([]string, error) LeaderFn func() string DatabaseFn func(name string) (*meta.DatabaseInfo, error) DatabasesFn func() ([]meta.DatabaseInfo, error) CreateDatabaseFn func(name string) (*meta.DatabaseInfo, error) DropDatabaseFn func(name string) error DeleteNodeFn func(nodeID uint64, force bool) error RenameDatabaseFn func(oldName, newName string) error DefaultRetentionPolicyFn func(database string) (*meta.RetentionPolicyInfo, error) CreateRetentionPolicyFn func(database string, rpi *meta.RetentionPolicyInfo) (*meta.RetentionPolicyInfo, error) UpdateRetentionPolicyFn func(database, name string, rpu *meta.RetentionPolicyUpdate) error SetDefaultRetentionPolicyFn func(database, name string) error DropRetentionPolicyFn func(database, name string) error UsersFn func() ([]meta.UserInfo, error) CreateUserFn func(name, password string, admin bool) (*meta.UserInfo, error) UpdateUserFn func(name, password string) error DropUserFn func(name string) error SetPrivilegeFn func(username, database string, p influxql.Privilege) error SetAdminPrivilegeFn func(username string, admin bool) error UserPrivilegesFn func(username string) (map[string]influxql.Privilege, error) UserPrivilegeFn func(username, database string) (*influxql.Privilege, error) ContinuousQueriesFn func() ([]meta.ContinuousQueryInfo, error) CreateContinuousQueryFn func(database, name, query string) error DropContinuousQueryFn func(database, name string) error } func (s *StatementExecutorStore) Node(id uint64) (*meta.NodeInfo, error) { return s.NodeFn(id) } func (s *StatementExecutorStore) Nodes() ([]meta.NodeInfo, error) { return s.NodesFn() } func (s *StatementExecutorStore) Peers() ([]string, error) { return s.PeersFn() } func (s *StatementExecutorStore) Leader() string { if s.LeaderFn != nil { return s.LeaderFn() } return "" } func (s *StatementExecutorStore) DeleteNode(nodeID uint64, force bool) error { return s.DeleteNodeFn(nodeID, force) } func (s *StatementExecutorStore) Database(name string) (*meta.DatabaseInfo, error) { return s.DatabaseFn(name) } func (s *StatementExecutorStore) Databases() ([]meta.DatabaseInfo, error) { return s.DatabasesFn() } func (s *StatementExecutorStore) CreateDatabase(name string) (*meta.DatabaseInfo, error) { return s.CreateDatabaseFn(name) } func (s *StatementExecutorStore) DropDatabase(name string) error { return s.DropDatabaseFn(name) } func (s *StatementExecutorStore) RenameDatabase(oldName, newName string) error { return s.RenameDatabaseFn(oldName, newName) } func (s *StatementExecutorStore) DefaultRetentionPolicy(database string) (*meta.RetentionPolicyInfo, error) { return s.DefaultRetentionPolicyFn(database) } func (s *StatementExecutorStore) CreateRetentionPolicy(database string, rpi *meta.RetentionPolicyInfo) (*meta.RetentionPolicyInfo, error) { return s.CreateRetentionPolicyFn(database, rpi) } func (s *StatementExecutorStore) UpdateRetentionPolicy(database, name string, rpu *meta.RetentionPolicyUpdate) error { return s.UpdateRetentionPolicyFn(database, name, rpu) } func (s *StatementExecutorStore) SetDefaultRetentionPolicy(database, name string) error { return s.SetDefaultRetentionPolicyFn(database, name) } func (s *StatementExecutorStore) DropRetentionPolicy(database, name string) error { return s.DropRetentionPolicyFn(database, name) } func (s *StatementExecutorStore) Users() ([]meta.UserInfo, error) { return s.UsersFn() } func (s *StatementExecutorStore) CreateUser(name, password string, admin bool) (*meta.UserInfo, error) { return s.CreateUserFn(name, password, admin) } func (s *StatementExecutorStore) UpdateUser(name, password string) error { return s.UpdateUserFn(name, password) } func (s *StatementExecutorStore) DropUser(name string) error { return s.DropUserFn(name) } func (s *StatementExecutorStore) SetPrivilege(username, database string, p influxql.Privilege) error { return s.SetPrivilegeFn(username, database, p) } func (s *StatementExecutorStore) SetAdminPrivilege(username string, admin bool) error { return s.SetAdminPrivilegeFn(username, admin) } func (s *StatementExecutorStore) UserPrivileges(username string) (map[string]influxql.Privilege, error) { return s.UserPrivilegesFn(username) } func (s *StatementExecutorStore) UserPrivilege(username, database string) (*influxql.Privilege, error) { return s.UserPrivilegeFn(username, database) } func (s *StatementExecutorStore) ContinuousQueries() ([]meta.ContinuousQueryInfo, error) { return s.ContinuousQueriesFn() } func (s *StatementExecutorStore) CreateContinuousQuery(database, name, query string) error { return s.CreateContinuousQueryFn(database, name, query) } func (s *StatementExecutorStore) DropContinuousQuery(database, name string) error { return s.DropContinuousQueryFn(database, name) }