github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/drop_database.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/config" 17 "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" 18 "github.com/cockroachdb/cockroach/pkg/keys" 19 "github.com/cockroachdb/cockroach/pkg/kv" 20 "github.com/cockroachdb/cockroach/pkg/security" 21 "github.com/cockroachdb/cockroach/pkg/server/telemetry" 22 "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" 23 "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" 24 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 25 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 26 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 27 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 28 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 29 "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" 30 "github.com/cockroachdb/cockroach/pkg/util/log" 31 "github.com/cockroachdb/errors" 32 ) 33 34 type dropDatabaseNode struct { 35 n *tree.DropDatabase 36 dbDesc *sqlbase.DatabaseDescriptor 37 td []toDelete 38 schemasToDelete []string 39 } 40 41 // DropDatabase drops a database. 42 // Privileges: DROP on database and DROP on all tables in the database. 43 // Notes: postgres allows only the database owner to DROP a database. 44 // mysql requires the DROP privileges on the database. 45 // TODO(XisiHuang): our DROP DATABASE is like the postgres DROP SCHEMA 46 // (cockroach database == postgres schema). the postgres default of not 47 // dropping the schema if there are dependent objects is more sensible 48 // (see the RESTRICT and CASCADE options). 49 func (p *planner) DropDatabase(ctx context.Context, n *tree.DropDatabase) (planNode, error) { 50 if n.Name == "" { 51 return nil, errEmptyDatabaseName 52 } 53 54 if string(n.Name) == p.SessionData().Database && p.SessionData().SafeUpdates { 55 return nil, pgerror.DangerousStatementf("DROP DATABASE on current database") 56 } 57 58 // Check that the database exists. 59 dbDesc, err := p.ResolveUncachedDatabaseByName(ctx, string(n.Name), !n.IfExists) 60 if err != nil { 61 return nil, err 62 } 63 if dbDesc == nil { 64 // IfExists was specified and database was not found. 65 return newZeroNode(nil /* columns */), nil 66 } 67 68 if err := p.CheckPrivilege(ctx, dbDesc, privilege.DROP); err != nil { 69 return nil, err 70 } 71 72 schemas, err := p.Tables().GetSchemasForDatabase(ctx, p.txn, dbDesc.ID) 73 if err != nil { 74 return nil, err 75 } 76 77 var tbNames TableNames 78 schemasToDelete := make([]string, 0, len(schemas)) 79 for _, schema := range schemas { 80 schemasToDelete = append(schemasToDelete, schema) 81 toAppend, err := resolver.GetObjectNames( 82 ctx, p.txn, p, p.ExecCfg().Codec, dbDesc, schema, true, /*explicitPrefix*/ 83 ) 84 if err != nil { 85 return nil, err 86 } 87 tbNames = append(tbNames, toAppend...) 88 } 89 90 if len(tbNames) > 0 { 91 switch n.DropBehavior { 92 case tree.DropRestrict: 93 return nil, pgerror.Newf(pgcode.DependentObjectsStillExist, 94 "database %q is not empty and RESTRICT was specified", 95 tree.ErrNameStringP(&dbDesc.Name)) 96 case tree.DropDefault: 97 // The default is CASCADE, however be cautious if CASCADE was 98 // not specified explicitly. 99 if p.SessionData().SafeUpdates { 100 return nil, pgerror.DangerousStatementf( 101 "DROP DATABASE on non-empty database without explicit CASCADE") 102 } 103 } 104 } 105 106 td := make([]toDelete, 0, len(tbNames)) 107 for i, tbName := range tbNames { 108 found, desc, err := p.LookupObject( 109 ctx, 110 tree.ObjectLookupFlags{ 111 CommonLookupFlags: tree.CommonLookupFlags{Required: true}, 112 RequireMutable: true, 113 IncludeOffline: true, 114 }, 115 tbName.Catalog(), 116 tbName.Schema(), 117 tbName.Table(), 118 ) 119 if err != nil { 120 return nil, err 121 } 122 if !found { 123 continue 124 } 125 tbDesc, ok := desc.(*sqlbase.MutableTableDescriptor) 126 if !ok { 127 return nil, errors.AssertionFailedf( 128 "descriptor for %q is not MutableTableDescriptor", 129 tbName.String(), 130 ) 131 } 132 if tbDesc.State == sqlbase.TableDescriptor_OFFLINE { 133 return nil, pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, 134 "cannot drop a database with OFFLINE tables, ensure %s is"+ 135 " dropped or made public before dropping database %s", 136 tbName.String(), tree.AsString((*tree.Name)(&dbDesc.Name))) 137 } 138 if err := p.prepareDropWithTableDesc(ctx, tbDesc); err != nil { 139 return nil, err 140 } 141 // Recursively check permissions on all dependent views, since some may 142 // be in different databases. 143 for _, ref := range tbDesc.DependedOnBy { 144 if err := p.canRemoveDependentView(ctx, tbDesc, ref, tree.DropCascade); err != nil { 145 return nil, err 146 } 147 } 148 td = append(td, toDelete{&tbNames[i], tbDesc}) 149 } 150 151 td, err = p.filterCascadedTables(ctx, td) 152 if err != nil { 153 return nil, err 154 } 155 156 return &dropDatabaseNode{n: n, dbDesc: dbDesc, td: td, schemasToDelete: schemasToDelete}, nil 157 } 158 159 func (n *dropDatabaseNode) startExec(params runParams) error { 160 telemetry.Inc(sqltelemetry.SchemaChangeDropCounter("database")) 161 162 ctx := params.ctx 163 p := params.p 164 tbNameStrings := make([]string, 0, len(n.td)) 165 droppedTableDetails := make([]jobspb.DroppedTableDetails, 0, len(n.td)) 166 tableDescs := make([]*sqlbase.MutableTableDescriptor, 0, len(n.td)) 167 168 for _, toDel := range n.td { 169 droppedTableDetails = append(droppedTableDetails, jobspb.DroppedTableDetails{ 170 Name: toDel.tn.FQString(), 171 ID: toDel.desc.ID, 172 }) 173 tableDescs = append(tableDescs, toDel.desc) 174 } 175 if err := p.createDropDatabaseJob( 176 ctx, n.dbDesc.ID, droppedTableDetails, tree.AsStringWithFQNames(n.n, params.Ann()), 177 ); err != nil { 178 return err 179 } 180 181 // When views, sequences, and tables are dropped, don't queue a separate job 182 // for each of them, since the single DROP DATABASE job will cover them all. 183 for _, toDel := range n.td { 184 desc := toDel.desc 185 var cascadedObjects []string 186 var err error 187 if desc.IsView() { 188 // TODO(knz): dependent dropped views should be qualified here. 189 cascadedObjects, err = p.dropViewImpl(ctx, desc, false /* queueJob */, "", tree.DropCascade) 190 } else if desc.IsSequence() { 191 err = p.dropSequenceImpl(ctx, desc, false /* queueJob */, "", tree.DropCascade) 192 } else { 193 // TODO(knz): dependent dropped table names should be qualified here. 194 cascadedObjects, err = p.dropTableImpl(ctx, desc, false /* queueJob */, "") 195 } 196 if err != nil { 197 return err 198 } 199 tbNameStrings = append(tbNameStrings, cascadedObjects...) 200 tbNameStrings = append(tbNameStrings, toDel.tn.FQString()) 201 } 202 203 descKey := sqlbase.MakeDescMetadataKey(p.ExecCfg().Codec, n.dbDesc.ID) 204 205 b := &kv.Batch{} 206 if p.ExtendedEvalContext().Tracing.KVTracingEnabled() { 207 log.VEventf(ctx, 2, "Del %s", descKey) 208 } 209 b.Del(descKey) 210 211 for _, schemaToDelete := range n.schemasToDelete { 212 if err := sqlbase.RemoveSchemaNamespaceEntry( 213 ctx, 214 p.txn, 215 p.ExecCfg().Codec, 216 n.dbDesc.ID, 217 schemaToDelete, 218 ); err != nil { 219 return err 220 } 221 } 222 223 err := sqlbase.RemoveDatabaseNamespaceEntry( 224 ctx, p.txn, p.ExecCfg().Codec, n.dbDesc.Name, p.ExtendedEvalContext().Tracing.KVTracingEnabled(), 225 ) 226 if err != nil { 227 return err 228 } 229 230 // No job was created because no tables were dropped, so zone config can be 231 // immediately removed. 232 if len(tableDescs) == 0 { 233 zoneKeyPrefix := config.MakeZoneKeyPrefix(uint32(n.dbDesc.ID)) 234 if p.ExtendedEvalContext().Tracing.KVTracingEnabled() { 235 log.VEventf(ctx, 2, "DelRange %s", zoneKeyPrefix) 236 } 237 // Delete the zone config entry for this database. 238 b.DelRange(zoneKeyPrefix, zoneKeyPrefix.PrefixEnd(), false /* returnKeys */) 239 } 240 241 p.Tables().AddUncommittedDatabase(n.dbDesc.Name, n.dbDesc.ID, descs.DBDropped) 242 243 if err := p.txn.Run(ctx, b); err != nil { 244 return err 245 } 246 247 if err := p.removeDbComment(ctx, n.dbDesc.ID); err != nil { 248 return err 249 } 250 251 // Log Drop Database event. This is an auditable log event and is recorded 252 // in the same transaction as the table descriptor update. 253 return MakeEventLogger(params.extendedEvalCtx.ExecCfg).InsertEventRecord( 254 ctx, 255 p.txn, 256 EventLogDropDatabase, 257 int32(n.dbDesc.ID), 258 int32(params.extendedEvalCtx.NodeID.SQLInstanceID()), 259 struct { 260 DatabaseName string 261 Statement string 262 User string 263 DroppedSchemaObjects []string 264 }{n.n.Name.String(), n.n.String(), p.SessionData().User, tbNameStrings}, 265 ) 266 } 267 268 func (*dropDatabaseNode) Next(runParams) (bool, error) { return false, nil } 269 func (*dropDatabaseNode) Close(context.Context) {} 270 func (*dropDatabaseNode) Values() tree.Datums { return tree.Datums{} } 271 272 // filterCascadedTables takes a list of table descriptors and removes any 273 // descriptors from the list that are dependent on other descriptors in the 274 // list (e.g. if view v1 depends on table t1, then v1 will be filtered from 275 // the list). 276 func (p *planner) filterCascadedTables(ctx context.Context, tables []toDelete) ([]toDelete, error) { 277 // Accumulate the set of all tables/views that will be deleted by cascade 278 // behavior so that we can filter them out of the list. 279 cascadedTables := make(map[sqlbase.ID]bool) 280 for _, toDel := range tables { 281 desc := toDel.desc 282 if err := p.accumulateDependentTables(ctx, cascadedTables, desc); err != nil { 283 return nil, err 284 } 285 } 286 filteredTableList := make([]toDelete, 0, len(tables)) 287 for _, toDel := range tables { 288 if !cascadedTables[toDel.desc.ID] { 289 filteredTableList = append(filteredTableList, toDel) 290 } 291 } 292 return filteredTableList, nil 293 } 294 295 func (p *planner) accumulateDependentTables( 296 ctx context.Context, dependentTables map[sqlbase.ID]bool, desc *sqlbase.MutableTableDescriptor, 297 ) error { 298 for _, ref := range desc.DependedOnBy { 299 dependentTables[ref.ID] = true 300 dependentDesc, err := p.Tables().GetMutableTableVersionByID(ctx, ref.ID, p.txn) 301 if err != nil { 302 return err 303 } 304 if err := p.accumulateDependentTables(ctx, dependentTables, dependentDesc); err != nil { 305 return err 306 } 307 } 308 return nil 309 } 310 311 func (p *planner) removeDbComment(ctx context.Context, dbID sqlbase.ID) error { 312 _, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.ExecEx( 313 ctx, 314 "delete-db-comment", 315 p.txn, 316 sqlbase.InternalExecutorSessionDataOverride{User: security.RootUser}, 317 "DELETE FROM system.comments WHERE type=$1 AND object_id=$2 AND sub_id=0", 318 keys.DatabaseCommentType, 319 dbID) 320 321 return err 322 }