// Copyright 2015 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. package ast import ( "bytes" "fmt" "strconv" "strings" "github.com/pingcap/errors" "github.com/pingcap/parser/auth" . "github.com/pingcap/parser/format" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" ) var ( _ StmtNode = &AdminStmt{} _ StmtNode = &AlterUserStmt{} _ StmtNode = &BeginStmt{} _ StmtNode = &BinlogStmt{} _ StmtNode = &CommitStmt{} _ StmtNode = &CreateUserStmt{} _ StmtNode = &DeallocateStmt{} _ StmtNode = &DoStmt{} _ StmtNode = &ExecuteStmt{} _ StmtNode = &ExplainStmt{} _ StmtNode = &GrantStmt{} _ StmtNode = &PrepareStmt{} _ StmtNode = &RollbackStmt{} _ StmtNode = &SetPwdStmt{} _ StmtNode = &SetRoleStmt{} _ StmtNode = &SetDefaultRoleStmt{} _ StmtNode = &SetStmt{} _ StmtNode = &UseStmt{} _ StmtNode = &FlushStmt{} _ StmtNode = &KillStmt{} _ StmtNode = &CreateBindingStmt{} _ StmtNode = &DropBindingStmt{} _ StmtNode = &ShutdownStmt{} _ Node = &PrivElem{} _ Node = &VariableAssignment{} ) // Isolation level constants. const ( ReadCommitted = "READ-COMMITTED" ReadUncommitted = "READ-UNCOMMITTED" Serializable = "SERIALIZABLE" RepeatableRead = "REPEATABLE-READ" // Valid formats for explain statement. ExplainFormatROW = "row" ExplainFormatDOT = "dot" PumpType = "PUMP" DrainerType = "DRAINER" ) // Transaction mode constants. const ( Optimistic = "OPTIMISTIC" Pessimistic = "PESSIMISTIC" ) var ( // ExplainFormats stores the valid formats for explain statement, used by validator. ExplainFormats = []string{ ExplainFormatROW, ExplainFormatDOT, } ) // TypeOpt is used for parsing data type option from SQL. type TypeOpt struct { IsUnsigned bool IsZerofill bool } // FloatOpt is used for parsing floating-point type option from SQL. // See http://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html type FloatOpt struct { Flen int Decimal int } // AuthOption is used for parsing create use statement. type AuthOption struct { // ByAuthString set as true, if AuthString is used for authorization. Otherwise, authorization is done by HashString. ByAuthString bool AuthString string HashString string // 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 Stmt StmtNode Format string } // Restore implements Node interface. func (n *TraceStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("TRACE ") if n.Format != "json" { ctx.WriteKeyWord("FORMAT") ctx.WritePlain(" = ") ctx.WriteString(n.Format) ctx.WritePlain(" ") } if err := n.Stmt.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore TraceStmt.Stmt") } return nil } // Accept implements Node Accept interface. func (n *TraceStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*TraceStmt) node, ok := n.Stmt.Accept(v) if !ok { return n, false } n.Stmt = node.(StmtNode) return v.Leave(n) } // ExplainForStmt is a statement to provite information about how is SQL statement executeing // in connection #ConnectionID // See https://dev.mysql.com/doc/refman/5.7/en/explain.html type ExplainForStmt struct { stmtNode Format string ConnectionID uint64 } // Restore implements Node interface. func (n *ExplainForStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("EXPLAIN ") ctx.WriteKeyWord("FORMAT ") ctx.WritePlain("= ") ctx.WriteString(n.Format) ctx.WritePlain(" ") ctx.WriteKeyWord("FOR ") ctx.WriteKeyWord("CONNECTION ") ctx.WritePlain(strconv.FormatUint(n.ConnectionID, 10)) return nil } // Accept implements Node Accept interface. func (n *ExplainForStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*ExplainForStmt) return v.Leave(n) } // ExplainStmt is a statement to provide information about how is SQL statement executed // or get columns information in a table. // See https://dev.mysql.com/doc/refman/5.7/en/explain.html type ExplainStmt struct { stmtNode Stmt StmtNode Format string Analyze bool } // Restore implements Node interface. func (n *ExplainStmt) Restore(ctx *RestoreCtx) error { if showStmt, ok := n.Stmt.(*ShowStmt); ok { ctx.WriteKeyWord("DESC ") if err := showStmt.Table.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore ExplainStmt.ShowStmt.Table") } if showStmt.Column != nil { ctx.WritePlain(" ") if err := showStmt.Column.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore ExplainStmt.ShowStmt.Column") } } return nil } ctx.WriteKeyWord("EXPLAIN ") if n.Analyze { ctx.WriteKeyWord("ANALYZE ") } else { ctx.WriteKeyWord("FORMAT ") ctx.WritePlain("= ") ctx.WriteString(n.Format) ctx.WritePlain(" ") } if err := n.Stmt.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore ExplainStmt.Stmt") } return nil } // Accept implements Node Accept interface. func (n *ExplainStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*ExplainStmt) node, ok := n.Stmt.Accept(v) if !ok { return n, false } n.Stmt = node.(DMLNode) return v.Leave(n) } // PrepareStmt is a statement to prepares a SQL statement which contains placeholders, // and it is executed with ExecuteStmt and released with DeallocateStmt. // See https://dev.mysql.com/doc/refman/5.7/en/prepare.html type PrepareStmt struct { stmtNode Name string SQLText string SQLVar *VariableExpr } // Restore implements Node interface. func (n *PrepareStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("PREPARE ") ctx.WriteName(n.Name) ctx.WriteKeyWord(" FROM ") if n.SQLText != "" { ctx.WriteString(n.SQLText) return nil } if n.SQLVar != nil { if err := n.SQLVar.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore PrepareStmt.SQLVar") } return nil } return errors.New("An error occurred while restore PrepareStmt") } // Accept implements Node Accept interface. func (n *PrepareStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*PrepareStmt) if n.SQLVar != nil { node, ok := n.SQLVar.Accept(v) if !ok { return n, false } n.SQLVar = node.(*VariableExpr) } return v.Leave(n) } // DeallocateStmt is a statement to release PreparedStmt. // See https://dev.mysql.com/doc/refman/5.7/en/deallocate-prepare.html type DeallocateStmt struct { stmtNode Name string } // Restore implements Node interface. func (n *DeallocateStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("DEALLOCATE PREPARE ") ctx.WriteName(n.Name) return nil } // Accept implements Node Accept interface. func (n *DeallocateStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*DeallocateStmt) return v.Leave(n) } // Prepared represents a prepared statement. type Prepared struct { Stmt StmtNode StmtType string Params []ParamMarkerExpr SchemaVersion int64 UseCache bool CachedPlan interface{} } // ExecuteStmt is a statement to execute PreparedStmt. // See https://dev.mysql.com/doc/refman/5.7/en/execute.html type ExecuteStmt struct { stmtNode Name string UsingVars []ExprNode BinaryArgs interface{} ExecID uint32 IdxInMulti int } // Restore implements Node interface. func (n *ExecuteStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("EXECUTE ") ctx.WriteName(n.Name) if len(n.UsingVars) > 0 { ctx.WriteKeyWord(" USING ") for i, val := range n.UsingVars { if i != 0 { ctx.WritePlain(",") } if err := val.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore ExecuteStmt.UsingVars index %d", i) } } } return nil } // Accept implements Node Accept interface. func (n *ExecuteStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*ExecuteStmt) for i, val := range n.UsingVars { node, ok := val.Accept(v) if !ok { return n, false } n.UsingVars[i] = node.(ExprNode) } return v.Leave(n) } // BeginStmt is a statement to start a new transaction. // See https://dev.mysql.com/doc/refman/5.7/en/commit.html type BeginStmt struct { stmtNode Mode string } // Restore implements Node interface. func (n *BeginStmt) Restore(ctx *RestoreCtx) error { if n.Mode == "" { ctx.WriteKeyWord("START TRANSACTION") } else { ctx.WriteKeyWord("BEGIN ") ctx.WriteKeyWord(n.Mode) } return nil } // Accept implements Node Accept interface. func (n *BeginStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*BeginStmt) return v.Leave(n) } // BinlogStmt is an internal-use statement. // We just parse and ignore it. // See http://dev.mysql.com/doc/refman/5.7/en/binlog.html type BinlogStmt struct { stmtNode Str string } // Restore implements Node interface. func (n *BinlogStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("BINLOG ") ctx.WriteString(n.Str) return nil } // Accept implements Node Accept interface. func (n *BinlogStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*BinlogStmt) return v.Leave(n) } // CommitStmt is a statement to commit the current transaction. // See https://dev.mysql.com/doc/refman/5.7/en/commit.html type CommitStmt struct { stmtNode } // Restore implements Node interface. func (n *CommitStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("COMMIT") return nil } // Accept implements Node Accept interface. func (n *CommitStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*CommitStmt) return v.Leave(n) } // RollbackStmt is a statement to roll back the current transaction. // See https://dev.mysql.com/doc/refman/5.7/en/commit.html type RollbackStmt struct { stmtNode } // Restore implements Node interface. func (n *RollbackStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("ROLLBACK") return nil } // Accept implements Node Accept interface. func (n *RollbackStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*RollbackStmt) return v.Leave(n) } // UseStmt is a statement to use the DBName database as the current database. // See https://dev.mysql.com/doc/refman/5.7/en/use.html type UseStmt struct { stmtNode DBName string } // Restore implements Node interface. func (n *UseStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("USE ") ctx.WriteName(n.DBName) return nil } // Accept implements Node Accept interface. func (n *UseStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*UseStmt) return v.Leave(n) } const ( // SetNames is the const for set names/charset stmt. // If VariableAssignment.Name == Names, it should be set names/charset stmt. SetNames = "SetNAMES" ) // VariableAssignment is a variable assignment struct. type VariableAssignment struct { node Name string Value ExprNode IsGlobal bool IsSystem bool // ExtendValue is a way to store extended info. // VariableAssignment should be able to store information for SetCharset/SetPWD Stmt. // For SetCharsetStmt, Value is charset, ExtendValue is collation. // TODO: Use SetStmt to implement set password statement. ExtendValue ValueExpr } // Restore implements Node interface. func (n *VariableAssignment) Restore(ctx *RestoreCtx) error { if n.IsSystem { ctx.WritePlain("@@") if n.IsGlobal { ctx.WriteKeyWord("GLOBAL") } else { ctx.WriteKeyWord("SESSION") } ctx.WritePlain(".") } else if n.Name != SetNames { ctx.WriteKeyWord("@") } if n.Name == SetNames { ctx.WriteKeyWord("NAMES ") } else { ctx.WriteName(n.Name) ctx.WritePlain("=") } if err := n.Value.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore VariableAssignment.Value") } if n.ExtendValue != nil { ctx.WriteKeyWord(" COLLATE ") if err := n.ExtendValue.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore VariableAssignment.ExtendValue") } } return nil } // Accept implements Node interface. func (n *VariableAssignment) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*VariableAssignment) node, ok := n.Value.Accept(v) if !ok { return n, false } n.Value = node.(ExprNode) return v.Leave(n) } // FlushStmtType is the type for FLUSH statement. type FlushStmtType int // Flush statement types. const ( FlushNone FlushStmtType = iota FlushTables FlushPrivileges FlushStatus FlushTiDBPlugin ) // FlushStmt is a statement to flush tables/privileges/optimizer costs and so on. type FlushStmt struct { stmtNode Tp FlushStmtType // Privileges/Tables/... NoWriteToBinLog bool Tables []*TableName // For FlushTableStmt, if Tables is empty, it means flush all tables. ReadLock bool Plugins []string } // Restore implements Node interface. func (n *FlushStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("FLUSH ") if n.NoWriteToBinLog { ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") } switch n.Tp { case FlushTables: ctx.WriteKeyWord("TABLES") for i, v := range n.Tables { if i == 0 { ctx.WritePlain(" ") } else { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore FlushStmt.Tables[%d]", i) } } if n.ReadLock { ctx.WriteKeyWord(" WITH READ LOCK") } case FlushPrivileges: ctx.WriteKeyWord("PRIVILEGES") case FlushStatus: ctx.WriteKeyWord("STATUS") case FlushTiDBPlugin: ctx.WriteKeyWord("TIDB PLUGINS") for i, v := range n.Plugins { if i == 0 { ctx.WritePlain(" ") } else { ctx.WritePlain(", ") } ctx.WritePlain(v) } default: return errors.New("Unsupported type of FlushTables") } return nil } // Accept implements Node Accept interface. func (n *FlushStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*FlushStmt) return v.Leave(n) } // KillStmt is a statement to kill a query or connection. type KillStmt struct { stmtNode // Query indicates whether terminate a single query on this connection or the whole connection. // If Query is true, terminates the statement the connection is currently executing, but leaves the connection itself intact. // If Query is false, terminates the connection associated with the given ConnectionID, after terminating any statement the connection is executing. Query bool ConnectionID uint64 // TiDBExtension is used to indicate whether the user knows he is sending kill statement to the right tidb-server. // When the SQL grammar is "KILL TIDB [CONNECTION | QUERY] connectionID", TiDBExtension will be set. // It's a special grammar extension in TiDB. This extension exists because, when the connection is: // client -> LVS proxy -> TiDB, and type Ctrl+C in client, the following action will be executed: // new a connection; kill xxx; // kill command may send to the wrong TiDB, because the exists of LVS proxy, and kill the wrong session. // So, "KILL TIDB" grammar is introduced, and it REQUIRES DIRECT client -> TiDB TOPOLOGY. // TODO: The standard KILL grammar will be supported once we have global connectionID. TiDBExtension bool } // Restore implements Node interface. func (n *KillStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("KILL") if n.TiDBExtension { ctx.WriteKeyWord(" TIDB") } if n.Query { ctx.WriteKeyWord(" QUERY") } ctx.WritePlainf(" %d", n.ConnectionID) return nil } // Accept implements Node Accept interface. func (n *KillStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*KillStmt) return v.Leave(n) } // SetStmt is the statement to set variables. type SetStmt struct { stmtNode // Variables is the list of variable assignment. Variables []*VariableAssignment } // Restore implements Node interface. func (n *SetStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("SET ") for i, v := range n.Variables { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore SetStmt.Variables[%d]", i) } } return nil } // Accept implements Node Accept interface. func (n *SetStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*SetStmt) for i, val := range n.Variables { node, ok := val.Accept(v) if !ok { return n, false } n.Variables[i] = node.(*VariableAssignment) } return v.Leave(n) } /* // SetCharsetStmt is a statement to assign values to character and collation variables. // See https://dev.mysql.com/doc/refman/5.7/en/set-statement.html type SetCharsetStmt struct { stmtNode Charset string Collate string } // Accept implements Node Accept interface. func (n *SetCharsetStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*SetCharsetStmt) return v.Leave(n) } */ // SetPwdStmt is a statement to assign a password to user account. // See https://dev.mysql.com/doc/refman/5.7/en/set-password.html type SetPwdStmt struct { stmtNode User *auth.UserIdentity Password string } // Restore implements Node interface. func (n *SetPwdStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("SET PASSWORD") if n.User != nil { ctx.WriteKeyWord(" FOR ") if err := n.User.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore SetPwdStmt.User") } } ctx.WritePlain("=") ctx.WriteString(n.Password) return nil } // SecureText implements SensitiveStatement interface. func (n *SetPwdStmt) SecureText() string { return fmt.Sprintf("set password for user %s", n.User) } // Accept implements Node Accept interface. func (n *SetPwdStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*SetPwdStmt) return v.Leave(n) } type ChangeStmt struct { stmtNode NodeType string State string NodeID string } // Restore implements Node interface. func (n *ChangeStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("CHANGE ") ctx.WriteKeyWord(n.NodeType) ctx.WriteKeyWord(" TO NODE_STATE ") ctx.WritePlain("=") ctx.WriteString(n.State) ctx.WriteKeyWord(" FOR NODE_ID ") ctx.WriteString(n.NodeID) return nil } // SecureText implements SensitiveStatement interface. func (n *ChangeStmt) SecureText() string { return fmt.Sprintf("change %s to node_state='%s' for node_id '%s'", strings.ToLower(n.NodeType), n.State, n.NodeID) } // Accept implements Node Accept interface. func (n *ChangeStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*ChangeStmt) return v.Leave(n) } // SetRoleStmtType is the type for FLUSH statement. type SetRoleStmtType int // SetRole statement types. const ( SetRoleDefault SetRoleStmtType = iota SetRoleNone SetRoleAll SetRoleAllExcept SetRoleRegular ) type SetRoleStmt struct { stmtNode SetRoleOpt SetRoleStmtType RoleList []*auth.RoleIdentity } func (n *SetRoleStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("SET ROLE") switch n.SetRoleOpt { case SetRoleDefault: ctx.WriteKeyWord(" DEFAULT") case SetRoleNone: ctx.WriteKeyWord(" NONE") case SetRoleAll: ctx.WriteKeyWord(" ALL") case SetRoleAllExcept: ctx.WriteKeyWord(" ALL EXCEPT") } for i, role := range n.RoleList { ctx.WritePlain(" ") err := role.Restore(ctx) if err != nil { return errors.Annotate(err, "An error occurred while restore SetRoleStmt.RoleList") } if i != len(n.RoleList)-1 { ctx.WritePlain(",") } } return nil } // Accept implements Node Accept interface. func (n *SetRoleStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*SetRoleStmt) return v.Leave(n) } type SetDefaultRoleStmt struct { stmtNode SetRoleOpt SetRoleStmtType RoleList []*auth.RoleIdentity UserList []*auth.UserIdentity } func (n *SetDefaultRoleStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("SET DEFAULT ROLE") switch n.SetRoleOpt { case SetRoleNone: ctx.WriteKeyWord(" NONE") case SetRoleAll: ctx.WriteKeyWord(" ALL") default: } for i, role := range n.RoleList { ctx.WritePlain(" ") err := role.Restore(ctx) if err != nil { return errors.Annotate(err, "An error occurred while restore SetDefaultRoleStmt.RoleList") } if i != len(n.RoleList)-1 { ctx.WritePlain(",") } } ctx.WritePlain(" TO") for i, user := range n.UserList { ctx.WritePlain(" ") err := user.Restore(ctx) if err != nil { return errors.Annotate(err, "An error occurred while restore SetDefaultRoleStmt.UserList") } if i != len(n.UserList)-1 { ctx.WritePlain(",") } } return nil } // Accept implements Node Accept interface. func (n *SetDefaultRoleStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*SetDefaultRoleStmt) return v.Leave(n) } // UserSpec is used for parsing create user statement. type UserSpec struct { User *auth.UserIdentity AuthOpt *AuthOption IsRole bool } // 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 (n *UserSpec) SecurityString() string { withPassword := false if opt := n.AuthOpt; opt != nil { if len(opt.AuthString) > 0 || len(opt.HashString) > 0 { withPassword = true } } if withPassword { return fmt.Sprintf("{%s password = ***}", n.User) } 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 (n *UserSpec) EncodedPassword() (string, bool) { if n.AuthOpt == nil { return "", true } opt := n.AuthOpt if opt.ByAuthString { return auth.EncodePassword(opt.AuthString), true } // Not a legal password string. if len(opt.HashString) != 41 || !strings.HasPrefix(opt.HashString, "*") { return "", false } return opt.HashString, true } const ( TslNone = iota Ssl X509 Cipher Issuer Subject ) type TslOption struct { Type int Value string } func (t *TslOption) Restore(ctx *RestoreCtx) error { switch t.Type { case TslNone: ctx.WriteKeyWord("NONE") case Ssl: ctx.WriteKeyWord("SSL") case X509: ctx.WriteKeyWord("X509") case Cipher: ctx.WriteKeyWord("CIPHER ") ctx.WriteString(t.Value) case Issuer: ctx.WriteKeyWord("ISSUER ") ctx.WriteString(t.Value) case Subject: ctx.WriteKeyWord("CIPHER") ctx.WriteString(t.Value) default: return errors.Errorf("Unsupported TslOption.Type %d", t.Type) } return nil } const ( MaxQueriesPerHour = iota + 1 MaxUpdatesPerHour MaxConnectionsPerHour MaxUserConnections ) type ResourceOption struct { Type int Count int64 } func (r *ResourceOption) Restore(ctx *RestoreCtx) error { switch r.Type { case MaxQueriesPerHour: ctx.WriteKeyWord("MAX_QUERIES_PER_HOUR ") case MaxUpdatesPerHour: ctx.WriteKeyWord("MAX_UPDATES_PER_HOUR ") case MaxConnectionsPerHour: ctx.WriteKeyWord("MAX_CONNECTIONS_PER_HOUR ") case MaxUserConnections: ctx.WriteKeyWord("MAX_USER_CONNECTIONS ") default: return errors.Errorf("Unsupported ResourceOption.Type %d", r.Type) } ctx.WritePlainf("%d", r.Count) return nil } const ( PasswordExpire = iota + 1 PasswordExpireDefault PasswordExpireNever PasswordExpireInterval Lock Unlock ) type PasswordOrLockOption struct { Type int Count int64 } func (p *PasswordOrLockOption) Restore(ctx *RestoreCtx) error { switch p.Type { case PasswordExpire: ctx.WriteKeyWord("PASSWORD EXPIRE") case PasswordExpireDefault: ctx.WriteKeyWord("PASSWORD EXPIRE DEFAULT") case PasswordExpireNever: ctx.WriteKeyWord("PASSWORD EXPIRE NEVER") case PasswordExpireInterval: ctx.WriteKeyWord("PASSWORD EXPIRE INTERVAL") ctx.WritePlainf(" %d", p.Count) ctx.WriteKeyWord(" DAY") case Lock: ctx.WriteKeyWord("ACCOUNT LOCK") case Unlock: ctx.WriteKeyWord("ACCOUNT UNLOCK") default: return errors.Errorf("Unsupported PasswordOrLockOption.Type %d", p.Type) } return nil } // CreateUserStmt creates user account. // See https://dev.mysql.com/doc/refman/5.7/en/create-user.html type CreateUserStmt struct { stmtNode IsCreateRole bool IfNotExists bool Specs []*UserSpec TslOptions []*TslOption ResourceOptions []*ResourceOption PasswordOrLockOptions []*PasswordOrLockOption } // Restore implements Node interface. func (n *CreateUserStmt) Restore(ctx *RestoreCtx) error { if n.IsCreateRole { ctx.WriteKeyWord("CREATE ROLE ") } else { 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) } } tslOptionLen := len(n.TslOptions) if tslOptionLen != 0 { ctx.WriteKeyWord(" REQUIRE ") } // Restore `tslOptions` reversely to keep order the same with original sql for i := tslOptionLen; i > 0; i-- { if i != tslOptionLen { ctx.WriteKeyWord(" AND ") } if err := n.TslOptions[i-1].Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.TslOptions[%d]", i) } } if len(n.ResourceOptions) != 0 { ctx.WriteKeyWord(" WITH") } for i, v := range n.ResourceOptions { ctx.WritePlain(" ") if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.ResourceOptions[%d]", i) } } for i, v := range n.PasswordOrLockOptions { ctx.WritePlain(" ") if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.PasswordOrLockOptions[%d]", i) } } return nil } // Accept implements Node Accept interface. func (n *CreateUserStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*CreateUserStmt) return v.Leave(n) } // SecureText implements SensitiveStatement interface. func (n *CreateUserStmt) SecureText() string { var buf bytes.Buffer buf.WriteString("create user") for _, user := range n.Specs { buf.WriteString(" ") buf.WriteString(user.SecurityString()) } return buf.String() } // AlterUserStmt modifies user account. // See https://dev.mysql.com/doc/refman/5.7/en/alter-user.html type AlterUserStmt struct { stmtNode IfExists bool CurrentAuth *AuthOption Specs []*UserSpec TslOptions []*TslOption ResourceOptions []*ResourceOption PasswordOrLockOptions []*PasswordOrLockOption } // Restore implements Node interface. func (n *AlterUserStmt) Restore(ctx *RestoreCtx) error { 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) } } tslOptionLen := len(n.TslOptions) if tslOptionLen != 0 { ctx.WriteKeyWord(" REQUIRE ") } // Restore `tslOptions` reversely to keep order the same with original sql for i := tslOptionLen; i > 0; i-- { if i != tslOptionLen { ctx.WriteKeyWord(" AND ") } if err := n.TslOptions[i-1].Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.TslOptions[%d]", i) } } if len(n.ResourceOptions) != 0 { ctx.WriteKeyWord(" WITH") } for i, v := range n.ResourceOptions { ctx.WritePlain(" ") if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.ResourceOptions[%d]", i) } } for i, v := range n.PasswordOrLockOptions { ctx.WritePlain(" ") if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.PasswordOrLockOptions[%d]", i) } } return nil } // SecureText implements SensitiveStatement interface. func (n *AlterUserStmt) SecureText() string { var buf bytes.Buffer buf.WriteString("alter user") for _, user := range n.Specs { buf.WriteString(" ") buf.WriteString(user.SecurityString()) } return buf.String() } // Accept implements Node Accept interface. func (n *AlterUserStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*AlterUserStmt) 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 IsDropRole bool UserList []*auth.UserIdentity } // Restore implements Node interface. func (n *DropUserStmt) Restore(ctx *RestoreCtx) error { if n.IsDropRole { ctx.WriteKeyWord("DROP ROLE ") } else { 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. 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) } // CreateBindingStmt creates sql binding hint. type CreateBindingStmt struct { stmtNode GlobalScope bool OriginSel StmtNode HintedSel StmtNode } func (n *CreateBindingStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("CREATE ") if n.GlobalScope { ctx.WriteKeyWord("GLOBAL ") } else { ctx.WriteKeyWord("SESSION ") } ctx.WriteKeyWord("BINDING FOR ") if err := n.OriginSel.Restore(ctx); err != nil { return errors.Trace(err) } ctx.WriteKeyWord(" USING ") if err := n.HintedSel.Restore(ctx); err != nil { return errors.Trace(err) } return nil } func (n *CreateBindingStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*CreateBindingStmt) selnode, ok := n.OriginSel.Accept(v) if !ok { return n, false } n.OriginSel = selnode.(*SelectStmt) hintedSelnode, ok := n.HintedSel.Accept(v) if !ok { return n, false } n.HintedSel = hintedSelnode.(*SelectStmt) return v.Leave(n) } // DropBindingStmt deletes sql binding hint. type DropBindingStmt struct { stmtNode GlobalScope bool OriginSel StmtNode } func (n *DropBindingStmt) Restore(ctx *RestoreCtx) error { return errors.New("Not implemented") } func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*DropBindingStmt) selnode, ok := n.OriginSel.Accept(v) if !ok { return n, false } n.OriginSel = selnode.(*SelectStmt) return v.Leave(n) } // DoStmt is the struct for DO statement. type DoStmt struct { stmtNode Exprs []ExprNode } // Restore implements Node interface. func (n *DoStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("DO ") for i, v := range n.Exprs { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore DoStmt.Exprs[%d]", i) } } return nil } // Accept implements Node Accept interface. func (n *DoStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*DoStmt) for i, val := range n.Exprs { node, ok := val.Accept(v) if !ok { return n, false } n.Exprs[i] = node.(ExprNode) } return v.Leave(n) } // AdminStmtType is the type for admin statement. type AdminStmtType int // Admin statement types. const ( AdminShowDDL = iota + 1 AdminCheckTable AdminShowDDLJobs AdminCancelDDLJobs AdminCheckIndex AdminRecoverIndex AdminCleanupIndex AdminCheckIndexRange AdminShowDDLJobQueries AdminChecksumTable AdminShowSlow AdminShowNextRowID AdminReloadExprPushdownBlacklist AdminReloadOptRuleBlacklist AdminPluginDisable AdminPluginEnable ) // HandleRange represents a range where handle value >= Begin and < End. type HandleRange struct { Begin int64 End int64 } // ShowSlowType defines the type for SlowSlow statement. type ShowSlowType int const ( // ShowSlowTop is a ShowSlowType constant. ShowSlowTop ShowSlowType = iota // ShowSlowRecent is a ShowSlowType constant. ShowSlowRecent ) // ShowSlowKind defines the kind for SlowSlow statement when the type is ShowSlowTop. type ShowSlowKind int const ( // ShowSlowKindDefault is a ShowSlowKind constant. ShowSlowKindDefault ShowSlowKind = iota // ShowSlowKindInternal is a ShowSlowKind constant. ShowSlowKindInternal // ShowSlowKindAll is a ShowSlowKind constant. ShowSlowKindAll ) // ShowSlow is used for the following command: // admin show slow top [ internal | all] N // admin show slow recent N type ShowSlow struct { Tp ShowSlowType Count uint64 Kind ShowSlowKind } // Restore implements Node interface. func (n *ShowSlow) Restore(ctx *RestoreCtx) error { switch n.Tp { case ShowSlowRecent: ctx.WriteKeyWord("RECENT ") case ShowSlowTop: ctx.WriteKeyWord("TOP ") switch n.Kind { case ShowSlowKindDefault: // do nothing case ShowSlowKindInternal: ctx.WriteKeyWord("INTERNAL ") case ShowSlowKindAll: ctx.WriteKeyWord("ALL ") default: return errors.New("Unsupported kind of ShowSlowTop") } default: return errors.New("Unsupported type of ShowSlow") } ctx.WritePlainf("%d", n.Count) return nil } // AdminStmt is the struct for Admin statement. type AdminStmt struct { stmtNode Tp AdminStmtType Index string Tables []*TableName JobIDs []int64 JobNumber int64 HandleRanges []HandleRange ShowSlow *ShowSlow Plugins []string Where ExprNode } // Restore implements Node interface. func (n *AdminStmt) Restore(ctx *RestoreCtx) error { restoreTables := func() error { for i, v := range n.Tables { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore AdminStmt.Tables[%d]", i) } } return nil } restoreJobIDs := func() { for i, v := range n.JobIDs { if i != 0 { ctx.WritePlain(", ") } ctx.WritePlainf("%d", v) } } ctx.WriteKeyWord("ADMIN ") switch n.Tp { case AdminShowDDL: ctx.WriteKeyWord("SHOW DDL") case AdminShowDDLJobs: ctx.WriteKeyWord("SHOW DDL JOBS") if n.JobNumber != 0 { ctx.WritePlainf(" %d", n.JobNumber) } if n.Where != nil { ctx.WriteKeyWord(" WHERE ") if err := n.Where.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore ShowStmt.Where") } } case AdminShowNextRowID: ctx.WriteKeyWord("SHOW ") if err := restoreTables(); err != nil { return err } ctx.WriteKeyWord(" NEXT_ROW_ID") case AdminCheckTable: ctx.WriteKeyWord("CHECK TABLE ") if err := restoreTables(); err != nil { return err } case AdminCheckIndex: ctx.WriteKeyWord("CHECK INDEX ") if err := restoreTables(); err != nil { return err } ctx.WritePlainf(" %s", n.Index) case AdminRecoverIndex: ctx.WriteKeyWord("RECOVER INDEX ") if err := restoreTables(); err != nil { return err } ctx.WritePlainf(" %s", n.Index) case AdminCleanupIndex: ctx.WriteKeyWord("CLEANUP INDEX ") if err := restoreTables(); err != nil { return err } ctx.WritePlainf(" %s", n.Index) case AdminCheckIndexRange: ctx.WriteKeyWord("CHECK INDEX ") if err := restoreTables(); err != nil { return err } ctx.WritePlainf(" %s", n.Index) if n.HandleRanges != nil { ctx.WritePlain(" ") for i, v := range n.HandleRanges { if i != 0 { ctx.WritePlain(", ") } ctx.WritePlainf("(%d,%d)", v.Begin, v.End) } } case AdminChecksumTable: ctx.WriteKeyWord("CHECKSUM TABLE ") if err := restoreTables(); err != nil { return err } case AdminCancelDDLJobs: ctx.WriteKeyWord("CANCEL DDL JOBS ") restoreJobIDs() case AdminShowDDLJobQueries: ctx.WriteKeyWord("SHOW DDL JOB QUERIES ") restoreJobIDs() case AdminShowSlow: ctx.WriteKeyWord("SHOW SLOW ") if err := n.ShowSlow.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore AdminStmt.ShowSlow") } case AdminReloadExprPushdownBlacklist: ctx.WriteKeyWord("RELOAD EXPR_PUSHDOWN_BLACKLIST") case AdminReloadOptRuleBlacklist: ctx.WriteKeyWord("RELOAD OPT_RULE_BLACKLIST") case AdminPluginEnable: ctx.WriteKeyWord("PLUGINS ENABLE") for i, v := range n.Plugins { if i == 0 { ctx.WritePlain(" ") } else { ctx.WritePlain(", ") } ctx.WritePlain(v) } case AdminPluginDisable: ctx.WriteKeyWord("PLUGINS DISABLE") for i, v := range n.Plugins { if i == 0 { ctx.WritePlain(" ") } else { ctx.WritePlain(", ") } ctx.WritePlain(v) } default: return errors.New("Unsupported AdminStmt type") } return nil } // Accept implements Node Accept interface. func (n *AdminStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*AdminStmt) for i, val := range n.Tables { node, ok := val.Accept(v) if !ok { return n, false } n.Tables[i] = node.(*TableName) } return v.Leave(n) } // PrivElem is the privilege type and optional column list. type PrivElem struct { node Priv mysql.PrivilegeType Cols []*ColumnName } // Restore implements Node interface. func (n *PrivElem) Restore(ctx *RestoreCtx) error { if n.Priv == 0 { ctx.WritePlain("/* UNSUPPORTED TYPE */") } else if n.Priv == mysql.AllPriv { ctx.WriteKeyWord("ALL") } else { str, ok := mysql.Priv2Str[n.Priv] if ok { ctx.WriteKeyWord(str) } else { return errors.New("Undefined privilege type") } } if n.Cols != nil { ctx.WritePlain(" (") for i, v := range n.Cols { if i != 0 { ctx.WritePlain(",") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore PrivElem.Cols[%d]", i) } } ctx.WritePlain(")") } return nil } // Accept implements Node Accept interface. func (n *PrivElem) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*PrivElem) for i, val := range n.Cols { node, ok := val.Accept(v) if !ok { return n, false } n.Cols[i] = node.(*ColumnName) } return v.Leave(n) } // ObjectTypeType is the type for object type. type ObjectTypeType int const ( // ObjectTypeNone is for empty object type. ObjectTypeNone ObjectTypeType = iota + 1 // ObjectTypeTable means the following object is a table. ObjectTypeTable ) // Restore implements Node interface. func (n ObjectTypeType) Restore(ctx *RestoreCtx) error { switch n { case ObjectTypeNone: // do nothing case ObjectTypeTable: ctx.WriteKeyWord("TABLE") default: return errors.New("Unsupported object type") } return nil } // GrantLevelType is the type for grant level. type GrantLevelType int const ( // GrantLevelNone is the dummy const for default value. GrantLevelNone GrantLevelType = iota + 1 // GrantLevelGlobal means the privileges are administrative or apply to all databases on a given server. GrantLevelGlobal // GrantLevelDB means the privileges apply to all objects in a given database. GrantLevelDB // GrantLevelTable means the privileges apply to all columns in a given table. GrantLevelTable ) // GrantLevel is used for store the privilege scope. type GrantLevel struct { Level GrantLevelType DBName string TableName string } // Restore implements Node interface. func (n *GrantLevel) Restore(ctx *RestoreCtx) error { switch n.Level { case GrantLevelDB: if n.DBName == "" { ctx.WritePlain("*") } else { ctx.WriteName(n.DBName) ctx.WritePlain(".*") } case GrantLevelGlobal: ctx.WritePlain("*.*") case GrantLevelTable: if n.DBName != "" { ctx.WriteName(n.DBName) ctx.WritePlain(".") } ctx.WriteName(n.TableName) } return nil } // RevokeStmt is the struct for REVOKE statement. type RevokeStmt struct { stmtNode Privs []*PrivElem ObjectType ObjectTypeType Level *GrantLevel Users []*UserSpec } // Restore implements Node interface. func (n *RevokeStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("REVOKE ") for i, v := range n.Privs { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore RevokeStmt.Privs[%d]", i) } } ctx.WriteKeyWord(" ON ") if n.ObjectType != ObjectTypeNone { if err := n.ObjectType.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore RevokeStmt.ObjectType") } ctx.WritePlain(" ") } if err := n.Level.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore RevokeStmt.Level") } ctx.WriteKeyWord(" FROM ") for i, v := range n.Users { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore RevokeStmt.Users[%d]", i) } } return nil } // Accept implements Node Accept interface. func (n *RevokeStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*RevokeStmt) for i, val := range n.Privs { node, ok := val.Accept(v) if !ok { return n, false } n.Privs[i] = node.(*PrivElem) } return v.Leave(n) } // RevokeStmt is the struct for REVOKE statement. type RevokeRoleStmt struct { stmtNode Roles []*auth.RoleIdentity Users []*auth.UserIdentity } // Restore implements Node interface. func (n *RevokeRoleStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("REVOKE ") for i, role := range n.Roles { if i != 0 { ctx.WritePlain(", ") } if err := role.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Roles[%d]", i) } } ctx.WriteKeyWord(" FROM ") for i, v := range n.Users { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Users[%d]", i) } } return nil } // Accept implements Node Accept interface. func (n *RevokeRoleStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*RevokeRoleStmt) return v.Leave(n) } // GrantStmt is the struct for GRANT statement. type GrantStmt struct { stmtNode Privs []*PrivElem ObjectType ObjectTypeType Level *GrantLevel Users []*UserSpec WithGrant bool } // Restore implements Node interface. func (n *GrantStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("GRANT ") for i, v := range n.Privs { if i != 0 && v.Priv != 0 { ctx.WritePlain(", ") } else if v.Priv == 0 { ctx.WritePlain(" ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore GrantStmt.Privs[%d]", i) } } ctx.WriteKeyWord(" ON ") if n.ObjectType != ObjectTypeNone { if err := n.ObjectType.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore GrantStmt.ObjectType") } ctx.WritePlain(" ") } if err := n.Level.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore GrantStmt.Level") } ctx.WriteKeyWord(" TO ") for i, v := range n.Users { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) } } if n.WithGrant { ctx.WriteKeyWord(" WITH GRANT OPTION") } return nil } // SecureText implements SensitiveStatement interface. func (n *GrantStmt) SecureText() string { text := n.text // Filter "identified by xxx" because it would expose password information. idx := strings.Index(strings.ToLower(text), "identified") if idx > 0 { text = text[:idx] } return text } // Accept implements Node Accept interface. func (n *GrantStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*GrantStmt) for i, val := range n.Privs { node, ok := val.Accept(v) if !ok { return n, false } n.Privs[i] = node.(*PrivElem) } return v.Leave(n) } // GrantRoleStmt is the struct for GRANT TO statement. type GrantRoleStmt struct { stmtNode Roles []*auth.RoleIdentity Users []*auth.UserIdentity } // Accept implements Node Accept interface. func (n *GrantRoleStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*GrantRoleStmt) return v.Leave(n) } // Restore implements Node interface. func (n *GrantRoleStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("GRANT ") if len(n.Roles) > 0 { for i, role := range n.Roles { if i != 0 { ctx.WritePlain(", ") } if err := role.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore GrantRoleStmt.Roles[%d]", i) } } } ctx.WriteKeyWord(" TO ") for i, v := range n.Users { if i != 0 { ctx.WritePlain(", ") } if err := v.Restore(ctx); err != nil { return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) } } return nil } // SecureText implements SensitiveStatement interface. func (n *GrantRoleStmt) SecureText() string { text := n.text // Filter "identified by xxx" because it would expose password information. idx := strings.Index(strings.ToLower(text), "identified") if idx > 0 { text = text[:idx] } return text } // ShutdownStmt is a statement to stop the TiDB server. // See https://dev.mysql.com/doc/refman/5.7/en/shutdown.html type ShutdownStmt struct { stmtNode } // Restore implements Node interface. func (n *ShutdownStmt) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord("SHUTDOWN") return nil } // Accept implements Node Accept interface. func (n *ShutdownStmt) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*ShutdownStmt) return v.Leave(n) } // Ident is the table identifier composed of schema name and table name. type Ident struct { Schema model.CIStr Name model.CIStr } // String implements fmt.Stringer interface. func (i Ident) String() string { if i.Schema.O == "" { return i.Name.O } return fmt.Sprintf("%s.%s", i.Schema, i.Name) } // SelectStmtOpts wrap around select hints and switches type SelectStmtOpts struct { Distinct bool SQLBigResult bool SQLBufferResult bool SQLCache bool SQLSmallResult bool CalcFoundRows bool StraightJoin bool Priority mysql.PriorityEnum TableHints []*TableOptimizerHint } // TableOptimizerHint is Table level optimizer hint type TableOptimizerHint struct { node // HintName is the name or alias of the table(s) which the hint will affect. // Table hints has no schema info // It allows only table name or alias (if table has an alias) HintName model.CIStr // QBName is the default effective query block of this hint. QBName model.CIStr Tables []HintTable Indexes []model.CIStr StoreType model.CIStr // Statement Execution Time Optimizer Hints // See https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time MaxExecutionTime uint64 MemoryQuota int64 QueryType model.CIStr HintFlag bool } // HintTable is table in the hint. It may have query block info. type HintTable struct { TableName model.CIStr QBName model.CIStr } func (ht *HintTable) Restore(ctx *RestoreCtx) { ctx.WriteName(ht.TableName.String()) if ht.QBName.L != "" { ctx.WriteKeyWord("@") ctx.WriteName(ht.QBName.String()) } } // Restore implements Node interface. func (n *TableOptimizerHint) Restore(ctx *RestoreCtx) error { ctx.WriteKeyWord(n.HintName.String()) ctx.WritePlain("(") if n.QBName.L != "" { if n.HintName.L != "qb_name" { ctx.WriteKeyWord("@") } ctx.WriteName(n.QBName.String()) } // Hints without args except query block. switch n.HintName.L { case "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "no_index_merge", "qb_name": ctx.WritePlain(")") return nil } if n.QBName.L != "" { ctx.WritePlain(" ") } // Hints with args except query block. switch n.HintName.L { case "max_execution_time": ctx.WritePlainf("%d", n.MaxExecutionTime) case "tidb_hj", "tidb_smj", "tidb_inlj", "hash_join", "sm_join", "inl_join": for i, table := range n.Tables { if i != 0 { ctx.WritePlain(", ") } table.Restore(ctx) } case "use_index", "ignore_index", "use_index_merge": n.Tables[0].Restore(ctx) ctx.WritePlain(" ") for i, index := range n.Indexes { if i != 0 { ctx.WritePlain(", ") } ctx.WriteName(index.String()) } case "use_toja", "enable_plan_cache": if n.HintFlag { ctx.WritePlain("TRUE") } else { ctx.WritePlain("FALSE") } case "query_type": ctx.WriteKeyWord(n.QueryType.String()) case "memory_quota": ctx.WritePlainf("%d MB", n.MemoryQuota/1024/1024) case "read_from_storage": ctx.WriteKeyWord(n.StoreType.String()) for i, table := range n.Tables { if i == 0 { ctx.WritePlain("[") } table.Restore(ctx) if i == len(n.Tables)-1 { ctx.WritePlain("]") } else { ctx.WritePlain(", ") } } } ctx.WritePlain(")") return nil } // Accept implements Node Accept interface. func (n *TableOptimizerHint) Accept(v Visitor) (Node, bool) { newNode, skipChildren := v.Enter(n) if skipChildren { return v.Leave(newNode) } n = newNode.(*TableOptimizerHint) return v.Leave(n) } type BinaryLiteral interface { ToString() string } // NewDecimal creates a types.Decimal value, it's provided by parser driver. var NewDecimal func(string) (interface{}, error) // NewHexLiteral creates a types.HexLiteral value, it's provided by parser driver. var NewHexLiteral func(string) (interface{}, error) // NewBitLiteral creates a types.BitLiteral value, it's provided by parser driver. var NewBitLiteral func(string) (interface{}, error)