github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/drop_view.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sql 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/server/telemetry" 17 "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" 18 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" 22 "github.com/cockroachdb/cockroach/pkg/util/log" 23 "github.com/cockroachdb/errors" 24 ) 25 26 type dropViewNode struct { 27 n *tree.DropView 28 td []toDelete 29 } 30 31 // DropView drops a view. 32 // Privileges: DROP on view. 33 // Notes: postgres allows only the view owner to DROP a view. 34 // mysql requires the DROP privilege on the view. 35 func (p *planner) DropView(ctx context.Context, n *tree.DropView) (planNode, error) { 36 td := make([]toDelete, 0, len(n.Names)) 37 for i := range n.Names { 38 tn := &n.Names[i] 39 droppedDesc, err := p.prepareDrop(ctx, tn, !n.IfExists, resolver.ResolveRequireViewDesc) 40 if err != nil { 41 return nil, err 42 } 43 if droppedDesc == nil { 44 // IfExists specified and the view did not exist. 45 continue 46 } 47 48 td = append(td, toDelete{tn, droppedDesc}) 49 } 50 51 // Ensure this view isn't depended on by any other views, or that if it is 52 // then `cascade` was specified or it was also explicitly specified in the 53 // DROP VIEW command. 54 for _, toDel := range td { 55 droppedDesc := toDel.desc 56 for _, ref := range droppedDesc.DependedOnBy { 57 // Don't verify that we can remove a dependent view if that dependent 58 // view was explicitly specified in the DROP VIEW command. 59 if descInSlice(ref.ID, td) { 60 continue 61 } 62 if err := p.canRemoveDependentView(ctx, droppedDesc, ref, n.DropBehavior); err != nil { 63 return nil, err 64 } 65 } 66 } 67 68 if len(td) == 0 { 69 return newZeroNode(nil /* columns */), nil 70 } 71 return &dropViewNode{n: n, td: td}, nil 72 } 73 74 // ReadingOwnWrites implements the planNodeReadingOwnWrites interface. 75 // This is because DROP VIEW performs multiple KV operations on descriptors 76 // and expects to see its own writes. 77 func (n *dropViewNode) ReadingOwnWrites() {} 78 79 func (n *dropViewNode) startExec(params runParams) error { 80 telemetry.Inc(sqltelemetry.SchemaChangeDropCounter("view")) 81 82 ctx := params.ctx 83 for _, toDel := range n.td { 84 droppedDesc := toDel.desc 85 if droppedDesc == nil { 86 continue 87 } 88 89 cascadeDroppedViews, err := params.p.dropViewImpl( 90 ctx, droppedDesc, true /* queueJob */, tree.AsStringWithFQNames(n.n, params.Ann()), n.n.DropBehavior, 91 ) 92 if err != nil { 93 return err 94 } 95 // Log a Drop View event for this table. This is an auditable log event 96 // and is recorded in the same transaction as the table descriptor 97 // update. 98 if err := MakeEventLogger(params.extendedEvalCtx.ExecCfg).InsertEventRecord( 99 ctx, 100 params.p.txn, 101 EventLogDropView, 102 int32(droppedDesc.ID), 103 int32(params.extendedEvalCtx.NodeID.SQLInstanceID()), 104 struct { 105 ViewName string 106 Statement string 107 User string 108 CascadeDroppedViews []string 109 }{toDel.tn.FQString(), n.n.String(), params.SessionData().User, cascadeDroppedViews}, 110 ); err != nil { 111 return err 112 } 113 } 114 return nil 115 } 116 117 func (*dropViewNode) Next(runParams) (bool, error) { return false, nil } 118 func (*dropViewNode) Values() tree.Datums { return tree.Datums{} } 119 func (*dropViewNode) Close(context.Context) {} 120 121 func descInSlice(descID sqlbase.ID, td []toDelete) bool { 122 for _, toDel := range td { 123 if descID == toDel.desc.ID { 124 return true 125 } 126 } 127 return false 128 } 129 130 func (p *planner) canRemoveDependentView( 131 ctx context.Context, 132 from *sqlbase.MutableTableDescriptor, 133 ref sqlbase.TableDescriptor_Reference, 134 behavior tree.DropBehavior, 135 ) error { 136 return p.canRemoveDependentViewGeneric(ctx, from.TypeName(), from.Name, from.ParentID, ref, behavior) 137 } 138 139 func (p *planner) canRemoveDependentViewGeneric( 140 ctx context.Context, 141 typeName string, 142 objName string, 143 parentID sqlbase.ID, 144 ref sqlbase.TableDescriptor_Reference, 145 behavior tree.DropBehavior, 146 ) error { 147 viewDesc, err := p.getViewDescForCascade(ctx, typeName, objName, parentID, ref.ID, behavior) 148 if err != nil { 149 return err 150 } 151 if err := p.CheckPrivilege(ctx, viewDesc, privilege.DROP); err != nil { 152 return err 153 } 154 // If this view is depended on by other views, we have to check them as well. 155 for _, ref := range viewDesc.DependedOnBy { 156 if err := p.canRemoveDependentView(ctx, viewDesc, ref, behavior); err != nil { 157 return err 158 } 159 } 160 return nil 161 } 162 163 // Drops the view and any additional views that depend on it. 164 // Returns the names of any additional views that were also dropped 165 // due to `cascade` behavior. 166 func (p *planner) removeDependentView( 167 ctx context.Context, tableDesc, viewDesc *sqlbase.MutableTableDescriptor, jobDesc string, 168 ) ([]string, error) { 169 // In the table whose index is being removed, filter out all back-references 170 // that refer to the view that's being removed. 171 tableDesc.DependedOnBy = removeMatchingReferences(tableDesc.DependedOnBy, viewDesc.ID) 172 // Then proceed to actually drop the view and log an event for it. 173 return p.dropViewImpl(ctx, viewDesc, true /* queueJob */, jobDesc, tree.DropCascade) 174 } 175 176 // dropViewImpl does the work of dropping a view (and views that depend on it 177 // if `cascade is specified`). Returns the names of any additional views that 178 // were also dropped due to `cascade` behavior. 179 func (p *planner) dropViewImpl( 180 ctx context.Context, 181 viewDesc *sqlbase.MutableTableDescriptor, 182 queueJob bool, 183 jobDesc string, 184 behavior tree.DropBehavior, 185 ) ([]string, error) { 186 var cascadeDroppedViews []string 187 188 // Remove back-references from the tables/views this view depends on. 189 for _, depID := range viewDesc.DependsOn { 190 dependencyDesc, err := p.Tables().GetMutableTableVersionByID(ctx, depID, p.txn) 191 if err != nil { 192 return cascadeDroppedViews, 193 errors.Errorf("error resolving dependency relation ID %d: %v", depID, err) 194 } 195 // The dependency is also being deleted, so we don't have to remove the 196 // references. 197 if dependencyDesc.Dropped() { 198 continue 199 } 200 dependencyDesc.DependedOnBy = removeMatchingReferences(dependencyDesc.DependedOnBy, viewDesc.ID) 201 // TODO (lucy): Have more consistent/informative names for dependent jobs. 202 if err := p.writeSchemaChange( 203 ctx, dependencyDesc, sqlbase.InvalidMutationID, "removing references for view", 204 ); err != nil { 205 return cascadeDroppedViews, err 206 } 207 } 208 viewDesc.DependsOn = nil 209 210 if behavior == tree.DropCascade { 211 for _, ref := range viewDesc.DependedOnBy { 212 dependentDesc, err := p.getViewDescForCascade( 213 ctx, viewDesc.TypeName(), viewDesc.Name, viewDesc.ParentID, ref.ID, behavior, 214 ) 215 if err != nil { 216 return cascadeDroppedViews, err 217 } 218 // TODO (lucy): Have more consistent/informative names for dependent jobs. 219 cascadedViews, err := p.dropViewImpl(ctx, dependentDesc, queueJob, "dropping dependent view", behavior) 220 if err != nil { 221 return cascadeDroppedViews, err 222 } 223 cascadeDroppedViews = append(cascadeDroppedViews, cascadedViews...) 224 cascadeDroppedViews = append(cascadeDroppedViews, dependentDesc.Name) 225 } 226 } 227 228 if err := p.initiateDropTable(ctx, viewDesc, queueJob, jobDesc, true /* drainName */); err != nil { 229 return cascadeDroppedViews, err 230 } 231 232 return cascadeDroppedViews, nil 233 } 234 235 func (p *planner) getViewDescForCascade( 236 ctx context.Context, 237 typeName string, 238 objName string, 239 parentID, viewID sqlbase.ID, 240 behavior tree.DropBehavior, 241 ) (*sqlbase.MutableTableDescriptor, error) { 242 viewDesc, err := p.Tables().GetMutableTableVersionByID(ctx, viewID, p.txn) 243 if err != nil { 244 log.Warningf(ctx, "unable to retrieve descriptor for view %d: %v", viewID, err) 245 return nil, errors.Wrapf(err, "error resolving dependent view ID %d", viewID) 246 } 247 if behavior != tree.DropCascade { 248 viewName := viewDesc.Name 249 if viewDesc.ParentID != parentID { 250 var err error 251 viewName, err = p.getQualifiedTableName(ctx, viewDesc.TableDesc()) 252 if err != nil { 253 log.Warningf(ctx, "unable to retrieve qualified name of view %d: %v", viewID, err) 254 return nil, sqlbase.NewDependentObjectErrorf( 255 "cannot drop %s %q because a view depends on it", typeName, objName) 256 } 257 } 258 return nil, errors.WithHintf( 259 sqlbase.NewDependentObjectErrorf("cannot drop %s %q because view %q depends on it", 260 typeName, objName, viewName), 261 "you can drop %s instead.", viewName) 262 } 263 return viewDesc, nil 264 }