Skip to content

Commit

Permalink
*: support drop user. (pingcap#1854)
Browse files Browse the repository at this point in the history
  • Loading branch information
hanfei1991 authored Oct 24, 2016
1 parent 3db4232 commit ba4d6ba
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 33 deletions.
19 changes: 19 additions & 0 deletions ast/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,25 @@ func (n *CreateUserStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}

// DropUserStmt creates user account.
// See http://dev.mysql.com/doc/refman/5.7/en/drop-user.html
type DropUserStmt struct {
stmtNode

IfExists bool
UserList []string
}

// Accept implements Node Accept interface.
func (n *DropUserStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*DropUserStmt)
return v.Leave(n)
}

// DoStmt is the struct for DO statement.
type DoStmt struct {
stmtNode
Expand Down
7 changes: 7 additions & 0 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/pingcap/tidb/inspectkv"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/model"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/plan"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/db"
Expand Down Expand Up @@ -67,6 +68,8 @@ const (
CodeWrongParamCount terror.ErrCode = 5
CodeRowKeyCount terror.ErrCode = 6
CodePrepareDDL terror.ErrCode = 7
// MySQL error code
CodeCannotUser terror.ErrCode = 1396
)

// Row represents a record row.
Expand Down Expand Up @@ -484,6 +487,10 @@ func init() {
}
return row.Data, nil
}
tableMySQLErrCodes := map[terror.ErrCode]uint16{
CodeCannotUser: mysql.ErrCannotUser,
}
terror.ErrClassToMySQLCodes[terror.ClassExecutor] = tableMySQLErrCodes
}

// HashJoinExec implements the hash join algorithm.
Expand Down
36 changes: 35 additions & 1 deletion executor/executor_simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/db"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/terror"
"github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/charset"
"github.com/pingcap/tidb/util/sqlexec"
Expand Down Expand Up @@ -82,6 +83,8 @@ func (e *SimpleExec) Next() (*Row, error) {
err = e.executeRollback(x)
case *ast.CreateUserStmt:
err = e.executeCreateUser(x)
case *ast.DropUserStmt:
err = e.executeDropUser(x)
case *ast.SetPwdStmt:
err = e.executeSetPwd(x)
case *ast.AnalyzeTableStmt:
Expand Down Expand Up @@ -343,8 +346,39 @@ func (e *SimpleExec) executeCreateUser(s *ast.CreateUserStmt) error {
return nil
}

func (e *SimpleExec) executeDropUser(s *ast.DropUserStmt) error {
failedUsers := make([]string, 0, len(s.UserList))
for _, user := range s.UserList {
userName, host := parseUser(user)
exists, err := userExists(e.ctx, userName, host)
if err != nil {
return errors.Trace(err)
}
if !exists {
if !s.IfExists {
failedUsers = append(failedUsers, user)
}
continue
}
sql := fmt.Sprintf(`DELETE FROM %s.%s WHERE Host = "%s" and User = "%s";`, mysql.SystemDB, mysql.UserTable, host, userName)
_, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql)
if err != nil {
failedUsers = append(failedUsers, user)
}
}
err := e.ctx.CommitTxn()
if err != nil {
return errors.Trace(err)
}
if len(failedUsers) > 0 {
errMsg := "Operation DROP USER failed for " + strings.Join(failedUsers, ",")
return terror.ClassExecutor.New(CodeCannotUser, errMsg)
}
return nil
}

// parse user string into username and host
// root@localhost -> roor, localhost
// root@localhost -> root, localhost
func parseUser(user string) (string, string) {
strs := strings.Split(user, "@")
return strs[0], strs[1]
Expand Down
38 changes: 30 additions & 8 deletions executor/executor_simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,40 @@ func (s *testSuite) TestCreateUser(c *C) {
createUserSQL = `CREATE USER 'test'@'localhost' IDENTIFIED BY '123';`
_, err := tk.Exec(createUserSQL)
c.Check(err, NotNil)
}

func (s *testSuite) TestCreateUserWithNoPassword(c *C) {
defer testleak.AfterTest(c)()
tk := testkit.NewTestKit(c, s.store)
dropUserSQL := `DROP USER IF EXISTS 'test'@'localhost' ;`
tk.MustExec(dropUserSQL)
// Create user test.
createUserSQL := `CREATE USER 'test1'@'localhost';`
createUserSQL = `CREATE USER 'test1'@'localhost';`
tk.MustExec(createUserSQL)
// Make sure user test in mysql.User.
result := tk.MustQuery(`SELECT Password FROM mysql.User WHERE User="test1" and Host="localhost"`)
rowStr := fmt.Sprintf("%v", []byte(util.EncodePassword("")))
result = tk.MustQuery(`SELECT Password FROM mysql.User WHERE User="test1" and Host="localhost"`)
rowStr = fmt.Sprintf("%v", []byte(util.EncodePassword("")))
result.Check(testkit.Rows(rowStr))
dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost' ;`
tk.MustExec(dropUserSQL)

// Test drop user if exists.
createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';`
tk.MustExec(createUserSQL)
dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost' ;`
tk.MustExec(dropUserSQL)
// Test negative cases without IF EXISTS.
createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';`
tk.MustExec(createUserSQL)
dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';`
_, err = tk.Exec(dropUserSQL)
c.Check(err, NotNil)
dropUserSQL = `DROP USER 'test3'@'localhost';`
_, err = tk.Exec(dropUserSQL)
c.Check(err, NotNil)
dropUserSQL = `DROP USER 'test1'@'localhost';`
_, err = tk.Exec(dropUserSQL)
c.Check(err, NotNil)
// Test positive cases without IF EXISTS.
createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';`
tk.MustExec(createUserSQL)
dropUserSQL = `DROP USER 'test1'@'localhost', 'test3'@'localhost';`
tk.MustExec(dropUserSQL)
}

func (s *testSuite) TestSetPwd(c *C) {
Expand Down
23 changes: 23 additions & 0 deletions parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ import (
DropDatabaseStmt "DROP DATABASE statement"
DropIndexStmt "DROP INDEX statement"
DropTableStmt "DROP TABLE statement"
DropUserStmt "DROP USER"
EmptyStmt "empty statement"
Enclosed "Enclosed by"
EqOpt "= or empty"
Expand Down Expand Up @@ -629,6 +630,7 @@ import (
UnlockTablesStmt "Unlock tables statement"
UpdateStmt "UPDATE statement"
Username "Username"
UsernameList "UsernameList"
UserSpec "Username and auth option"
UserSpecList "Username and auth option list"
UserVariable "User defined variable name"
Expand Down Expand Up @@ -1508,6 +1510,16 @@ DropTableStmt:
$$ = &ast.DropTableStmt{IfExists: true, Tables: $5.([]*ast.TableName)}
}

DropUserStmt:
"DROP" "USER" UsernameList
{
$$ = &ast.DropUserStmt{IfExists: false, UserList: $3.([]string)}
}
| "DROP" "USER" "IF" "EXISTS" UsernameList
{
$$ = &ast.DropUserStmt{IfExists: true, UserList: $5.([]string)}
}

TableOrTables:
"TABLE"
| "TABLES"
Expand Down Expand Up @@ -3896,6 +3908,16 @@ Username:
$$ = $1 + "@" + $3
}

UsernameList:
Username
{
$$ = []string{$1.(string)}
}
| UsernameList ',' Username
{
$$ = append($1.([]string), $3.(string))
}

PasswordOpt:
stringLit
{
Expand Down Expand Up @@ -4196,6 +4218,7 @@ Statement:
| DropDatabaseStmt
| DropIndexStmt
| DropTableStmt
| DropUserStmt
| FlushStmt
| GrantStmt
| InsertIntoStmt
Expand Down
2 changes: 2 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,8 @@ func (s *testParserSuite) TestPrivilege(c *C) {
{`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true},
{`CREATE USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true},
{`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true},
{`DROP USER 'root'@'localhost', 'root1'@'localhost'`, true},
{`DROP USER IF EXISTS 'root'@'localhost'`, true},

// For grant statement
{"GRANT ALL ON db1.* TO 'jeffrey'@'localhost';", true},
Expand Down
27 changes: 3 additions & 24 deletions plan/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ func (b *planBuilder) build(node ast.Node) Plan {
return b.buildAdmin(x)
case *ast.AlterTableStmt:
return b.buildDDL(x)
case *ast.AnalyzeTableStmt:
return b.buildSimple(x)
case *ast.BinlogStmt:
return b.buildSimple(x)
case *ast.CreateDatabaseStmt:
return b.buildDDL(x)
case *ast.CreateIndexStmt:
Expand All @@ -83,8 +79,6 @@ func (b *planBuilder) build(node ast.Node) Plan {
return &Execute{Name: x.Name, UsingVars: x.UsingVars}
case *ast.ExplainStmt:
return b.buildExplain(x)
case *ast.FlushTableStmt:
return b.buildSimple(x)
case *ast.InsertStmt:
return b.buildInsert(x)
case *ast.LoadDataStmt:
Expand All @@ -97,26 +91,11 @@ func (b *planBuilder) build(node ast.Node) Plan {
return b.buildUnion(x)
case *ast.UpdateStmt:
return b.buildUpdate(x)
case *ast.UseStmt:
return b.buildSimple(x)
case *ast.SetStmt:
return b.buildSimple(x)
case *ast.ShowStmt:
return b.buildShow(x)
case *ast.DoStmt:
return b.buildSimple(x)
case *ast.BeginStmt:
return b.buildSimple(x)
case *ast.CommitStmt:
return b.buildSimple(x)
case *ast.RollbackStmt:
return b.buildSimple(x)
case *ast.CreateUserStmt:
return b.buildSimple(x)
case *ast.SetPwdStmt:
return b.buildSimple(x)
case *ast.GrantStmt:
return b.buildSimple(x)
case *ast.AnalyzeTableStmt, *ast.BinlogStmt, *ast.FlushTableStmt, *ast.UseStmt, *ast.SetStmt, *ast.DoStmt, *ast.BeginStmt,
*ast.CommitStmt, *ast.RollbackStmt, *ast.CreateUserStmt, *ast.SetPwdStmt, *ast.GrantStmt, *ast.DropUserStmt:
return b.buildSimple(node.(ast.StmtNode))
case *ast.TruncateTableStmt:
return b.buildDDL(x)
}
Expand Down

0 comments on commit ba4d6ba

Please sign in to comment.