github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/drop_role.go (about) 1 // Copyright 2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plan 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/mysql_db" 23 "github.com/dolthub/go-mysql-server/sql/types" 24 ) 25 26 // DropRole represents the statement DROP ROLE. 27 type DropRole struct { 28 IfExists bool 29 Roles []UserName 30 MySQLDb sql.Database 31 } 32 33 // NewDropRole returns a new DropRole node. 34 func NewDropRole(ifExists bool, roles []UserName) *DropRole { 35 return &DropRole{ 36 IfExists: ifExists, 37 Roles: roles, 38 MySQLDb: sql.UnresolvedDatabase("mysql"), 39 } 40 } 41 42 var _ sql.Node = (*DropRole)(nil) 43 var _ sql.Databaser = (*DropRole)(nil) 44 var _ sql.CollationCoercible = (*DropRole)(nil) 45 46 // Schema implements the interface sql.Node. 47 func (n *DropRole) Schema() sql.Schema { 48 return types.OkResultSchema 49 } 50 51 // String implements the interface sql.Node. 52 func (n *DropRole) String() string { 53 roles := make([]string, len(n.Roles)) 54 for i, role := range n.Roles { 55 roles[i] = role.String("") 56 } 57 ifExists := "" 58 if n.IfExists { 59 ifExists = "IfExists: " 60 } 61 return fmt.Sprintf("DropRole(%s%s)", ifExists, strings.Join(roles, ", ")) 62 } 63 64 // Database implements the interface sql.Databaser. 65 func (n *DropRole) Database() sql.Database { 66 return n.MySQLDb 67 } 68 69 // WithDatabase implements the interface sql.Databaser. 70 func (n *DropRole) WithDatabase(db sql.Database) (sql.Node, error) { 71 nn := *n 72 nn.MySQLDb = db 73 return &nn, nil 74 } 75 76 // Resolved implements the interface sql.Node. 77 func (n *DropRole) Resolved() bool { 78 _, ok := n.MySQLDb.(sql.UnresolvedDatabase) 79 return !ok 80 } 81 82 func (n *DropRole) IsReadOnly() bool { 83 return false 84 } 85 86 // Children implements the interface sql.Node. 87 func (n *DropRole) Children() []sql.Node { 88 return nil 89 } 90 91 // WithChildren implements the interface sql.Node. 92 func (n *DropRole) WithChildren(children ...sql.Node) (sql.Node, error) { 93 if len(children) != 0 { 94 return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0) 95 } 96 return n, nil 97 } 98 99 // CheckPrivileges implements the interface sql.Node. 100 func (n *DropRole) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { 101 subject := sql.PrivilegeCheckSubject{} 102 // Both DROP ROLE and CREATE USER are valid privileges, so we use an OR 103 return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_DropRole)) || 104 opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_CreateUser)) 105 } 106 107 // CollationCoercibility implements the interface sql.CollationCoercible. 108 func (*DropRole) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 109 return sql.Collation_binary, 7 110 } 111 112 // RowIter implements the interface sql.Node. 113 func (n *DropRole) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) { 114 mysqlDb, ok := n.MySQLDb.(*mysql_db.MySQLDb) 115 if !ok { 116 return nil, sql.ErrDatabaseNotFound.New("mysql") 117 } 118 119 editor := mysqlDb.Editor() 120 defer editor.Close() 121 122 for _, role := range n.Roles { 123 userPk := mysql_db.UserPrimaryKey{ 124 Host: role.Host, 125 User: role.Name, 126 } 127 if role.AnyHost { 128 userPk.Host = "%" 129 } 130 existingUser, ok := editor.GetUser(userPk) 131 if !ok { 132 if n.IfExists { 133 continue 134 } 135 return nil, sql.ErrRoleDeletionFailure.New(role.String("'")) 136 } 137 138 //TODO: if a role is mentioned in the "mandatory_roles" system variable then they cannot be dropped 139 editor.RemoveUser(userPk) 140 editor.RemoveRoleEdgesFromKey(mysql_db.RoleEdgesFromKey{ 141 FromHost: existingUser.Host, 142 FromUser: existingUser.User, 143 }) 144 editor.RemoveRoleEdgesToKey(mysql_db.RoleEdgesToKey{ 145 ToHost: existingUser.Host, 146 ToUser: existingUser.User, 147 }) 148 } 149 if err := mysqlDb.Persist(ctx, editor); err != nil { 150 return nil, err 151 } 152 return sql.RowsToRowIter(sql.Row{types.NewOkResult(0)}), nil 153 }