github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/delete.go (about) 1 // Copyright 2020-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 20 "gopkg.in/src-d/go-errors.v1" 21 22 "github.com/dolthub/go-mysql-server/sql" 23 ) 24 25 var ErrDeleteFromNotSupported = errors.NewKind("table doesn't support DELETE FROM") 26 27 // DeleteFrom is a node describing a deletion from some table. 28 type DeleteFrom struct { 29 UnaryNode 30 // targets are the explicitly specified table nodes from which rows should be deleted. For simple DELETES against a 31 // single source table, targets do NOT need to be explicitly specified and will not be set here. For DELETE FROM JOIN 32 // statements, targets MUST be explicitly specified by the user and will be populated here. 33 explicitTargets []sql.Node 34 } 35 36 var _ sql.Databaseable = (*DeleteFrom)(nil) 37 var _ sql.Node = (*DeleteFrom)(nil) 38 var _ sql.CollationCoercible = (*DeleteFrom)(nil) 39 40 // NewDeleteFrom creates a DeleteFrom node. 41 func NewDeleteFrom(n sql.Node, targets []sql.Node) *DeleteFrom { 42 return &DeleteFrom{ 43 UnaryNode: UnaryNode{n}, 44 explicitTargets: targets, 45 } 46 } 47 48 // HasExplicitTargets returns true if the target delete tables were explicitly specified. This can only happen with 49 // DELETE FROM JOIN statements – for DELETE FROM statements using a single source table, the target is NOT explicitly 50 // specified and is assumed to be the single source table. 51 func (p *DeleteFrom) HasExplicitTargets() bool { 52 return len(p.explicitTargets) > 0 53 } 54 55 // WithExplicitTargets returns a new DeleteFrom node instance with the specified |targets| set as the explicitly 56 // specified targets of the delete operation. 57 func (p *DeleteFrom) WithExplicitTargets(targets []sql.Node) *DeleteFrom { 58 copy := *p 59 copy.explicitTargets = targets 60 return © 61 } 62 63 // GetDeleteTargets returns the sql.Nodes representing the tables from which rows should be deleted. For a DELETE FROM 64 // JOIN statement, this will return the tables explicitly specified by the caller. For a DELETE FROM statement this will 65 // return the single table in the DELETE FROM source that is implicitly assumed to be the target of the delete operation. 66 func (p *DeleteFrom) GetDeleteTargets() []sql.Node { 67 if len(p.explicitTargets) == 0 { 68 return []sql.Node{p.Child} 69 } else { 70 return p.explicitTargets 71 } 72 } 73 74 // Resolved implements the sql.Resolvable interface. 75 func (p *DeleteFrom) Resolved() bool { 76 if p.Child.Resolved() == false { 77 return false 78 } 79 80 for _, target := range p.explicitTargets { 81 if target.Resolved() == false { 82 return false 83 } 84 } 85 86 return true 87 } 88 89 func (p *DeleteFrom) IsReadOnly() bool { 90 return false 91 } 92 93 // DB returns the database being deleted from. |Database| is used by another interface we implement. 94 func (p *DeleteFrom) DB() sql.Database { 95 return GetDatabase(p.Child) 96 } 97 98 func (p *DeleteFrom) Database() string { 99 database := GetDatabase(p.Child) 100 if database == nil { 101 return "" 102 } 103 return database.Name() 104 } 105 106 // WithChildren implements the Node interface. 107 func (p *DeleteFrom) WithChildren(children ...sql.Node) (sql.Node, error) { 108 if len(children) != 1 { 109 return nil, sql.ErrInvalidChildrenNumber.New(p, len(children), 1) 110 } 111 return NewDeleteFrom(children[0], p.explicitTargets), nil 112 } 113 114 // CheckPrivileges implements the interface sql.Node. 115 func (p *DeleteFrom) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { 116 // TODO: If column values are retrieved then the SELECT privilege is required 117 // For example: "DELETE FROM table WHERE z > 0" 118 // We would need SELECT privileges on the "z" column as it's retrieving values 119 120 for _, target := range p.GetDeleteTargets() { 121 deletable, err := GetDeletable(target) 122 if err != nil { 123 ctx.GetLogger().Warnf("unable to determine deletable table from delete target: %v", target) 124 return false 125 } 126 127 subject := sql.PrivilegeCheckSubject{ 128 Database: CheckPrivilegeNameForDatabase(GetDatabase(target)), 129 Table: deletable.Name(), 130 } 131 op := sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Delete) 132 if opChecker.UserHasPrivileges(ctx, op) == false { 133 return false 134 } 135 } 136 137 return true 138 } 139 140 func GetDeletable(node sql.Node) (sql.DeletableTable, error) { 141 switch node := node.(type) { 142 case sql.DeletableTable: 143 return node, nil 144 case *IndexedTableAccess: 145 return GetDeletable(node.TableNode) 146 case *ResolvedTable: 147 return getDeletableTable(node.Table) 148 case *TableAlias: 149 return GetDeletable(node.Child) 150 case *SubqueryAlias: 151 return nil, ErrDeleteFromNotSupported.New() 152 case *TriggerExecutor: 153 return GetDeletable(node.Left()) 154 case sql.TableWrapper: 155 return getDeletableTable(node.Underlying()) 156 case *JSONTable: 157 return nil, fmt.Errorf("target table %s of the DELETE is not updatable", node.Name()) 158 } 159 if len(node.Children()) > 1 { 160 return nil, ErrDeleteFromNotSupported.New() 161 } 162 for _, child := range node.Children() { 163 deleter, _ := GetDeletable(child) 164 if deleter != nil { 165 return deleter, nil 166 } 167 } 168 return nil, ErrDeleteFromNotSupported.New() 169 } 170 171 func getDeletableTable(t sql.Table) (sql.DeletableTable, error) { 172 switch t := t.(type) { 173 case sql.DeletableTable: 174 return t, nil 175 case sql.TableWrapper: 176 return getDeletableTable(t.Underlying()) 177 default: 178 return nil, ErrDeleteFromNotSupported.New() 179 } 180 } 181 182 // CollationCoercibility implements the interface sql.CollationCoercible. 183 func (*DeleteFrom) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 184 return sql.Collation_binary, 7 185 } 186 187 func (p *DeleteFrom) String() string { 188 pr := sql.NewTreePrinter() 189 _ = pr.WriteNode("Delete") 190 _ = pr.WriteChildren(p.Child.String()) 191 return pr.String() 192 } 193 194 func (p *DeleteFrom) DebugString() string { 195 pr := sql.NewTreePrinter() 196 _ = pr.WriteNode("Delete") 197 _ = pr.WriteChildren(sql.DebugString(p.Child)) 198 return pr.String() 199 }