-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ddl: improve ddl test #1387
ddl: improve ddl test #1387
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
package ddl | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/juju/errors" | ||
"github.com/ngaut/log" | ||
. "github.com/pingcap/check" | ||
"github.com/pingcap/tidb/ast" | ||
"github.com/pingcap/tidb/context" | ||
"github.com/pingcap/tidb/kv" | ||
"github.com/pingcap/tidb/meta" | ||
"github.com/pingcap/tidb/meta/autoid" | ||
"github.com/pingcap/tidb/model" | ||
"github.com/pingcap/tidb/table" | ||
"github.com/pingcap/tidb/util/testleak" | ||
"github.com/pingcap/tidb/util/testutil" | ||
"github.com/pingcap/tidb/util/types" | ||
) | ||
|
||
var _ = Suite(&testColumnChangeSuite{}) | ||
|
||
type testColumnChangeSuite struct { | ||
store kv.Storage | ||
dbInfo *model.DBInfo | ||
} | ||
|
||
func (s *testColumnChangeSuite) SetUpSuite(c *C) { | ||
s.store = testCreateStore(c, "test_column_change") | ||
log.Errorf("test column change store %s", s.store.UUID()) | ||
s.dbInfo = &model.DBInfo{ | ||
Name: model.NewCIStr("test_column_change"), | ||
ID: 1, | ||
} | ||
err := kv.RunInNewTxn(s.store, true, func(txn kv.Transaction) error { | ||
t := meta.NewMeta(txn) | ||
err1 := errors.Trace(t.CreateDatabase(s.dbInfo)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not return the error directly? |
||
return errors.Trace(err1) | ||
}) | ||
c.Check(err, IsNil) | ||
} | ||
|
||
func (s *testColumnChangeSuite) TestColumnChange(c *C) { | ||
defer testleak.AfterTest(c)() | ||
d := newDDL(s.store, nil, nil, testLease) | ||
// create table t (c1 int, c2 int); | ||
tblInfo := testTableInfo(c, d, "t", 2) | ||
ctx := testNewContext(c, d) | ||
_, err := ctx.GetTxn(true) | ||
c.Assert(err, IsNil) | ||
testCreateTable(c, ctx, d, s.dbInfo, tblInfo) | ||
// insert t values (1, 2); | ||
originTable := testGetTable(c, d, s.dbInfo.ID, tblInfo.ID) | ||
row := types.MakeDatums(1, 2) | ||
_, err = originTable.AddRecord(ctx, row) | ||
c.Assert(err, IsNil) | ||
err = ctx.CommitTxn() | ||
c.Assert(err, IsNil) | ||
|
||
tc := &testDDLCallback{} | ||
// set up hook | ||
prevState := model.StateNone | ||
var ( | ||
deleteOnlyTable table.Table | ||
writeOnlyTable table.Table | ||
publicTable table.Table | ||
) | ||
var checkErr error | ||
tc.onJobUpdated = func(job *model.Job) { | ||
if job.SchemaState == prevState { | ||
return | ||
} | ||
prevState = job.SchemaState | ||
var err error | ||
switch job.SchemaState { | ||
case model.StateDeleteOnly: | ||
deleteOnlyTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) | ||
if err != nil { | ||
checkErr = errors.Trace(err) | ||
} | ||
case model.StateWriteOnly: | ||
writeOnlyTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) | ||
if err != nil { | ||
checkErr = errors.Trace(err) | ||
} | ||
err = s.checkAddWriteOnly(d, ctx, deleteOnlyTable, writeOnlyTable) | ||
if err != nil { | ||
checkErr = errors.Trace(err) | ||
} | ||
case model.StatePublic: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Miss StateReorg? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Write only reorg has the same operation rule as write only. |
||
publicTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) | ||
if err != nil { | ||
checkErr = errors.Trace(err) | ||
} | ||
err = s.checkAddPublic(d, ctx, writeOnlyTable, publicTable) | ||
if err != nil { | ||
checkErr = errors.Trace(err) | ||
} | ||
} | ||
} | ||
d.hook = tc | ||
defaultValue := int64(3) | ||
job := testCreateColumn(c, ctx, d, s.dbInfo, tblInfo, "c3", &ast.ColumnPosition{Tp: ast.ColumnPositionNone}, defaultValue) | ||
c.Assert(errors.ErrorStack(checkErr), Equals, "") | ||
testCheckJobDone(c, d, job, true) | ||
s.testColumnDrop(c, ctx, d, publicTable) | ||
d.close() | ||
} | ||
|
||
func (s *testColumnChangeSuite) testColumnDrop(c *C, ctx context.Context, d *ddl, tbl table.Table) { | ||
d.close() | ||
dropCol := tbl.Cols()[0] | ||
tc := &testDDLCallback{} | ||
// set up hook | ||
prevState := model.StateNone | ||
var checkErr error | ||
tc.onJobUpdated = func(job *model.Job) { | ||
if job.SchemaState == prevState { | ||
return | ||
} | ||
prevState = job.SchemaState | ||
currentTbl, err := getCurrentTable(d, s.dbInfo.ID, tbl.Meta().ID) | ||
if err != nil { | ||
checkErr = errors.Trace(err) | ||
} | ||
for _, col := range currentTbl.Cols() { | ||
if col.ID == dropCol.ID { | ||
checkErr = errors.Errorf("column is not dropped") | ||
} | ||
} | ||
} | ||
d.hook = tc | ||
d.start() | ||
testDropColumn(c, ctx, d, s.dbInfo, tbl.Meta(), dropCol.Name.L, false) | ||
} | ||
|
||
func (s *testColumnChangeSuite) checkAddWriteOnly(d *ddl, ctx context.Context, deleteOnlyTable, writeOnlyTable table.Table) error { | ||
// WriteOnlyTable: insert t values (2, 3) | ||
_, err := writeOnlyTable.AddRecord(ctx, types.MakeDatums(2, 3)) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = ctx.CommitTxn() | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = checkResult(ctx, writeOnlyTable, testutil.RowsWithSep(" ", "1 2 <nil>", "2 3 3")) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// DeleteOnlyTable: select * from t | ||
err = checkResult(ctx, deleteOnlyTable, testutil.RowsWithSep(" ", "1 2", "2 3")) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// WriteOnlyTable: update t set c1 = 2 where c1 = 1 | ||
h, _, err := writeOnlyTable.Seek(ctx, 0) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = writeOnlyTable.UpdateRecord(ctx, h, types.MakeDatums(1, 2), types.MakeDatums(2, 2), touchedMap(writeOnlyTable)) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = ctx.CommitTxn() | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// After we update the first row, its default value is also set. | ||
err = checkResult(ctx, writeOnlyTable, testutil.RowsWithSep(" ", "2 2 3", "2 3 3")) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// DeleteOnlyTable: delete from t where c2 = 2 | ||
err = deleteOnlyTable.RemoveRecord(ctx, h, types.MakeDatums(2, 2)) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = ctx.CommitTxn() | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// After delete table has deleted the first row, check the WriteOnly table records. | ||
err = checkResult(ctx, writeOnlyTable, testutil.RowsWithSep(" ", "2 3 3")) | ||
return errors.Trace(err) | ||
} | ||
|
||
func touchedMap(t table.Table) map[int]bool { | ||
touched := make(map[int]bool) | ||
for _, col := range t.Cols() { | ||
touched[col.Offset] = true | ||
} | ||
return touched | ||
} | ||
|
||
func (s *testColumnChangeSuite) checkAddPublic(d *ddl, ctx context.Context, writeOnlyTable, publicTable table.Table) error { | ||
// publicTable Insert t values (4, 4, 4) | ||
h, err := publicTable.AddRecord(ctx, types.MakeDatums(4, 4, 4)) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = ctx.CommitTxn() | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// writeOnlyTable update t set c1 = 3 where c1 = 4 | ||
oldRow, err := writeOnlyTable.RowWithCols(ctx, h, writeOnlyTable.WritableCols()) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
if len(oldRow) != 3 { | ||
return errors.Errorf("%v", oldRow) | ||
} | ||
newRow := types.MakeDatums(3, 4, oldRow[2].GetValue()) | ||
err = writeOnlyTable.UpdateRecord(ctx, h, oldRow, newRow, touchedMap(writeOnlyTable)) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = ctx.CommitTxn() | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// publicTable select * from t, make sure the new c3 value 4 is not overwritten to default value 3. | ||
err = checkResult(ctx, publicTable, testutil.RowsWithSep(" ", "2 3 3", "3 4 4")) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
return nil | ||
} | ||
|
||
func getCurrentTable(d *ddl, schemaID, tableID int64) (table.Table, error) { | ||
var tblInfo *model.TableInfo | ||
err := kv.RunInNewTxn(d.store, false, func(txn kv.Transaction) error { | ||
t := meta.NewMeta(txn) | ||
var err error | ||
tblInfo, err = t.GetTable(schemaID, tableID) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
alloc := autoid.NewAllocator(d.store, schemaID) | ||
tbl, err := table.TableFromMeta(alloc, tblInfo) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
return tbl, err | ||
} | ||
|
||
func checkResult(ctx context.Context, t table.Table, rows [][]interface{}) error { | ||
var gotRows [][]interface{} | ||
t.IterRecords(ctx, t.FirstKey(), t.WritableCols(), func(h int64, data []types.Datum, cols []*table.Column) (bool, error) { | ||
gotRows = append(gotRows, datumsToInterfaces(data)) | ||
return true, nil | ||
}) | ||
got := fmt.Sprintf("%v", gotRows) | ||
expect := fmt.Sprintf("%v", rows) | ||
if got != expect { | ||
return errors.Errorf("expect %v, got %v", expect, got) | ||
} | ||
return nil | ||
} | ||
|
||
func datumsToInterfaces(datums []types.Datum) []interface{} { | ||
var ifs []interface{} | ||
for _, d := range datums { | ||
ifs = append(ifs, d.GetValue()) | ||
} | ||
return ifs | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,6 @@ package ddl | |
|
||
import ( | ||
"reflect" | ||
"time" | ||
|
||
"github.com/juju/errors" | ||
. "github.com/pingcap/check" | ||
|
@@ -43,19 +42,15 @@ type testColumnSuite struct { | |
} | ||
|
||
func (s *testColumnSuite) SetUpSuite(c *C) { | ||
trySkipTest(c) | ||
|
||
s.store = testCreateStore(c, "test_column") | ||
lease := 50 * time.Millisecond | ||
s.d = newDDL(s.store, nil, nil, lease) | ||
s.d = newDDL(s.store, nil, nil, testLease) | ||
|
||
s.dbInfo = testSchemaInfo(c, s.d, "test_column") | ||
testCreateSchema(c, mock.NewContext(), s.d, s.dbInfo) | ||
} | ||
|
||
func (s *testColumnSuite) TearDownSuite(c *C) { | ||
trySkipTest(c) | ||
|
||
//trySkipTest(c) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this? |
||
testDropSchema(c, mock.NewContext(), s.d, s.dbInfo) | ||
s.d.close() | ||
|
||
|
@@ -96,14 +91,12 @@ func testDropColumn(c *C, ctx context.Context, d *ddl, dbInfo *model.DBInfo, tbl | |
Type: model.ActionDropColumn, | ||
Args: []interface{}{model.NewCIStr(colName)}, | ||
} | ||
|
||
err := d.doDDLJob(ctx, job) | ||
if isError { | ||
c.Assert(err, NotNil) | ||
return nil | ||
} | ||
|
||
c.Assert(err, IsNil) | ||
c.Assert(errors.ErrorStack(err), Equals, "") | ||
return job | ||
} | ||
|
||
|
@@ -736,7 +729,7 @@ func (s *testColumnSuite) testGetColumn(t table.Table, name string, isExist bool | |
|
||
func (s *testColumnSuite) TestAddColumn(c *C) { | ||
defer testleak.AfterTest(c)() | ||
d := newDDL(s.store, nil, nil, 100*time.Millisecond) | ||
d := newDDL(s.store, nil, nil, testLease) | ||
tblInfo := testTableInfo(c, d, "t", 3) | ||
ctx := testNewContext(c, d) | ||
|
||
|
@@ -808,7 +801,7 @@ func (s *testColumnSuite) TestAddColumn(c *C) { | |
|
||
func (s *testColumnSuite) TestDropColumn(c *C) { | ||
defer testleak.AfterTest(c)() | ||
d := newDDL(s.store, nil, nil, 100*time.Millisecond) | ||
d := newDDL(s.store, nil, nil, testLease) | ||
tblInfo := testTableInfo(c, d, "t", 4) | ||
ctx := testNewContext(c, d) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use error for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove it.