diff --git a/parser/ast/dml.go b/parser/ast/dml.go index 0604c73b7a8c6..21c94d103407a 100755 --- a/parser/ast/dml.go +++ b/parser/ast/dml.go @@ -1798,18 +1798,6 @@ func (n *ShowStmt) Restore(ctx *RestoreCtx) error { } return nil } - restoreUserName := func() error { - if n.User.CurrentUser { - ctx.WriteKeyWord("CURRENT_USER") - } else { - ctx.WriteString(n.User.Username) - if n.User.Hostname != "" { - ctx.WritePlain("@") - ctx.WriteString(n.User.Hostname) - } - } - return nil - } ctx.WriteKeyWord("SHOW ") switch n.Tp { @@ -1831,12 +1819,16 @@ func (n *ShowStmt) Restore(ctx *RestoreCtx) error { ctx.WriteName(n.DBName) case ShowCreateUser: ctx.WriteKeyWord("CREATE USER ") - restoreUserName() + if err := n.User.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ShowStmt.User") + } case ShowGrants: ctx.WriteKeyWord("GRANTS") if n.User != nil { ctx.WriteKeyWord(" FOR ") - restoreUserName() + if err := n.User.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ShowStmt.User") + } } case ShowMasterStatus: ctx.WriteKeyWord("MASTER STATUS") diff --git a/parser/ast/misc.go b/parser/ast/misc.go index 20a41396953dc..6b474eef511f0 100755 --- a/parser/ast/misc.go +++ b/parser/ast/misc.go @@ -93,6 +93,18 @@ type AuthOption struct { // TODO: support auth_plugin } +// Restore implements Node interface. +func (n *AuthOption) Restore(ctx *RestoreCtx) error { + ctx.WriteKeyWord("IDENTIFIED BY ") + if n.ByAuthString { + ctx.WriteString(n.AuthString) + } else { + ctx.WriteKeyWord("PASSWORD ") + ctx.WriteString(n.HashString) + } + return nil +} + // TraceStmt is a statement to trace what sql actually does at background. type TraceStmt struct { stmtNode @@ -559,28 +571,42 @@ type UserSpec struct { AuthOpt *AuthOption } +// Restore implements Node interface. +func (n *UserSpec) Restore(ctx *RestoreCtx) error { + if err := n.User.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore UserSpec.User") + } + if n.AuthOpt != nil { + ctx.WritePlain(" ") + if err := n.AuthOpt.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore UserSpec.AuthOpt") + } + } + return nil +} + // SecurityString formats the UserSpec without password information. -func (u *UserSpec) SecurityString() string { +func (n *UserSpec) SecurityString() string { withPassword := false - if opt := u.AuthOpt; opt != nil { + if opt := n.AuthOpt; opt != nil { if len(opt.AuthString) > 0 || len(opt.HashString) > 0 { withPassword = true } } if withPassword { - return fmt.Sprintf("{%s password = ***}", u.User) + return fmt.Sprintf("{%s password = ***}", n.User) } - return u.User.String() + return n.User.String() } // EncodedPassword returns the encoded password (which is the real data mysql.user). // The boolean value indicates input's password format is legal or not. -func (u *UserSpec) EncodedPassword() (string, bool) { - if u.AuthOpt == nil { +func (n *UserSpec) EncodedPassword() (string, bool) { + if n.AuthOpt == nil { return "", true } - opt := u.AuthOpt + opt := n.AuthOpt if opt.ByAuthString { return auth.EncodePassword(opt.AuthString), true } @@ -603,7 +629,19 @@ type CreateUserStmt struct { // Restore implements Node interface. func (n *CreateUserStmt) Restore(ctx *RestoreCtx) error { - return errors.New("Not implemented") + ctx.WriteKeyWord("CREATE USER ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + for i, v := range n.Specs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.Specs[%d]", i) + } + } + return nil } // Accept implements Node Accept interface. @@ -639,7 +677,26 @@ type AlterUserStmt struct { // Restore implements Node interface. func (n *AlterUserStmt) Restore(ctx *RestoreCtx) error { - return errors.New("Not implemented") + ctx.WriteKeyWord("ALTER USER ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + if n.CurrentAuth != nil { + ctx.WriteKeyWord("USER") + ctx.WritePlain("() ") + if err := n.CurrentAuth.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterUserStmt.CurrentAuth") + } + } + for i, v := range n.Specs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.Specs[%d]", i) + } + } + return nil } // SecureText implements SensitiveStatement interface. @@ -674,7 +731,19 @@ type DropUserStmt struct { // Restore implements Node interface. func (n *DropUserStmt) Restore(ctx *RestoreCtx) error { - return errors.New("Not implemented") + ctx.WriteKeyWord("DROP USER ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + for i, v := range n.UserList { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DropUserStmt.UserList[%d]", i) + } + } + return nil } // Accept implements Node Accept interface. diff --git a/parser/auth/auth.go b/parser/auth/auth.go index 1d33fbdc01e37..fe930bad3d8ac 100644 --- a/parser/auth/auth.go +++ b/parser/auth/auth.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/pingcap/errors" + . "github.com/pingcap/parser/format" "github.com/pingcap/parser/terror" ) @@ -32,6 +33,20 @@ type UserIdentity struct { AuthHostname string // Match in privs system (i.e. could be a wildcard) } +// Restore implements Node interface. +func (user *UserIdentity) Restore(ctx *RestoreCtx) error { + if user.CurrentUser { + ctx.WriteKeyWord("CURRENT_USER") + } else { + ctx.WriteName(user.Username) + if user.Hostname != "" { + ctx.WritePlain("@") + ctx.WriteName(user.Hostname) + } + } + return nil +} + // String converts UserIdentity to the format user@host. func (user *UserIdentity) String() string { // TODO: Escape username and hostname. diff --git a/parser/parser_test.go b/parser/parser_test.go index e7e227b9fbeea..54b012a43ef86 100755 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -634,7 +634,7 @@ func (s *testParserSuite) TestDBAStmt(c *C) { {`SHOW FULL TABLES FROM icar_qa LIKE play_evolutions`, true, "SHOW FULL TABLES IN `icar_qa` LIKE `play_evolutions`"}, {`SHOW FULL TABLES WHERE Table_Type != 'VIEW'`, true, "SHOW FULL TABLES WHERE `Table_Type`!='VIEW'"}, {`SHOW GRANTS`, true, "SHOW GRANTS"}, - {`SHOW GRANTS FOR 'test'@'localhost'`, true, "SHOW GRANTS FOR 'test'@'localhost'"}, + {`SHOW GRANTS FOR 'test'@'localhost'`, true, "SHOW GRANTS FOR `test`@`localhost`"}, {`SHOW GRANTS FOR current_user()`, true, "SHOW GRANTS FOR CURRENT_USER"}, {`SHOW GRANTS FOR current_user`, true, "SHOW GRANTS FOR CURRENT_USER"}, {`SHOW COLUMNS FROM City;`, true, "SHOW COLUMNS IN `City`"}, @@ -2058,40 +2058,40 @@ func (s *testParserSuite) TestType(c *C) { func (s *testParserSuite) TestPrivilege(c *C) { table := []testCase{ // for create user - {`CREATE USER 'test'`, true, ""}, - {`CREATE USER test`, true, ""}, - {"CREATE USER `test`", true, ""}, + {`CREATE USER 'test'`, true, "CREATE USER `test`@`%`"}, + {`CREATE USER test`, true, "CREATE USER `test`@`%`"}, + {"CREATE USER `test`", true, "CREATE USER `test`@`%`"}, {"CREATE USER test-user", false, ""}, {"CREATE USER test.user", false, ""}, - {"CREATE USER 'test-user'", true, ""}, - {"CREATE USER `test-user`", true, ""}, + {"CREATE USER 'test-user'", true, "CREATE USER `test-user`@`%`"}, + {"CREATE USER `test-user`", true, "CREATE USER `test-user`@`%`"}, {"CREATE USER test.user", false, ""}, - {"CREATE USER 'test.user'", true, ""}, - {"CREATE USER `test.user`", true, ""}, - {"CREATE USER uesr1@localhost", true, ""}, - {"CREATE USER `uesr1`@localhost", true, ""}, - {"CREATE USER uesr1@`localhost`", true, ""}, - {"CREATE USER `uesr1`@`localhost`", true, ""}, - {"CREATE USER 'uesr1'@localhost", true, ""}, - {"CREATE USER uesr1@'localhost'", true, ""}, - {"CREATE USER 'uesr1'@'localhost'", true, ""}, - {"CREATE USER 'uesr1'@`localhost`", true, ""}, - {"CREATE USER `uesr1`@'localhost'", true, ""}, - {"create user 'bug19354014user'@'%' identified WITH mysql_native_password", true, ""}, - {"create user 'bug19354014user'@'%' identified WITH mysql_native_password by 'new-password'", true, ""}, - {"create user 'bug19354014user'@'%' identified WITH mysql_native_password as 'hashstring'", true, ""}, - {`CREATE USER IF NOT EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, ""}, - {`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, ""}, - {`ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, ""}, - {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, ""}, - {`ALTER USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, ""}, - {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, ""}, - {`ALTER USER USER() IDENTIFIED BY 'new-password'`, true, ""}, - {`ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'`, true, ""}, - {`DROP USER 'root'@'localhost', 'root1'@'localhost'`, true, ""}, - {`DROP USER IF EXISTS 'root'@'localhost'`, true, ""}, + {"CREATE USER 'test.user'", true, "CREATE USER `test.user`@`%`"}, + {"CREATE USER `test.user`", true, "CREATE USER `test.user`@`%`"}, + {"CREATE USER uesr1@localhost", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER `uesr1`@localhost", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER uesr1@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER `uesr1`@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER 'uesr1'@localhost", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER uesr1@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER 'uesr1'@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER 'uesr1'@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER `uesr1`@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, + {"create user 'bug19354014user'@'%' identified WITH mysql_native_password", true, "CREATE USER `bug19354014user`@`%`"}, + {"create user 'bug19354014user'@'%' identified WITH mysql_native_password by 'new-password'", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED BY 'new-password'"}, + {"create user 'bug19354014user'@'%' identified WITH mysql_native_password as 'hashstring'", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED BY PASSWORD 'hashstring'"}, + {`CREATE USER IF NOT EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "CREATE USER IF NOT EXISTS `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`CREATE USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY PASSWORD 'hashstring'"}, + {`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY 'new-password', `root`@`127.0.0.1` IDENTIFIED BY PASSWORD 'hashstring'"}, + {`ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "ALTER USER IF EXISTS `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`ALTER USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY PASSWORD 'hashstring'"}, + {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY 'new-password', `root`@`127.0.0.1` IDENTIFIED BY PASSWORD 'hashstring'"}, + {`ALTER USER USER() IDENTIFIED BY 'new-password'`, true, "ALTER USER USER() IDENTIFIED BY 'new-password'"}, + {`ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'`, true, "ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'"}, + {`DROP USER 'root'@'localhost', 'root1'@'localhost'`, true, "DROP USER `root`@`localhost`, `root1`@`localhost`"}, + {`DROP USER IF EXISTS 'root'@'localhost'`, true, "DROP USER IF EXISTS `root`@`localhost`"}, // for grant statement {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost';", true, ""},