// Test that we fail to lease a table that was marked for deletion. func TestCantLeaseDeletedTable(testingT *testing.T) { defer leaktest.AfterTest(testingT)() defer csql.TestDisableAsyncSchemaChangeExec()() var execKnobs csql.ExecutorTestingKnobs var mu sync.Mutex clearSchemaChangers := false execKnobs.SyncSchemaChangersFilter = func(tscc csql.TestingSchemaChangerCollection) { mu.Lock() defer mu.Unlock() if clearSchemaChangers { tscc.ClearSchemaChangers() } } ctx, _ := createTestServerContext() ctx.TestingKnobs.SQLExecutor = &execKnobs t := newLeaseTest(testingT, ctx) defer t.cleanup() sql := ` CREATE DATABASE test; CREATE TABLE test.t(a INT PRIMARY KEY); ` _, err := t.db.Exec(sql) if err != nil { t.Fatal(err) } // Block schema changers so that the table we're about to DROP is not actually // dropped; it will be left in a "deleted" state. mu.Lock() clearSchemaChangers = true mu.Unlock() // DROP the table _, err = t.db.Exec(`DROP TABLE test.t`) if err != nil { t.Fatal(err) } // Make sure we can't get a lease on the descriptor. tableDesc := getTableDescriptor(t.kvDB, "test", "t") // try to acquire at a bogus version to make sure we don't get back a lease we // already had. _, err = t.acquire(1, tableDesc.ID, tableDesc.Version+1) if !testutils.IsError(err, "descriptor deleted") { t.Fatalf("got a different error than expected: %s", err) } }
func TestCommandsWithPendingMutations(t *testing.T) { defer leaktest.AfterTest(t) // The descriptor changes made must have an immediate effect // so disable leases on tables. defer csql.TestDisableTableLeases()() // Disable external processing of mutations. defer csql.TestDisableAsyncSchemaChangeExec()() server, sqlDB, kvDB := setup(t) defer cleanup(server, sqlDB) if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (a CHAR PRIMARY KEY, b CHAR, c CHAR, INDEX foo (c)); `); err != nil { t.Fatal(err) } // Read table descriptor nameKey := csql.MakeNameMetadataKey(keys.MaxReservedDescID+1, "test") gr, err := kvDB.Get(nameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := csql.MakeDescMetadataKey(csql.ID(gr.ValueInt())) desc := &csql.Descriptor{} if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } mt := mutationTest{ T: t, kvDB: kvDB, sqlDB: sqlDB, descKey: descKey, desc: desc, } // Test CREATE INDEX in the presence of mutations. // Add index DROP mutation "foo"" mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) if _, err := sqlDB.Exec(`CREATE INDEX foo ON t.test (c)`); !testutils.IsError(err, `index "foo" being dropped, try again later`) { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // "foo" is being added. mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) if _, err := sqlDB.Exec(`CREATE INDEX foo ON t.test (c)`); !testutils.IsError(err, `duplicate index name: "foo"`) { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // Add column DROP mutation "b" mt.writeColumnMutation("b", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) if _, err := sqlDB.Exec(`CREATE INDEX bar ON t.test (b)`); !testutils.IsError(err, `index "bar" contains unknown column "b"`) { t.Fatal(err) } // Make "b" live. mt.makeMutationsActive() // "b" is being added. mt.writeColumnMutation("b", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) // An index referencing a column mutation that is being added // is allowed to be added. if _, err := sqlDB.Exec(`CREATE INDEX bar ON t.test (b)`); err != nil { t.Fatal(err) } // Make "b" live. mt.makeMutationsActive() // Test DROP INDEX in the presence of mutations. // Add index DROP mutation "foo"" mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) // Noop. if _, err := sqlDB.Exec(`DROP INDEX t.test@foo`); err != nil { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // "foo" is being added. mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) if _, err := sqlDB.Exec(`DROP INDEX t.test@foo`); !testutils.IsError(err, `index "foo" in the middle of being added, try again later`) { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // Test ALTER TABLE ADD/DROP column in the presence of mutations. // Add column DROP mutation "b" mt.writeColumnMutation("b", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD b CHAR`); !testutils.IsError(err, `column "b" being dropped, try again later`) { t.Fatal(err) } // Noop. if _, err := sqlDB.Exec(`ALTER TABLE t.test DROP b`); err != nil { t.Fatal(err) } // Make "b" live. mt.makeMutationsActive() // "b" is being added. mt.writeColumnMutation("b", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD b CHAR`); !testutils.IsError(err, `duplicate column name: "b"`) { t.Fatal(err) } if _, err := sqlDB.Exec(`ALTER TABLE t.test DROP b`); !testutils.IsError(err, `column "b" in the middle of being added, try again later`) { t.Fatal(err) } // Make "b" live. mt.makeMutationsActive() // Test ALTER TABLE ADD CONSTRAINT in the presence of mutations. // Add index DROP mutation "foo"" mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD CONSTRAINT foo UNIQUE (c)`); !testutils.IsError(err, `index "foo" being dropped, try again later`) { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // "foo" is being added. mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD CONSTRAINT foo UNIQUE (c)`); !testutils.IsError(err, `duplicate index name: "foo"`) { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // Add column mutation "b" mt.writeColumnMutation("b", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD CONSTRAINT bar UNIQUE (b)`); !testutils.IsError(err, `index "bar" contains unknown column "b"`) { t.Fatal(err) } // Make "b" live. mt.makeMutationsActive() // "b" is being added. mt.writeColumnMutation("b", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) // Noop. if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD CONSTRAINT bar UNIQUE (b)`); err != nil { t.Fatal(err) } // Make "b" live. mt.makeMutationsActive() // Test DROP CONSTRAINT in the presence of mutations. // Add index mutation "foo"" mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_DROP}) // Noop. if _, err := sqlDB.Exec(`ALTER TABLE t.test DROP CONSTRAINT foo`); err != nil { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // "foo" is being added. mt.writeIndexMutation("foo", csql.DescriptorMutation{Direction: csql.DescriptorMutation_ADD}) if _, err := sqlDB.Exec(`ALTER TABLE t.test DROP CONSTRAINT foo`); !testutils.IsError(err, `constraint "foo" in the middle of being added, try again later`) { t.Fatal(err) } // Make "foo" live. mt.makeMutationsActive() // Rename column/index, while index is under mutation. // Add index mutation "foo"" mt.writeIndexMutation("foo", csql.DescriptorMutation{}) if _, err := sqlDB.Exec(`ALTER INDEX t.test@foo RENAME to ufo`); err != nil { mt.Fatal(err) } if _, err := sqlDB.Exec(`ALTER TABLE t.test RENAME COLUMN c TO d`); err != nil { mt.Fatal(err) } // The mutation in the table descriptor has changed and we would like // to update our copy to make it live. if err := mt.kvDB.GetProto(mt.descKey, mt.desc); err != nil { mt.Fatal(err) } // Make "ufo" live. mt.makeMutationsActive() // The index has been renamed to ufo, and the column to d. _ = mt.checkQueryResponse("SHOW INDEX FROM t.test", [][]string{{"test", "primary", "true", "1", "a", "ASC", "false"}, {"test", "ufo", "false", "1", "d", "ASC", "false"}}) // Rename column under mutation works properly. // Add column mutation "b". mt.writeColumnMutation("b", csql.DescriptorMutation{}) if _, err := sqlDB.Exec(`ALTER TABLE t.test RENAME COLUMN b TO e`); err != nil { mt.Fatal(err) } // The mutation in the table descriptor has changed and we would like // to update our copy to make it live. if err := mt.kvDB.GetProto(mt.descKey, mt.desc); err != nil { mt.Fatal(err) } // Make column "e" live. mt.makeMutationsActive() // Column b changed to d. _ = mt.checkQueryResponse("SHOW COLUMNS FROM t.test", [][]string{{"a", "STRING", "true", "NULL"}, {"d", "STRING", "true", "NULL"}, {"e", "STRING", "true", "NULL"}}) }
func TestOperationsWithIndexMutation(t *testing.T) { defer leaktest.AfterTest(t) // The descriptor changes made must have an immediate effect. defer csql.TestDisableTableLeases()() // Disable external processing of mutations. defer csql.TestDisableAsyncSchemaChangeExec()() server, sqlDB, kvDB := setup(t) defer cleanup(server, sqlDB) if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k CHAR PRIMARY KEY, v CHAR, INDEX foo (v)); `); err != nil { t.Fatal(err) } // read table descriptor nameKey := csql.MakeNameMetadataKey(keys.MaxReservedDescID+1, "test") gr, err := kvDB.Get(nameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := csql.MakeDescMetadataKey(csql.ID(gr.ValueInt())) desc := &csql.Descriptor{} if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } mTest := mutationTest{ T: t, kvDB: kvDB, sqlDB: sqlDB, descKey: descKey, desc: desc, } starQuery := `SELECT * FROM t.test` indexQuery := `SELECT * FROM t.test@foo` // See the effect of the operations depending on the state. for _, state := range []csql.DescriptorMutation_State{csql.DescriptorMutation_DELETE_ONLY, csql.DescriptorMutation_WRITE_ONLY} { // Init table with some entries. if _, err := sqlDB.Exec(`TRUNCATE TABLE t.test`); err != nil { t.Fatal(err) } initRows := [][]string{{"a", "z"}, {"b", "y"}} for _, row := range initRows { if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ($1, $2)`, row[0], row[1]); err != nil { t.Fatal(err) } } _ = mTest.checkQueryResponse(starQuery, initRows) // Index foo is visible. _ = mTest.checkQueryResponse(indexQuery, [][]string{{"y"}, {"z"}}) // Index foo is invisible once it's a mutation. mTest.writeIndexMutation("foo", csql.DescriptorMutation{State: state}) if _, err := sqlDB.Query(indexQuery); !testutils.IsError(err, `index "foo" not found`) { t.Fatal(err) } // Insert a new entry. if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ('c', 'x')`); err != nil { t.Fatal(err) } _ = mTest.checkQueryResponse(starQuery, [][]string{{"a", "z"}, {"b", "y"}, {"c", "x"}}) // Make index "foo" live so that we can read it. mTest.makeMutationsActive() if state == csql.DescriptorMutation_DELETE_ONLY { // "x" didn't get added to the index. _ = mTest.checkQueryResponse(indexQuery, [][]string{{"y"}, {"z"}}) } else { // "x" got added to the index. _ = mTest.checkQueryResponse(indexQuery, [][]string{{"x"}, {"y"}, {"z"}}) } // Make "foo" a mutation. mTest.writeIndexMutation("foo", csql.DescriptorMutation{State: state}) // Update. if _, err := sqlDB.Exec(`UPDATE t.test SET v = 'w' WHERE k = 'c'`); err != nil { t.Fatal(err) } // Update "v" to its current value "z" in row "a". if _, err := sqlDB.Exec(`UPDATE t.test SET v = 'z' WHERE k = 'a'`); err != nil { t.Fatal(err) } _ = mTest.checkQueryResponse(starQuery, [][]string{{"a", "z"}, {"b", "y"}, {"c", "w"}}) // Make index "foo" live so that we can read it. mTest.makeMutationsActive() if state == csql.DescriptorMutation_DELETE_ONLY { // updating "x" -> "w" is a noop on the index, // updating "z" -> "z" results in "z" being deleted from the index. _ = mTest.checkQueryResponse(indexQuery, [][]string{{"y"}, {"z"}}) } else { // updating "x" -> "w" results in the index updating from "x" -> "w", // updating "z" -> "z" is a noop on the index. _ = mTest.checkQueryResponse(indexQuery, [][]string{{"w"}, {"y"}, {"z"}}) } // Make "foo" a mutation. mTest.writeIndexMutation("foo", csql.DescriptorMutation{State: state}) // Delete row "b". if _, err := sqlDB.Exec(`DELETE FROM t.test WHERE k = 'b'`); err != nil { t.Fatal(err) } _ = mTest.checkQueryResponse(starQuery, [][]string{{"a", "z"}, {"c", "w"}}) // Make index "foo" live so that we can read it. mTest.makeMutationsActive() // Deleting row "b" deletes "y" from the index. if state == csql.DescriptorMutation_DELETE_ONLY { mTest.checkQueryResponse(indexQuery, [][]string{{"z"}}) } else { mTest.checkQueryResponse(indexQuery, [][]string{{"w"}, {"z"}}) } } // Check that a mutation can only be inserted with an explicit mutation state. tableDesc := desc.GetTable() tableDesc.Mutations = []csql.DescriptorMutation{{Descriptor_: &csql.DescriptorMutation_Index{Index: &tableDesc.Indexes[len(tableDesc.Indexes)-1]}}} tableDesc.Indexes = tableDesc.Indexes[:len(tableDesc.Indexes)-1] if err := tableDesc.Validate(); !testutils.IsError(err, "mutation in state UNKNOWN, direction NONE, index foo, id 2") { t.Fatal(err) } }
func TestOperationsWithColumnMutation(t *testing.T) { defer leaktest.AfterTest(t) // The descriptor changes made must have an immediate effect // so disable leases on tables. defer csql.TestDisableTableLeases()() // Disable external processing of mutations. defer csql.TestDisableAsyncSchemaChangeExec()() server, sqlDB, kvDB := setup(t) defer cleanup(server, sqlDB) if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k CHAR PRIMARY KEY, v CHAR, i CHAR DEFAULT 'i'); `); err != nil { t.Fatal(err) } // read table descriptor nameKey := csql.MakeNameMetadataKey(keys.MaxReservedDescID+1, "test") gr, err := kvDB.Get(nameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := csql.MakeDescMetadataKey(csql.ID(gr.ValueInt())) desc := &csql.Descriptor{} if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } mTest := mutationTest{ T: t, kvDB: kvDB, sqlDB: sqlDB, descKey: descKey, desc: desc, } starQuery := `SELECT * FROM t.test` // Run the tests for both states. for _, state := range []csql.DescriptorMutation_State{csql.DescriptorMutation_DELETE_ONLY, csql.DescriptorMutation_WRITE_ONLY} { // Init table to start state. if _, err := sqlDB.Exec(`TRUNCATE TABLE t.test`); err != nil { t.Fatal(err) } initRows := [][]string{{"a", "z", "q"}} for _, row := range initRows { if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ($1, $2, $3)`, row[0], row[1], row[2]); err != nil { t.Fatal(err) } } // Check that the table only contains the initRows. _ = mTest.checkQueryResponse(starQuery, initRows) // Add column "i" as a mutation. mTest.writeColumnMutation("i", csql.DescriptorMutation{State: state}) // A direct read of column "i" fails. if _, err := sqlDB.Query(`SELECT i FROM t.test`); err == nil { t.Fatalf("Read succeeded despite column being in %v state", csql.DescriptorMutation{State: state}) } // The table only contains columns "k" and "v". _ = mTest.checkQueryResponse(starQuery, [][]string{{"a", "z"}}) // Inserting a row into the table while specifying column "i" results in an error. if _, err := sqlDB.Exec(`INSERT INTO t.test (k, v, i) VALUES ('b', 'y', 'i')`); !testutils.IsError(err, `column "i" does not exist`) { t.Fatal(err) } // Repeating the same without specifying the columns results in a different error. if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ('b', 'y', 'i')`); !testutils.IsError(err, "INSERT has more expressions than target columns: 3/2") { t.Fatal(err) } // Make column "i" live so that it is read. mTest.makeMutationsActive() // Check that we can read all the rows and columns. _ = mTest.checkQueryResponse(starQuery, initRows) var afterInsert, afterUpdate, afterDelete [][]string if state == csql.DescriptorMutation_DELETE_ONLY { // The default value of "i" for column "i" is not written. afterInsert = [][]string{{"a", "z", "q"}, {"c", "x", "NULL"}} // Update is a noop for column "i". afterUpdate = [][]string{{"a", "u", "q"}, {"c", "x", "NULL"}} // Delete also deletes column "i". afterDelete = [][]string{{"c", "x", "NULL"}} } else { // The default value of "i" for column "i" is written. afterInsert = [][]string{{"a", "z", "q"}, {"c", "x", "i"}} // Update is a noop for column "i". afterUpdate = [][]string{{"a", "u", "q"}, {"c", "x", "i"}} // Delete also deletes column "i". afterDelete = [][]string{{"c", "x", "i"}} } // Make column "i" a mutation. mTest.writeColumnMutation("i", csql.DescriptorMutation{State: state}) // Insert a row into the table. if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ('c', 'x')`); err != nil { t.Fatal(err) } // Make column "i" live so that it is read. mTest.makeMutationsActive() // Notice that the default value of "i" is only written when the // descriptor is in the WRITE_ONLY state. _ = mTest.checkQueryResponse(starQuery, afterInsert) // Make column "i" a mutation. mTest.writeColumnMutation("i", csql.DescriptorMutation{State: state}) // Updating column "i" for a row fails. if _, err := sqlDB.Exec(`UPDATE t.test SET (v, i) = ('u', 'u') WHERE k = 'a'`); !testutils.IsError(err, `column "i" does not exist`) { t.Fatal(err) } // Make column "i" live so that it is read. mTest.makeMutationsActive() // The above failed update was a noop. _ = mTest.checkQueryResponse(starQuery, afterInsert) // Make column "i" a mutation. mTest.writeColumnMutation("i", csql.DescriptorMutation{State: state}) // Update a row without specifying mutation column "i". if _, err := sqlDB.Exec(`UPDATE t.test SET v = 'u' WHERE k = 'a'`); err != nil { t.Fatal(err) } // Make column "i" live so that it is read. mTest.makeMutationsActive() // The update to column "v" is seen; there is no effect on column "i". _ = mTest.checkQueryResponse(starQuery, afterUpdate) // Make column "i" a mutation. mTest.writeColumnMutation("i", csql.DescriptorMutation{State: state}) // Delete row "a". if _, err := sqlDB.Exec(`DELETE FROM t.test WHERE k = 'a'`); err != nil { t.Fatal(err) } // Make column "i" live so that it is read. mTest.makeMutationsActive() // Row "a" is deleted. numVals is the number of non-NULL values seen, // or the number of KV values belonging to all the rows in the table // excluding row "a" since it's deleted. numVals := mTest.checkQueryResponse(starQuery, afterDelete) // Check that there are no hidden KV values for row "a", // and column "i" for row "a" was deleted. mTest.checkTableSize(numVals) } // Check that a mutation can only be inserted with an explicit mutation state, and direction. tableDesc := desc.GetTable() tableDesc.Mutations = []csql.DescriptorMutation{{}} if err := tableDesc.Validate(); !testutils.IsError(err, "mutation in state UNKNOWN, direction NONE, and no column/index descriptor") { t.Fatal(err) } tableDesc.Mutations = []csql.DescriptorMutation{{Descriptor_: &csql.DescriptorMutation_Column{Column: &tableDesc.Columns[len(tableDesc.Columns)-1]}}} tableDesc.Columns = tableDesc.Columns[:len(tableDesc.Columns)-1] if err := tableDesc.Validate(); !testutils.IsError(err, "mutation in state UNKNOWN, direction NONE, col i, id 3") { t.Fatal(err) } tableDesc.Mutations[0].State = csql.DescriptorMutation_DELETE_ONLY if err := tableDesc.Validate(); !testutils.IsError(err, "mutation in state DELETE_ONLY, direction NONE, col i, id 3") { t.Fatal(err) } tableDesc.Mutations[0].State = csql.DescriptorMutation_UNKNOWN tableDesc.Mutations[0].Direction = csql.DescriptorMutation_DROP if err := tableDesc.Validate(); !testutils.IsError(err, "mutation in state UNKNOWN, direction DROP, col i, id 3") { t.Fatal(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()() // Disable external processing of mutations. defer csql.TestDisableAsyncSchemaChangeExec()() server, sqlDB, kvDB := setup(t) defer cleanup(server, sqlDB) var id = csql.ID(keys.MaxReservedDescID + 2) var node = roachpb.NodeID(2) db := server.DB() leaseMgr := csql.NewLeaseManager(0, *db, hlc.NewClock(hlc.UnixNano)) changer := csql.NewSchemaChangerForTesting(id, 0, node, *db, 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. nameKey := csql.MakeNameMetadataKey(keys.MaxReservedDescID+1, "test") gr, err := kvDB.Get(nameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := csql.MakeDescMetadataKey(csql.ID(gr.ValueInt())) desc := &csql.Descriptor{} // Check that MaybeIncrementVersion doesn't increment the version // when the up_version bit is not set. if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } expectedVersion := desc.GetTable().Version if err := changer.MaybeIncrementVersion(); err != nil { t.Fatal(err) } if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } newVersion := desc.GetTable().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", desc.GetTable()) } // Check that MaybeIncrementVersion increments the version // correctly. expectedVersion++ desc.GetTable().UpVersion = true if err := kvDB.Put(descKey, desc); 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()) } if err := changer.MaybeIncrementVersion(); err != nil { t.Fatal(err) } if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } newVersion = desc.GetTable().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", desc.GetTable()) } // Check that RunStateMachineBeforeBackfill doesn't do anything // if there are no mutations queued. if err := changer.RunStateMachineBeforeBackfill(); err != nil { t.Fatal(err) } if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } newVersion = desc.GetTable().Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } // Check that RunStateMachineBeforeBackfill functions properly. if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } table := desc.GetTable() expectedVersion = table.Version // Make a copy of the index for use in a mutation. index := proto.Clone(&table.Indexes[0]).(*csql.IndexDescriptor) index.Name = "bar" index.ID = table.NextIndexID table.NextIndexID++ changer = csql.NewSchemaChangerForTesting(id, table.NextMutationID, node, *db, leaseMgr) table.Mutations = append(table.Mutations, csql.DescriptorMutation{ Descriptor_: &csql.DescriptorMutation_Index{Index: index}, Direction: csql.DescriptorMutation_ADD, State: csql.DescriptorMutation_DELETE_ONLY, MutationID: table.NextMutationID, }) table.NextMutationID++ // Run state machine in both directions. for _, direction := range []csql.DescriptorMutation_Direction{csql.DescriptorMutation_ADD, csql.DescriptorMutation_DROP} { table.Mutations[0].Direction = direction expectedVersion++ if err := kvDB.Put(descKey, desc); err != nil { t.Fatal(err) } // The expected end state. expectedState := csql.DescriptorMutation_WRITE_ONLY if direction == csql.DescriptorMutation_DROP { expectedState = csql.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) } if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } table = desc.GetTable() newVersion = table.Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } state := table.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", desc.GetTable()) } }
// Test schema change backfills are not affected by various operations // that run simultaneously. func TestRaceWithBackfill(t *testing.T) { defer leaktest.AfterTest(t)() // Disable asynchronous schema change execution to allow synchronous path // to trigger start of backfill notification. defer csql.TestDisableAsyncSchemaChangeExec()() var execKnobs csql.ExecutorTestingKnobs var backfillNotification chan bool execKnobs.SchemaChangersStartBackfillNotification = func() { if backfillNotification != nil { // Close channel to notify that the backfill has started. close(backfillNotification) } } ctx, _ := createTestServerContext() ctx.TestingKnobs.SQLExecutor = &execKnobs server, sqlDB, kvDB := setupWithContext(t, ctx) defer cleanup(server, sqlDB) if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v INT, pi DECIMAL DEFAULT (DECIMAL '3.14')); CREATE UNIQUE INDEX vidx ON t.test (v); `); err != nil { t.Fatal(err) } // Bulk insert. maxValue := 4000 insert := fmt.Sprintf(`INSERT INTO t.test VALUES (%d, %d)`, 0, maxValue) for i := 1; i <= maxValue; i++ { insert += fmt.Sprintf(` ,(%d, %d)`, i, maxValue-i) } if _, err := sqlDB.Exec(insert); err != nil { t.Fatal(err) } // Read table descriptor for version. nameKey := sqlbase.MakeNameMetadataKey(keys.MaxReservedDescID+1, "test") gr, err := kvDB.Get(nameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(gr.ValueInt())) desc := &sqlbase.Descriptor{} if err := kvDB.GetProto(descKey, desc); err != nil { t.Fatal(err) } tableDesc := desc.GetTable() tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() // number of keys == 4 * number of rows; 3 columns and 1 index entry for // each row. if kvs, err := kvDB.Scan(tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := 4 * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // Run some schema changes with operations. // Add column. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "ALTER TABLE t.test ADD COLUMN x DECIMAL DEFAULT (DECIMAL '1.4')", maxValue, 5, descKey, backfillNotification) // Drop column. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "ALTER TABLE t.test DROP pi", maxValue, 4, descKey, backfillNotification) // Add index. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "CREATE UNIQUE INDEX foo ON t.test (v)", maxValue, 5, descKey, backfillNotification) // Drop index. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "DROP INDEX t.test@vidx", maxValue, 4, descKey, backfillNotification) // Verify that the index foo over v is consistent, and that column x has // been backfilled properly. rows, err := sqlDB.Query(`SELECT v, x from t.test@foo`) if err != nil { t.Fatal(err) } count := 0 for ; rows.Next(); count++ { var val int var x float64 if err := rows.Scan(&val, &x); err != nil { t.Errorf("row %d scan failed: %s", count, err) continue } if count != val { t.Errorf("e = %d, v = %d", count, val) } if 1.4 != x { t.Errorf("e = %f, v = %f", 1.4, x) } } if err := rows.Err(); err != nil { t.Fatal(err) } eCount := maxValue + 1 if eCount != count { t.Fatalf("read the wrong number of rows: e = %d, v = %d", eCount, count) } // Verify that a table delete in the middle of a backfill works properly. // The backfill will terminate in the middle, and the delete will // successfully delete all the table data. // // This test could be made its own test but is placed here to speed up the // testing. backfillNotification = make(chan bool) // Run the schema change in a separate goroutine. var wg sync.WaitGroup wg.Add(1) go func() { // Start schema change that eventually runs a backfill. if _, err := sqlDB.Exec("CREATE UNIQUE INDEX bar ON t.test (v)"); err != nil { t.Error(err) } wg.Done() }() // Wait until the schema change backfill starts. <-backfillNotification // Wait for a short bit to ensure that the backfill has likely progressed // and written some data, but not long enough that the backfill has // completed. time.Sleep(10 * time.Millisecond) if _, err := sqlDB.Exec("DROP TABLE t.test"); err != nil { t.Fatal(err) } // Wait until the schema change is done. wg.Wait() // Ensure that the table data has been deleted. if kvs, err := kvDB.Scan(tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if len(kvs) != 0 { t.Fatalf("expected %d key value pairs, but got %d", 0, len(kvs)) } }
// Test that once a table is marked as deleted, a lease's refcount dropping to 0 // means the lease is released immediately, as opposed to being released only // when it expires. func TestLeasesOnDeletedTableAreReleasedImmediately(t *testing.T) { defer leaktest.AfterTest(t)() defer csql.TestDisableAsyncSchemaChangeExec()() var execKnobs csql.ExecutorTestingKnobs var lmKnobs csql.LeaseManagerTestingKnobs var mu sync.Mutex clearSchemaChangers := false execKnobs.SyncSchemaChangersFilter = func(tscc csql.TestingSchemaChangerCollection) { mu.Lock() defer mu.Unlock() if clearSchemaChangers { tscc.ClearSchemaChangers() } } var waitTableID sqlbase.ID deleted := make(chan bool) lmKnobs.TestingLeasesRefreshedEvent = func(cfg config.SystemConfig) { mu.Lock() defer mu.Unlock() if waitTableID != 0 { if isDeleted(waitTableID, cfg) { close(deleted) waitTableID = 0 } } } ctx, _ := createTestServerContext() ctx.TestingKnobs.SQLExecutor = &execKnobs ctx.TestingKnobs.SQLLeaseManager = &lmKnobs s, db, kvDB := setupWithContext(t, ctx) defer cleanup(s, db) sql := ` CREATE DATABASE test; CREATE TABLE test.t(a INT PRIMARY KEY); ` _, err := db.Exec(sql) if err != nil { t.Fatal(err) } tableDesc := getTableDescriptor(kvDB, "test", "t") lease1, err := acquire(s.TestServer, tableDesc.ID, 0) if err != nil { t.Fatal(err) } lease2, err := acquire(s.TestServer, tableDesc.ID, 0) if err != nil { t.Fatal(err) } // Block schema changers so that the table we're about to DROP is not actually // dropped; it will be left in a "deleted" state. // Also install a way to wait for the config update to be processed. mu.Lock() clearSchemaChangers = true waitTableID = tableDesc.ID mu.Unlock() // DROP the table _, err = db.Exec(`DROP TABLE test.t`) if err != nil { t.Fatal(err) } // Block until the LeaseManager has processed the gossip update. <-deleted // We should still be able to acquire, because we have an active lease. lease3, err := acquire(s.TestServer, tableDesc.ID, 0) if err != nil { t.Fatal(err) } // Release everything. if err := s.LeaseManager().Release(lease1); err != nil { t.Fatal(err) } if err := s.LeaseManager().Release(lease2); err != nil { t.Fatal(err) } if err := s.LeaseManager().Release(lease3); err != nil { t.Fatal(err) } // Now we shouldn't be able to acquire any more. _, err = acquire(s.TestServer, tableDesc.ID, 0) if !testutils.IsError(err, "descriptor deleted") { t.Fatalf("got a different error than expected: %s", err) } }