github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rename_table.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/kv" 17 "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" 18 "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" 19 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/util/log" 23 "github.com/cockroachdb/errors" 24 ) 25 26 type renameTableNode struct { 27 n *tree.RenameTable 28 oldTn, newTn *tree.TableName 29 tableDesc *sqlbase.MutableTableDescriptor 30 } 31 32 // RenameTable renames the table, view or sequence. 33 // Privileges: DROP on source table/view/sequence, CREATE on destination database. 34 // Notes: postgres requires the table owner. 35 // mysql requires ALTER, DROP on the original table, and CREATE, INSERT 36 // on the new table (and does not copy privileges over). 37 func (p *planner) RenameTable(ctx context.Context, n *tree.RenameTable) (planNode, error) { 38 oldTn := n.Name.ToTableName() 39 newTn := n.NewName.ToTableName() 40 toRequire := resolver.ResolveRequireTableOrViewDesc 41 if n.IsView { 42 toRequire = resolver.ResolveRequireViewDesc 43 } else if n.IsSequence { 44 toRequire = resolver.ResolveRequireSequenceDesc 45 } 46 47 tableDesc, err := p.ResolveMutableTableDescriptor(ctx, &oldTn, !n.IfExists, toRequire) 48 if err != nil { 49 return nil, err 50 } 51 if tableDesc == nil { 52 // Noop. 53 return newZeroNode(nil /* columns */), nil 54 } 55 56 if tableDesc.State != sqlbase.TableDescriptor_PUBLIC { 57 return nil, sqlbase.NewUndefinedRelationError(&oldTn) 58 } 59 60 if err := p.CheckPrivilege(ctx, tableDesc, privilege.DROP); err != nil { 61 return nil, err 62 } 63 64 // Check if any views depend on this table/view. Because our views 65 // are currently just stored as strings, they explicitly specify the name 66 // of everything they depend on. Rather than trying to rewrite the view's 67 // query with the new name, we simply disallow such renames for now. 68 if len(tableDesc.DependedOnBy) > 0 { 69 return nil, p.dependentViewRenameError( 70 ctx, tableDesc.TypeName(), oldTn.String(), tableDesc.ParentID, tableDesc.DependedOnBy[0].ID) 71 } 72 73 return &renameTableNode{n: n, oldTn: &oldTn, newTn: &newTn, tableDesc: tableDesc}, nil 74 } 75 76 // ReadingOwnWrites implements the planNodeReadingOwnWrites interface. 77 // This is because RENAME DATABASE performs multiple KV operations on descriptors 78 // and expects to see its own writes. 79 func (n *renameTableNode) ReadingOwnWrites() {} 80 81 func (n *renameTableNode) startExec(params runParams) error { 82 p := params.p 83 ctx := params.ctx 84 oldTn := n.oldTn 85 newTn := n.newTn 86 tableDesc := n.tableDesc 87 88 oldUn := oldTn.ToUnresolvedObjectName() 89 prevDbDesc, prefix, err := p.ResolveUncachedDatabase(ctx, oldUn) 90 if err != nil { 91 return err 92 } 93 oldTn.ObjectNamePrefix = prefix 94 95 // Check if target database exists. 96 // We also look at uncached descriptors here. 97 newUn := newTn.ToUnresolvedObjectName() 98 targetDbDesc, prefix, err := p.ResolveUncachedDatabase(ctx, newUn) 99 if err != nil { 100 return err 101 } 102 newTn.ObjectNamePrefix = prefix 103 104 if err := p.CheckPrivilege(ctx, targetDbDesc, privilege.CREATE); err != nil { 105 return err 106 } 107 108 // oldTn and newTn are already normalized, so we can compare directly here. 109 if oldTn.Catalog() == newTn.Catalog() && 110 oldTn.Schema() == newTn.Schema() && 111 oldTn.Table() == newTn.Table() { 112 // Noop. 113 return nil 114 } 115 116 tableDesc.SetName(newTn.Table()) 117 tableDesc.ParentID = targetDbDesc.ID 118 119 newTbKey := sqlbase.MakePublicTableNameKey(ctx, params.ExecCfg().Settings, 120 targetDbDesc.ID, newTn.Table()).Key(p.ExecCfg().Codec) 121 122 if err := tableDesc.Validate(ctx, p.txn, p.ExecCfg().Codec); err != nil { 123 return err 124 } 125 126 descID := tableDesc.GetID() 127 parentSchemaID := tableDesc.GetParentSchemaID() 128 129 renameDetails := sqlbase.TableDescriptor_NameInfo{ 130 ParentID: prevDbDesc.ID, 131 ParentSchemaID: parentSchemaID, 132 Name: oldTn.Table()} 133 tableDesc.DrainingNames = append(tableDesc.DrainingNames, renameDetails) 134 if err := p.writeSchemaChange( 135 ctx, tableDesc, sqlbase.InvalidMutationID, tree.AsStringWithFQNames(n.n, params.Ann()), 136 ); err != nil { 137 return err 138 } 139 140 // We update the descriptor to the new name, but also leave the mapping of the 141 // old name to the id, so that the name is not reused until the schema changer 142 // has made sure it's not in use any more. 143 b := &kv.Batch{} 144 if p.extendedEvalCtx.Tracing.KVTracingEnabled() { 145 log.VEventf(ctx, 2, "CPut %s -> %d", newTbKey, descID) 146 } 147 err = catalogkv.WriteDescToBatch(ctx, p.extendedEvalCtx.Tracing.KVTracingEnabled(), 148 p.EvalContext().Settings, b, p.ExecCfg().Codec, descID, tableDesc.TableDesc()) 149 if err != nil { 150 return err 151 } 152 153 exists, id, err := sqlbase.LookupPublicTableID( 154 params.ctx, params.p.txn, p.ExecCfg().Codec, targetDbDesc.ID, newTn.Table(), 155 ) 156 if err == nil && exists { 157 // Try and see what kind of object we collided with. 158 desc, err := catalogkv.GetDescriptorByID(params.ctx, params.p.txn, p.ExecCfg().Codec, id) 159 if err != nil { 160 return err 161 } 162 return makeObjectAlreadyExistsError(desc, newTn.Table()) 163 } else if err != nil { 164 return err 165 } 166 167 b.CPut(newTbKey, descID, nil) 168 return p.txn.Run(ctx, b) 169 } 170 171 func (n *renameTableNode) Next(runParams) (bool, error) { return false, nil } 172 func (n *renameTableNode) Values() tree.Datums { return tree.Datums{} } 173 func (n *renameTableNode) Close(context.Context) {} 174 175 // TODO(a-robinson): Support renaming objects depended on by views once we have 176 // a better encoding for view queries (#10083). 177 func (p *planner) dependentViewRenameError( 178 ctx context.Context, typeName, objName string, parentID, viewID sqlbase.ID, 179 ) error { 180 viewDesc, err := sqlbase.GetTableDescFromID(ctx, p.txn, p.ExecCfg().Codec, viewID) 181 if err != nil { 182 return err 183 } 184 viewName := viewDesc.Name 185 if viewDesc.ParentID != parentID { 186 var err error 187 viewName, err = p.getQualifiedTableName(ctx, viewDesc) 188 if err != nil { 189 log.Warningf(ctx, "unable to retrieve name of view %d: %v", viewID, err) 190 return sqlbase.NewDependentObjectErrorf( 191 "cannot rename %s %q because a view depends on it", 192 typeName, objName) 193 } 194 } 195 return errors.WithHintf( 196 sqlbase.NewDependentObjectErrorf("cannot rename %s %q because view %q depends on it", 197 typeName, objName, viewName), 198 "you can drop %s instead.", viewName) 199 }