// writeTableDesc implements the SchemaAccessor interface. func (p *planner) writeTableDesc(tableDesc *sqlbase.TableDescriptor) error { if isVirtualDescriptor(tableDesc) { panic(fmt.Sprintf("Virtual Descriptors cannot be stored, found: %v", tableDesc)) } return p.txn.Put(sqlbase.MakeDescMetadataKey(tableDesc.GetID()), sqlbase.WrapDescriptor(tableDesc)) }
// Convert all the mutations into live descriptors for the table // and write the updated table descriptor to the DB. func (mt mutationTest) makeMutationsActive() { // Remove mutation to check real values in DB using SQL if mt.tableDesc.Mutations == nil || len(mt.tableDesc.Mutations) == 0 { mt.Fatal("No mutations to make active") } for _, m := range mt.tableDesc.Mutations { if col := m.GetColumn(); col != nil { mt.tableDesc.Columns = append(mt.tableDesc.Columns, *col) } else if index := m.GetIndex(); index != nil { mt.tableDesc.Indexes = append(mt.tableDesc.Indexes, *index) } else { mt.Fatalf("no descriptor in mutation: %v", m) } } mt.tableDesc.Mutations = nil if err := mt.tableDesc.ValidateTable(); err != nil { mt.Fatal(err) } if err := mt.kvDB.Put( context.TODO(), sqlbase.MakeDescMetadataKey(mt.tableDesc.ID), sqlbase.WrapDescriptor(mt.tableDesc), ); err != nil { mt.Fatal(err) } }
// writeMutation writes the mutation to the table descriptor. If the // State or the Direction is undefined, these values are populated via // picking random values before the mutation is written. func (mt mutationTest) writeMutation(m sqlbase.DescriptorMutation) { if m.Direction == sqlbase.DescriptorMutation_NONE { // randomly pick ADD/DROP mutation if this is the first mutation, or // pick the direction already chosen for the first mutation. if len(mt.tableDesc.Mutations) > 0 { m.Direction = mt.tableDesc.Mutations[0].Direction } else { m.Direction = sqlbase.DescriptorMutation_DROP if rand.Intn(2) == 0 { m.Direction = sqlbase.DescriptorMutation_ADD } } } if m.State == sqlbase.DescriptorMutation_UNKNOWN { // randomly pick DELETE_ONLY/WRITE_ONLY state. r := rand.Intn(2) if r == 0 { m.State = sqlbase.DescriptorMutation_DELETE_ONLY } else { m.State = sqlbase.DescriptorMutation_WRITE_ONLY } } mt.tableDesc.Mutations = append(mt.tableDesc.Mutations, m) if err := mt.tableDesc.ValidateTable(); err != nil { mt.Fatal(err) } if err := mt.kvDB.Put( context.TODO(), sqlbase.MakeDescMetadataKey(mt.tableDesc.ID), sqlbase.WrapDescriptor(mt.tableDesc), ); err != nil { mt.Fatal(err) } }
func (p *planner) createDescriptorWithID( idKey roachpb.Key, id sqlbase.ID, descriptor sqlbase.DescriptorProto, ) error { descriptor.SetID(id) // TODO(pmattis): The error currently returned below is likely going to be // difficult to interpret. // // TODO(pmattis): Need to handle if-not-exists here as well. // // TODO(pmattis): This is writing the namespace and descriptor table entries, // but not going through the normal INSERT logic and not performing a precise // mimicry. In particular, we're only writing a single key per table, while // perfect mimicry would involve writing a sentinel key for each row as well. descKey := sqlbase.MakeDescMetadataKey(descriptor.GetID()) b := &client.Batch{} descID := descriptor.GetID() descDesc := sqlbase.WrapDescriptor(descriptor) if log.V(2) { log.Infof(p.ctx(), "CPut %s -> %d", idKey, descID) log.Infof(p.ctx(), "CPut %s -> %s", descKey, descDesc) } b.CPut(idKey, descID, nil) b.CPut(descKey, descDesc, nil) p.setTestingVerifyMetadata(func(systemConfig config.SystemConfig) error { if err := expectDescriptorID(systemConfig, idKey, descID); err != nil { return err } return expectDescriptor(systemConfig, descKey, descDesc) }) return p.txn.Run(b) }
// AcquireLease acquires a schema change lease on the table if // an unexpired lease doesn't exist. It returns the lease. func (sc *SchemaChanger) AcquireLease() (sqlbase.TableDescriptor_SchemaChangeLease, error) { var lease sqlbase.TableDescriptor_SchemaChangeLease err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { txn.SetSystemConfigTrigger() tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } // A second to deal with the time uncertainty across nodes. // It is perfectly valid for two or more goroutines to hold a valid // lease and execute a schema change in parallel, because schema // changes are executed using transactions that run sequentially. // This just reduces the probability of a write collision. expirationTimeUncertainty := time.Second if tableDesc.Lease != nil { if time.Unix(0, tableDesc.Lease.ExpirationTime).Add(expirationTimeUncertainty).After(timeutil.Now()) { return errExistingSchemaChangeLease } log.Infof(txn.Context, "Overriding existing expired lease %v", tableDesc.Lease) } lease = sc.createSchemaChangeLease() tableDesc.Lease = &lease return txn.Put(sqlbase.MakeDescMetadataKey(tableDesc.ID), sqlbase.WrapDescriptor(tableDesc)) }) return lease, err }
func (sc *SchemaChanger) maybeWriteResumeSpan( txn *client.Txn, version sqlbase.DescriptorVersion, resume roachpb.Span, mutationIdx int, lastCheckpoint *time.Time, ) error { checkpointInterval := checkpointInterval if sc.testingKnobs.WriteCheckpointInterval > 0 { checkpointInterval = sc.testingKnobs.WriteCheckpointInterval } if timeutil.Since(*lastCheckpoint) < checkpointInterval { return nil } tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } if tableDesc.Version != version { return errVersionMismatch } tableDesc.Mutations[mutationIdx].ResumeSpan = resume txn.SetSystemConfigTrigger() if err := txn.Put(sqlbase.MakeDescMetadataKey(tableDesc.GetID()), sqlbase.WrapDescriptor(tableDesc)); err != nil { return err } *lastCheckpoint = timeutil.Now() return nil }
func restoreTableDesc( ctx context.Context, txn *client.Txn, database sqlbase.DatabaseDescriptor, table sqlbase.TableDescriptor, ) error { // Run getDescriptorID again to make sure the database hasn't been dropped // while we were importing. var err error if table.ParentID, err = getDescriptorID(txn, tableKey{name: database.Name}); err != nil { return err } tableIDKey := tableKey{parentID: table.ParentID, name: table.Name}.Key() tableDescKey := sqlbase.MakeDescMetadataKey(table.ID) // Check for an existing table. var existingDesc sqlbase.Descriptor existingIDKV, err := txn.Get(tableIDKey) if err != nil { return err } if existingIDKV.Value != nil { existingID, err := existingIDKV.Value.GetInt() if err != nil { return err } existingDescKV, err := txn.Get(sqlbase.MakeDescMetadataKey(sqlbase.ID(existingID))) if err != nil { return err } if err := existingDescKV.Value.GetProto(&existingDesc); err != nil { return err } } // Write the new descriptors. First the ID -> TableDescriptor for the new // table, then flip (or initialize) the name -> ID entry so any new queries // will use the new one. If there was an existing table, it can now be // cleaned up. b := txn.NewBatch() b.CPut(tableDescKey, sqlbase.WrapDescriptor(&table), nil) if existingTable := existingDesc.GetTable(); existingTable == nil { b.CPut(tableIDKey, table.ID, nil) } else { existingIDKV.Value.ClearChecksum() b.CPut(tableIDKey, table.ID, existingIDKV.Value) // TODO(dan): This doesn't work for interleaved tables. Fix it when we // fix the empty range interleaved table TODO below. existingDataPrefix := roachpb.Key(keys.MakeTablePrefix(uint32(existingTable.ID))) b.DelRange(existingDataPrefix, existingDataPrefix.PrefixEnd(), false) zoneKey, _, descKey := GetKeysForTableDescriptor(existingTable) // Delete the desc and zone entries. Leave the name because the new // table is using it. b.Del(descKey) b.Del(zoneKey) } return txn.Run(b) }
// ReleaseLease releases the table lease if it is the one registered with // the table descriptor. func (sc *SchemaChanger) ReleaseLease(lease sqlbase.TableDescriptor_SchemaChangeLease) error { err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { tableDesc, err := sc.findTableWithLease(txn, lease) if err != nil { return err } tableDesc.Lease = nil txn.SetSystemConfigTrigger() return txn.Put(sqlbase.MakeDescMetadataKey(tableDesc.ID), sqlbase.WrapDescriptor(tableDesc)) }) return err }
func (p *planner) changePrivileges( targets parser.TargetList, grantees parser.NameList, changePrivilege func(*sqlbase.PrivilegeDescriptor, string), ) (planNode, error) { descriptors, err := p.getDescriptorsFromTargetList(targets) if err != nil { return nil, err } for _, descriptor := range descriptors { if err := p.checkPrivilege(descriptor, privilege.GRANT); err != nil { return nil, err } privileges := descriptor.GetPrivileges() for _, grantee := range grantees { changePrivilege(privileges, string(grantee)) } switch d := descriptor.(type) { case *sqlbase.DatabaseDescriptor: if err := d.Validate(); err != nil { return nil, err } case *sqlbase.TableDescriptor: if err := d.Validate(p.txn); err != nil { return nil, err } if err := d.SetUpVersion(); err != nil { return nil, err } p.notifySchemaChange(d.ID, sqlbase.InvalidMutationID) } } // Now update the descriptors transactionally. b := p.txn.NewBatch() for _, descriptor := range descriptors { descKey := sqlbase.MakeDescMetadataKey(descriptor.GetID()) b.Put(descKey, sqlbase.WrapDescriptor(descriptor)) } if err := p.txn.Run(b); err != nil { return nil, err } return &emptyNode{}, nil }
// renameDatabase implements the DatabaseAccessor interface. func (p *planner) renameDatabase(oldDesc *sqlbase.DatabaseDescriptor, newName string) error { onAlreadyExists := func() error { return fmt.Errorf("the new database name %q already exists", newName) } if p.session.virtualSchemas.isVirtualDatabase(newName) { return onAlreadyExists() } oldName := oldDesc.Name oldDesc.SetName(newName) if err := oldDesc.Validate(); err != nil { return err } oldKey := databaseKey{oldName}.Key() newKey := databaseKey{newName}.Key() descID := oldDesc.GetID() descKey := sqlbase.MakeDescMetadataKey(descID) descDesc := sqlbase.WrapDescriptor(oldDesc) b := &client.Batch{} b.CPut(newKey, descID, nil) b.Put(descKey, descDesc) b.Del(oldKey) if err := p.txn.Run(b); err != nil { if _, ok := err.(*roachpb.ConditionFailedError); ok { return onAlreadyExists() } return err } p.setTestingVerifyMetadata(func(systemConfig config.SystemConfig) error { if err := expectDescriptorID(systemConfig, newKey, descID); err != nil { return err } if err := expectDescriptor(systemConfig, descKey, descDesc); err != nil { return err } return expectDeleted(systemConfig, oldKey) }) return nil }
// TestAddingFKs checks the behavior of a table in the non-public `ADD` state. // Being non-public, it should not be visible to clients, and is therefore // assumed to be empty (e.g. by foreign key checks), since no one could have // written to it yet. func TestAddingFKs(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.products (id INT PRIMARY KEY); INSERT INTO t.products VALUES (1), (2); CREATE TABLE t.orders (id INT PRIMARY KEY, product INT REFERENCES t.products, INDEX (product)); `); err != nil { t.Fatal(err) } // Step the referencing table back to the ADD state. ordersDesc := sqlbase.GetTableDescriptor(kvDB, "t", "orders") ordersDesc.State = sqlbase.TableDescriptor_ADD ordersDesc.Version++ if err := kvDB.Put( context.TODO(), sqlbase.MakeDescMetadataKey(ordersDesc.ID), sqlbase.WrapDescriptor(ordersDesc), ); err != nil { t.Fatal(err) } // Generally a referenced table needs to lookup referencing tables to check // FKs during delete operations, but referencing tables in the ADD state are // given special treatment. if _, err := sqlDB.Exec(`DELETE FROM t.products`); err != nil { t.Fatal(err) } // Client should not see the orders table. if _, err := sqlDB.Exec( `SELECT * FROM t.orders`, ); !testutils.IsError(err, "table is being added") { t.Fatal(err) } }
// ExtendLease for the current leaser. This needs to be called often while // doing a schema change to prevent more than one node attempting to apply a // schema change (which is still safe, but unwise). func (sc *SchemaChanger) ExtendLease( existingLease sqlbase.TableDescriptor_SchemaChangeLease, ) (sqlbase.TableDescriptor_SchemaChangeLease, error) { // Check if there is still time on this lease. minDesiredExpiration := timeutil.Now().Add(MinSchemaChangeLeaseDuration) if time.Unix(0, existingLease.ExpirationTime).After(minDesiredExpiration) { return existingLease, nil } // Update lease. var lease sqlbase.TableDescriptor_SchemaChangeLease err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { tableDesc, err := sc.findTableWithLease(txn, existingLease) if err != nil { return err } lease = sc.createSchemaChangeLease() tableDesc.Lease = &lease txn.SetSystemConfigTrigger() return txn.Put(sqlbase.MakeDescMetadataKey(tableDesc.ID), sqlbase.WrapDescriptor(tableDesc)) }) return lease, err }
func TestSchemaChangeProcess(t *testing.T) { defer leaktest.AfterTest(t)() // The descriptor changes made must have an immediate effect // so disable leases on tables. defer csql.TestDisableTableLeases()() params, _ := createTestServerParams() // Disable external processing of mutations. params.Knobs.SQLSchemaChanger = &csql.SchemaChangerTestingKnobs{ AsyncExecNotification: asyncSchemaChangerDisabled, } s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() var id = sqlbase.ID(keys.MaxReservedDescID + 2) var node = roachpb.NodeID(2) stopper := stop.NewStopper() leaseMgr := csql.NewLeaseManager( &base.NodeIDContainer{}, *kvDB, hlc.NewClock(hlc.UnixNano, time.Nanosecond), csql.LeaseManagerTestingKnobs{}, stopper, &csql.MemoryMetrics{}, ) defer stopper.Stop() changer := csql.NewSchemaChangerForTesting(id, 0, node, *kvDB, leaseMgr) if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k CHAR PRIMARY KEY, v CHAR, INDEX foo(v)); INSERT INTO t.test VALUES ('a', 'b'), ('c', 'd'); `); err != nil { t.Fatal(err) } // Read table descriptor for version. tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") expectedVersion := tableDesc.Version desc, err := changer.MaybeIncrementVersion() if err != nil { t.Fatal(err) } tableDesc = desc.GetTable() newVersion := tableDesc.Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } isDone, err := changer.IsDone() if err != nil { t.Fatal(err) } if !isDone { t.Fatalf("table expected to not have an outstanding schema change: %v", tableDesc) } // Check that MaybeIncrementVersion increments the version // correctly. expectedVersion++ tableDesc.UpVersion = true if err := kvDB.Put( context.TODO(), sqlbase.MakeDescMetadataKey(tableDesc.ID), sqlbase.WrapDescriptor(tableDesc), ); err != nil { t.Fatal(err) } isDone, err = changer.IsDone() if err != nil { t.Fatal(err) } if isDone { t.Fatalf("table expected to have an outstanding schema change: %v", desc.GetTable()) } desc, err = changer.MaybeIncrementVersion() if err != nil { t.Fatal(err) } tableDesc = desc.GetTable() savedTableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") newVersion = tableDesc.Version if newVersion != expectedVersion { t.Fatalf("bad version in returned desc; e = %d, v = %d", expectedVersion, newVersion) } newVersion = savedTableDesc.Version if newVersion != expectedVersion { t.Fatalf("bad version in saved desc; e = %d, v = %d", expectedVersion, newVersion) } isDone, err = changer.IsDone() if err != nil { t.Fatal(err) } if !isDone { t.Fatalf("table expected to not have an outstanding schema change: %v", tableDesc) } // Check that RunStateMachineBeforeBackfill doesn't do anything // if there are no mutations queued. if err := changer.RunStateMachineBeforeBackfill(); err != nil { t.Fatal(err) } tableDesc = sqlbase.GetTableDescriptor(kvDB, "t", "test") newVersion = tableDesc.Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } // Check that RunStateMachineBeforeBackfill functions properly. expectedVersion = tableDesc.Version // Make a copy of the index for use in a mutation. index := protoutil.Clone(&tableDesc.Indexes[0]).(*sqlbase.IndexDescriptor) index.Name = "bar" index.ID = tableDesc.NextIndexID tableDesc.NextIndexID++ changer = csql.NewSchemaChangerForTesting(id, tableDesc.NextMutationID, node, *kvDB, leaseMgr) tableDesc.Mutations = append(tableDesc.Mutations, sqlbase.DescriptorMutation{ Descriptor_: &sqlbase.DescriptorMutation_Index{Index: index}, Direction: sqlbase.DescriptorMutation_ADD, State: sqlbase.DescriptorMutation_DELETE_ONLY, MutationID: tableDesc.NextMutationID, }) tableDesc.NextMutationID++ // Run state machine in both directions. for _, direction := range []sqlbase.DescriptorMutation_Direction{sqlbase.DescriptorMutation_ADD, sqlbase.DescriptorMutation_DROP} { tableDesc.Mutations[0].Direction = direction expectedVersion++ if err := kvDB.Put( context.TODO(), sqlbase.MakeDescMetadataKey(tableDesc.ID), sqlbase.WrapDescriptor(tableDesc), ); err != nil { t.Fatal(err) } // The expected end state. expectedState := sqlbase.DescriptorMutation_WRITE_ONLY if direction == sqlbase.DescriptorMutation_DROP { expectedState = sqlbase.DescriptorMutation_DELETE_ONLY } // Run two times to ensure idempotency of operations. for i := 0; i < 2; i++ { if err := changer.RunStateMachineBeforeBackfill(); err != nil { t.Fatal(err) } tableDesc = sqlbase.GetTableDescriptor(kvDB, "t", "test") newVersion = tableDesc.Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } state := tableDesc.Mutations[0].State if state != expectedState { t.Fatalf("bad state; e = %d, v = %d", expectedState, state) } } } // RunStateMachineBeforeBackfill() doesn't complete the schema change. isDone, err = changer.IsDone() if err != nil { t.Fatal(err) } if isDone { t.Fatalf("table expected to have an outstanding schema change: %v", tableDesc) } }
// RenameColumn renames the column. // Privileges: CREATE on table. // notes: postgres requires CREATE on the table. // mysql requires ALTER, CREATE, INSERT on the table. func (p *planner) RenameColumn(n *parser.RenameColumn) (planNode, error) { // Check if table exists. tn, err := n.Table.NormalizeWithDatabaseName(p.session.Database) if err != nil { return nil, err } tableDesc, err := p.getTableDesc(tn) if err != nil { return nil, err } if tableDesc == nil { if n.IfExists { // Noop. return &emptyNode{}, nil } // Key does not exist, but we want it to: error out. return nil, fmt.Errorf("table %q does not exist", tn.Table()) } if err := p.checkPrivilege(tableDesc, privilege.CREATE); err != nil { return nil, err } if n.NewName == "" { return nil, errEmptyColumnName } normNewColName := n.NewName.Normalize() normColName := n.Name.Normalize() status, i, err := tableDesc.FindColumnByNormalizedName(normColName) // n.IfExists only applies to table, no need to check here. if err != nil { return nil, err } var column *sqlbase.ColumnDescriptor if status == sqlbase.DescriptorActive { column = &tableDesc.Columns[i] } else { column = tableDesc.Mutations[i].GetColumn() } for _, tableRef := range tableDesc.DependedOnBy { found := false for _, colID := range tableRef.ColumnIDs { if colID == column.ID { found = true } } if found { return nil, p.dependentViewRenameError( "column", n.Name.String(), tableDesc.ParentID, tableRef.ID) } } if normColName == normNewColName { // Noop. return &emptyNode{}, nil } if _, _, err := tableDesc.FindColumnByNormalizedName(normNewColName); err == nil { return nil, fmt.Errorf("column name %q already exists", n.NewName) } preFn := func(expr parser.Expr) (err error, recurse bool, newExpr parser.Expr) { if vBase, ok := expr.(parser.VarName); ok { v, err := vBase.NormalizeVarName() if err != nil { return err, false, nil } if c, ok := v.(*parser.ColumnItem); ok { if c.ColumnName.Normalize() == normColName { c.ColumnName = n.NewName } } return nil, false, v } return nil, true, expr } exprStrings := make([]string, len(tableDesc.Checks)) for i, check := range tableDesc.Checks { exprStrings[i] = check.Expr } exprs, err := parser.ParseExprsTraditional(exprStrings) if err != nil { return nil, err } for i := range tableDesc.Checks { expr, err := parser.SimpleVisit(exprs[i], preFn) if err != nil { return nil, err } if after := expr.String(); after != tableDesc.Checks[i].Expr { tableDesc.Checks[i].Expr = after } } // Rename the column in the indexes. tableDesc.RenameColumnNormalized(column.ID, normNewColName) column.Name = normNewColName if err := tableDesc.SetUpVersion(); err != nil { return nil, err } descKey := sqlbase.MakeDescMetadataKey(tableDesc.GetID()) if err := tableDesc.Validate(p.txn); err != nil { return nil, err } if err := p.txn.Put(descKey, sqlbase.WrapDescriptor(tableDesc)); err != nil { return nil, err } p.notifySchemaChange(tableDesc.ID, sqlbase.InvalidMutationID) return &emptyNode{}, nil }
// RenameIndex renames the index. // Privileges: CREATE on table. // notes: postgres requires CREATE on the table. // mysql requires ALTER, CREATE, INSERT on the table. func (p *planner) RenameIndex(n *parser.RenameIndex) (planNode, error) { tn, err := p.expandIndexName(n.Index) if err != nil { return nil, err } tableDesc, err := p.mustGetTableDesc(tn) if err != nil { return nil, err } normIdxName := n.Index.Index.Normalize() status, i, err := tableDesc.FindIndexByNormalizedName(normIdxName) if err != nil { if n.IfExists { // Noop. return &emptyNode{}, nil } // Index does not exist, but we want it to: error out. return nil, err } if err := p.checkPrivilege(tableDesc, privilege.CREATE); err != nil { return nil, err } for _, tableRef := range tableDesc.DependedOnBy { if tableRef.IndexID != tableDesc.Indexes[i].ID { continue } return nil, p.dependentViewRenameError( "index", n.Index.Index.String(), tableDesc.ParentID, tableRef.ID) } if n.NewName == "" { return nil, errEmptyIndexName } normNewIdxName := n.NewName.Normalize() if normIdxName == normNewIdxName { // Noop. return &emptyNode{}, nil } if _, _, err := tableDesc.FindIndexByNormalizedName(normNewIdxName); err == nil { return nil, fmt.Errorf("index name %q already exists", n.NewName) } if status == sqlbase.DescriptorActive { tableDesc.Indexes[i].Name = normNewIdxName } else { tableDesc.Mutations[i].GetIndex().Name = normNewIdxName } if err := tableDesc.SetUpVersion(); err != nil { return nil, err } descKey := sqlbase.MakeDescMetadataKey(tableDesc.GetID()) if err := tableDesc.Validate(p.txn); err != nil { return nil, err } if err := p.txn.Put(descKey, sqlbase.WrapDescriptor(tableDesc)); err != nil { return nil, err } p.notifySchemaChange(tableDesc.ID, sqlbase.InvalidMutationID) return &emptyNode{}, nil }
// RenameTable renames the table or view. // Privileges: DROP on source table/view, CREATE on destination database. // Notes: postgres requires the table owner. // mysql requires ALTER, DROP on the original table, and CREATE, INSERT // on the new table (and does not copy privileges over). func (p *planner) RenameTable(n *parser.RenameTable) (planNode, error) { oldTn, err := n.Name.NormalizeWithDatabaseName(p.session.Database) if err != nil { return nil, err } newTn, err := n.NewName.NormalizeWithDatabaseName(p.session.Database) if err != nil { return nil, err } dbDesc, err := p.mustGetDatabaseDesc(oldTn.Database()) if err != nil { return nil, err } // Check if source table or view exists. // Note that Postgres's behavior here is a little lenient - it'll let you // modify views by running ALTER TABLE, but won't let you modify tables // by running ALTER VIEW. Our behavior is strict for now, but can be // made more lenient down the road if needed. var tableDesc *sqlbase.TableDescriptor if n.IsView { tableDesc, err = p.getViewDesc(oldTn) if err != nil { return nil, err } if tableDesc == nil { if n.IfExists { // Noop. return &emptyNode{}, nil } // Key does not exist, but we want it to: error out. return nil, sqlbase.NewUndefinedViewError(oldTn.String()) } if tableDesc.State != sqlbase.TableDescriptor_PUBLIC { return nil, sqlbase.NewUndefinedViewError(oldTn.String()) } } else { tableDesc, err = p.getTableDesc(oldTn) if err != nil { return nil, err } if tableDesc == nil { if n.IfExists { // Noop. return &emptyNode{}, nil } // Key does not exist, but we want it to: error out. return nil, sqlbase.NewUndefinedTableError(oldTn.String()) } if tableDesc.State != sqlbase.TableDescriptor_PUBLIC { return nil, sqlbase.NewUndefinedTableError(oldTn.String()) } } if err := p.checkPrivilege(tableDesc, privilege.DROP); err != nil { return nil, err } // Check if any views depend on this table/view. Because our views // are currently just stored as strings, they explicitly specify the name // of everything they depend on. Rather than trying to rewrite the view's // query with the new name, we simply disallow such renames for now. if len(tableDesc.DependedOnBy) > 0 { return nil, p.dependentViewRenameError( tableDesc.TypeName(), oldTn.String(), tableDesc.ParentID, tableDesc.DependedOnBy[0].ID) } // Check if target database exists. targetDbDesc, err := p.mustGetDatabaseDesc(newTn.Database()) if err != nil { return nil, err } if err := p.checkPrivilege(targetDbDesc, privilege.CREATE); err != nil { return nil, err } // oldTn and newTn are already normalized, so we can compare directly here. if oldTn.Database() == newTn.Database() && oldTn.Table() == newTn.Table() { // Noop. return &emptyNode{}, nil } tableDesc.SetName(newTn.Table()) tableDesc.ParentID = targetDbDesc.ID descKey := sqlbase.MakeDescMetadataKey(tableDesc.GetID()) newTbKey := tableKey{targetDbDesc.ID, newTn.Table()}.Key() if err := tableDesc.Validate(p.txn); err != nil { return nil, err } descID := tableDesc.GetID() descDesc := sqlbase.WrapDescriptor(tableDesc) if err := tableDesc.SetUpVersion(); err != nil { return nil, err } renameDetails := sqlbase.TableDescriptor_RenameInfo{ OldParentID: dbDesc.ID, OldName: oldTn.Table()} tableDesc.Renames = append(tableDesc.Renames, renameDetails) if err := p.writeTableDesc(tableDesc); err != nil { return nil, err } // We update the descriptor to the new name, but also leave the mapping of the // old name to the id, so that the name is not reused until the schema changer // has made sure it's not in use any more. b := &client.Batch{} b.Put(descKey, descDesc) b.CPut(newTbKey, descID, nil) if err := p.txn.Run(b); err != nil { if _, ok := err.(*roachpb.ConditionFailedError); ok { return nil, sqlbase.NewRelationAlreadyExistsError(newTn.Table()) } return nil, err } p.notifySchemaChange(tableDesc.ID, sqlbase.InvalidMutationID) p.setTestingVerifyMetadata(func(systemConfig config.SystemConfig) error { if err := expectDescriptorID(systemConfig, newTbKey, descID); err != nil { return err } if err := expectDescriptor(systemConfig, descKey, descDesc); err != nil { return err } return nil }) return &emptyNode{}, nil }