github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rename_test.go (about) 1 // Copyright 2015 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 "sync" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/base" 19 "github.com/cockroachdb/cockroach/pkg/keys" 20 "github.com/cockroachdb/cockroach/pkg/sql/catalog/lease" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 23 "github.com/cockroachdb/cockroach/pkg/testutils" 24 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 25 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 26 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 27 ) 28 29 // TestRenameTable tests the table descriptor changes during 30 // a rename operation. 31 func TestRenameTable(t *testing.T) { 32 defer leaktest.AfterTest(t)() 33 s, db, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 34 defer s.Stopper().Stop(context.Background()) 35 36 counter := int64(keys.MinNonPredefinedUserDescID) 37 38 oldDBID := sqlbase.ID(counter) 39 if _, err := db.Exec(`CREATE DATABASE test`); err != nil { 40 t.Fatal(err) 41 } 42 43 // Create table in 'test'. 44 counter++ 45 oldName := "foo" 46 if _, err := db.Exec(`CREATE TABLE test.foo (k INT PRIMARY KEY, v int)`); err != nil { 47 t.Fatal(err) 48 } 49 50 // Check the table descriptor. 51 desc := &sqlbase.Descriptor{} 52 tableDescKey := sqlbase.MakeDescMetadataKey(keys.SystemSQLCodec, sqlbase.ID(counter)) 53 ts, err := kvDB.GetProtoTs(context.Background(), tableDescKey, desc) 54 if err != nil { 55 t.Fatal(err) 56 } 57 tableDesc := desc.Table(ts) 58 if tableDesc.Name != oldName { 59 t.Fatalf("Wrong table name, expected %s, got: %+v", oldName, tableDesc) 60 } 61 if tableDesc.ParentID != oldDBID { 62 t.Fatalf("Wrong parent ID on table, expected %d, got: %+v", oldDBID, tableDesc) 63 } 64 65 // Create database test2. 66 counter++ 67 newDBID := sqlbase.ID(counter) 68 if _, err := db.Exec(`CREATE DATABASE test2`); err != nil { 69 t.Fatal(err) 70 } 71 72 // Move table to test2 and change its name as well. 73 newName := "bar" 74 if _, err := db.Exec(`ALTER TABLE test.foo RENAME TO test2.bar`); err != nil { 75 t.Fatal(err) 76 } 77 78 // Check the table descriptor again. 79 ts, err = kvDB.GetProtoTs(context.Background(), tableDescKey, desc) 80 if err != nil { 81 t.Fatal(err) 82 } 83 tableDesc = desc.Table(ts) 84 if tableDesc.Name != newName { 85 t.Fatalf("Wrong table name, expected %s, got: %+v", newName, tableDesc) 86 } 87 if tableDesc.ParentID != newDBID { 88 t.Fatalf("Wrong parent ID on table, expected %d, got: %+v", newDBID, tableDesc) 89 } 90 } 91 92 // Test that a SQL txn that resolved a name can keep resolving that name during 93 // its lifetime even after the table has been renamed. 94 // Also tests that the name of a renamed table cannot be reused until everybody 95 // has stopped using it. Otherwise, we'd have different transactions in the 96 // systems using a single name for different tables. 97 // Also tests that the old name cannot be used by node that doesn't have a lease 98 // on the old version even while the name mapping still exists. 99 func TestTxnCanStillResolveOldName(t *testing.T) { 100 defer leaktest.AfterTest(t)() 101 102 var lmKnobs lease.ManagerTestingKnobs 103 // renameUnblocked is used to block the rename schema change until the test 104 // doesn't need the old name->id mapping to exist anymore. 105 renameUnblocked := make(chan interface{}) 106 serverParams := base.TestServerArgs{ 107 Knobs: base.TestingKnobs{ 108 SQLSchemaChanger: &SchemaChangerTestingKnobs{ 109 OldNamesDrainedNotification: func() { 110 <-renameUnblocked 111 }, 112 }, 113 SQLLeaseManager: &lmKnobs, 114 }} 115 var mu syncutil.Mutex 116 var waitTableID sqlbase.ID 117 // renamed is used to block until the node cannot get leases with the original 118 // table name. It will be signaled once the table has been renamed and the update 119 // about the new name has been processed. Moreover, not only does an update to 120 // the name needs to have been received, but the version of the descriptor needs to 121 // have also been incremented in order to guarantee that the node cannot get 122 // leases using the old name (an update with the new name but the original 123 // version is ignored by the leasing refresh mechanism). 124 renamed := make(chan interface{}) 125 lmKnobs.TestingTableRefreshedEvent = 126 func(table *sqlbase.TableDescriptor) { 127 mu.Lock() 128 defer mu.Unlock() 129 if waitTableID != table.ID { 130 return 131 } 132 if table.Name == "t2" && table.Version == 2 { 133 close(renamed) 134 waitTableID = 0 135 } 136 } 137 s, db, kvDB := serverutils.StartServer(t, serverParams) 138 defer s.Stopper().Stop(context.Background()) 139 140 sql := ` 141 CREATE DATABASE test; 142 CREATE TABLE test.t (a INT PRIMARY KEY); 143 ` 144 _, err := db.Exec(sql) 145 if err != nil { 146 t.Fatal(err) 147 } 148 149 tableDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") 150 mu.Lock() 151 waitTableID = tableDesc.ID 152 mu.Unlock() 153 154 txn, err := db.Begin() 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 // Run a command to make the transaction resolves the table name. 160 if _, err := txn.Exec("SELECT * FROM test.t"); err != nil { 161 t.Fatal(err) 162 } 163 164 // Concurrently, rename the table. 165 threadDone := make(chan error) 166 go func() { 167 // The ALTER will commit and signal the main thread through `renamed`, but 168 // the schema changer will remain blocked by the lease on the "t" version 169 // held by the txn started above. 170 _, err := db.Exec("ALTER TABLE test.t RENAME TO test.t2") 171 threadDone <- err 172 }() 173 defer func() { 174 close(renameUnblocked) 175 // Block until the thread doing the rename has finished, so the test can clean 176 // up. It needed to wait for the transaction to release its lease. 177 if err := <-threadDone; err != nil { 178 t.Fatal(err) 179 } 180 }() 181 182 // Block until the Manager has processed the gossip update. 183 <-renamed 184 185 // Run another command in the transaction and make sure that we can still 186 // resolve the table name. 187 if _, err := txn.Exec("SELECT * FROM test.t"); err != nil { 188 t.Fatal(err) 189 } 190 191 // Check that the name cannot be reused while somebody still has a lease on 192 // the old one (the mechanism for ensuring this is that the entry for the old 193 // name is not deleted from the database until the async schema changer checks 194 // that there's no more leases on the old version). 195 if _, err := db.Exec("CREATE TABLE test.t (a INT PRIMARY KEY)"); !testutils.IsError( 196 err, `relation "t" already exists`) { 197 t.Fatal(err) 198 } 199 200 if err := txn.Commit(); err != nil { 201 t.Fatal(err) 202 } 203 204 // Check that the old name is not usable outside of the transaction now 205 // that the node doesn't have a lease on it anymore (committing the txn 206 // should have released the lease on the version of the descriptor with the 207 // old name), even though the name mapping still exists. 208 209 var foundLease bool 210 s.LeaseManager().(*lease.Manager).VisitLeases(func( 211 desc sqlbase.TableDescriptor, dropped bool, refCount int, expiration tree.DTimestamp, 212 ) (wantMore bool) { 213 if desc.ID == tableDesc.ID && desc.Name == "t" { 214 foundLease = true 215 } 216 return true 217 }) 218 if foundLease { 219 t.Fatalf(`still have lease on "t"`) 220 } 221 if _, err := db.Exec("SELECT * FROM test.t"); !testutils.IsError( 222 err, `relation "test.t" does not exist`) { 223 t.Fatal(err) 224 } 225 } 226 227 // Test that a txn doing a rename can use the new name immediately. 228 // It can also use the old name if it took a lease on it before the rename, for 229 // better or worse. 230 func TestTxnCanUseNewNameAfterRename(t *testing.T) { 231 defer leaktest.AfterTest(t)() 232 s, db, _ := serverutils.StartServer(t, base.TestServerArgs{}) 233 defer s.Stopper().Stop(context.Background()) 234 235 sql := ` 236 CREATE DATABASE test; 237 CREATE TABLE test.t (a INT PRIMARY KEY); 238 ` 239 _, err := db.Exec(sql) 240 if err != nil { 241 t.Fatal(err) 242 } 243 244 // Make sure we take a lease on the version called "t". 245 if _, err := db.Exec("SELECT * FROM test.t"); err != nil { 246 t.Fatal(err) 247 } 248 { 249 txn, err := db.Begin() 250 if err != nil { 251 t.Fatal(err) 252 } 253 254 if _, err := txn.Exec("ALTER TABLE test.t RENAME TO test.t2"); err != nil { 255 t.Fatal(err) 256 } 257 // Check that we can use the new name. 258 if _, err := txn.Exec("SELECT * FROM test.t2"); err != nil { 259 t.Fatal(err) 260 } 261 262 if err := txn.Commit(); err != nil { 263 t.Fatal(err) 264 } 265 } 266 267 { 268 txn, err := db.Begin() 269 if err != nil { 270 t.Fatal(err) 271 } 272 273 if _, err := txn.Exec("ALTER TABLE test.t2 RENAME TO test.t"); err != nil { 274 t.Fatal(err) 275 } 276 // Check that we can use the new name. 277 if _, err := txn.Exec("SELECT * FROM test.t"); err != nil { 278 t.Fatal(err) 279 } 280 // Check that we cannot use the old name. 281 if _, err := txn.Exec(` 282 SELECT * FROM test.t2 283 `); !testutils.IsError(err, "relation \"test.t2\" does not exist") { 284 t.Fatalf("err = %v", err) 285 } 286 if err := txn.Rollback(); err != nil { 287 t.Fatal(err) 288 } 289 } 290 } 291 292 // Check that we properly cleanup all the temporary names when performing a 293 // series of renames in a transaction. 294 func TestSeriesOfRenames(t *testing.T) { 295 defer leaktest.AfterTest(t)() 296 s, db, _ := serverutils.StartServer(t, base.TestServerArgs{}) 297 defer s.Stopper().Stop(context.Background()) 298 299 sql := ` 300 CREATE DATABASE test; 301 CREATE TABLE test.t (a INT PRIMARY KEY); 302 ` 303 _, err := db.Exec(sql) 304 if err != nil { 305 t.Fatal(err) 306 } 307 308 txn, err := db.Begin() 309 if err != nil { 310 t.Fatal(err) 311 } 312 if _, err := txn.Exec("ALTER TABLE test.t RENAME TO test.t2"); err != nil { 313 t.Fatal(err) 314 } 315 if _, err := txn.Exec("ALTER TABLE test.t2 RENAME TO test.t3"); err != nil { 316 t.Fatal(err) 317 } 318 if _, err := txn.Exec("ALTER TABLE test.t3 RENAME TO test.t4"); err != nil { 319 t.Fatal(err) 320 } 321 if err := txn.Commit(); err != nil { 322 t.Fatal(err) 323 } 324 325 // Check that the temp names have been properly cleaned up by creating tables 326 // with those names. 327 if _, err := db.Exec("CREATE TABLE test.t (a INT PRIMARY KEY)"); err != nil { 328 t.Fatal(err) 329 } 330 if _, err := db.Exec("CREATE TABLE test.t2 (a INT PRIMARY KEY)"); err != nil { 331 t.Fatal(err) 332 } 333 if _, err := db.Exec("CREATE TABLE test.t3 (a INT PRIMARY KEY)"); err != nil { 334 t.Fatal(err) 335 } 336 } 337 338 // Tests that a RENAME while a name is being drained will result in the 339 // table version being incremented again, implying that all old names 340 // are drained correctly. The new RENAME will succeed with 341 // all old names drained. 342 func TestRenameDuringDrainingName(t *testing.T) { 343 defer leaktest.AfterTest(t)() 344 345 // two channels that signal the start of the second rename 346 // and the end of the second rename. 347 startRename := make(chan interface{}) 348 finishRename := make(chan interface{}) 349 serverParams := base.TestServerArgs{ 350 Knobs: base.TestingKnobs{ 351 SQLSchemaChanger: &SchemaChangerTestingKnobs{ 352 OldNamesDrainedNotification: func() { 353 if startRename != nil { 354 // Run second rename. 355 start := startRename 356 startRename = nil 357 close(start) 358 <-finishRename 359 } 360 }, 361 // Don't run the schema changer for the second RENAME so that we can be 362 // sure that the first schema changer runs both schema changes. This 363 // behavior is due to the fact that the schema changer for the first job 364 // will process all the draining names on the table descriptor, 365 // including the ones queued up by the second job. It's not ideal since 366 // we would like jobs to manage their own state without interference 367 // from other jobs, but that's how schema change jobs work right now. 368 SchemaChangeJobNoOp: func() bool { 369 return startRename == nil 370 }, 371 }, 372 }} 373 374 s, db, kvDB := serverutils.StartServer(t, serverParams) 375 defer s.Stopper().Stop(context.Background()) 376 377 sql := ` 378 CREATE DATABASE test; 379 CREATE TABLE test.t (a INT PRIMARY KEY); 380 ` 381 _, err := db.Exec(sql) 382 if err != nil { 383 t.Fatal(err) 384 } 385 386 tableDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") 387 // The expected version will be the result of two increments for the two 388 // schema changes and one increment for signaling of the completion of the 389 // drain. See the above comment for an explanation of why there's only one 390 // expected version update for draining names. 391 expectedVersion := tableDesc.Version + 3 392 393 // Concurrently, rename the table. 394 start := startRename 395 var wg sync.WaitGroup 396 wg.Add(1) 397 go func() { 398 if _, err := db.Exec("ALTER TABLE test.t RENAME TO test.t2"); err != nil { 399 t.Error(err) 400 } 401 wg.Done() 402 }() 403 404 <-start 405 if _, err := db.Exec("ALTER TABLE test.t2 RENAME TO test.t3"); err != nil { 406 t.Fatal(err) 407 } 408 close(finishRename) 409 410 wg.Wait() 411 412 // Table rename to t3 was successful. 413 tableDesc = sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t3") 414 if version := tableDesc.Version; expectedVersion != version { 415 t.Fatalf("version mismatch: expected = %d, current = %d", expectedVersion, version) 416 } 417 418 // Old names are gone. 419 if _, err := db.Exec("SELECT * FROM test.t"); !testutils.IsError( 420 err, `relation "test.t" does not exist`) { 421 t.Fatal(err) 422 } 423 if _, err := db.Exec("SELECT * FROM test.t2"); !testutils.IsError( 424 err, `relation "test.t2" does not exist`) { 425 t.Fatal(err) 426 } 427 }