diff --git a/ddl/ddl.go b/ddl/ddl.go index 6fcbbcd6c5ce5..1527d4cd20035 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -426,7 +426,7 @@ func (d *ddl) AlterTable(ctx context.Context, ident table.Ident, specs []*AlterS for _, spec := range specs { switch spec.Action { case AlterAddColumn: - if err := d.addColumn(ident.Schema, tbl, spec, is.SchemaMetaVersion()); err != nil { + if err := d.addColumn(ctx, ident.Schema, tbl, spec, is.SchemaMetaVersion()); err != nil { return errors.Trace(err) } default: @@ -438,7 +438,7 @@ func (d *ddl) AlterTable(ctx context.Context, ident table.Ident, specs []*AlterS } // Add a column into table -func (d *ddl) addColumn(schema model.CIStr, tbl table.Table, spec *AlterSpecification, schemaMetaVersion int64) error { +func (d *ddl) addColumn(ctx context.Context, schema model.CIStr, tbl table.Table, spec *AlterSpecification, schemaMetaVersion int64) error { // Find position cols := tbl.Cols() position := len(cols) @@ -489,9 +489,11 @@ func (d *ddl) addColumn(schema model.CIStr, tbl table.Table, spec *AlterSpecific tb := tbl.(*tables.Table) tb.Columns = newCols // TODO: update index - // TODO: update default value - // update infomation schema + if err = updateOldRows(ctx, tb, col); err != nil { + return errors.Trace(err) + } + // update infomation schema err = kv.RunInNewTxn(d.store, false, func(txn kv.Transaction) error { err := d.verifySchemaMetaVersion(txn, schemaMetaVersion) if err != nil { @@ -506,6 +508,40 @@ func (d *ddl) addColumn(schema model.CIStr, tbl table.Table, spec *AlterSpecific return errors.Trace(err) } +func updateOldRows(ctx context.Context, t *tables.Table, col *column.Col) error { + txn, err := ctx.GetTxn(false) + if err != nil { + return errors.Trace(err) + } + it, err := txn.Seek([]byte(t.FirstKey()), nil) + if err != nil { + return errors.Trace(err) + } + defer it.Close() + + prefix := t.KeyPrefix() + for it.Valid() && strings.HasPrefix(it.Key(), prefix) { + handle, err0 := util.DecodeHandleFromRowKey(it.Key()) + if err0 != nil { + return errors.Trace(err0) + } + k := t.RecordKey(handle, col) + + // TODO: check and get timestamp/datetime default value. + // refer to getDefaultValue in stmt/stmts/stmt_helper.go. + if err0 = t.SetColValue(txn, k, col.DefaultValue); err0 != nil { + return errors.Trace(err0) + } + + rk := t.RecordKey(handle, nil) + if it, err0 = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)); err0 != nil { + return errors.Trace(err0) + } + } + + return nil +} + // drop table will proceed even if some table in the list does not exists func (d *ddl) DropTable(ctx context.Context, ti table.Ident) (err error) { is := d.GetInformationSchema() diff --git a/ddl/ddl_test.go b/ddl/ddl_test.go index b9cd64ee03eab..7343b9674ff35 100644 --- a/ddl/ddl_test.go +++ b/ddl/ddl_test.go @@ -23,6 +23,7 @@ import ( "github.com/pingcap/tidb/ddl" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/model" + mysql "github.com/pingcap/tidb/mysqldef" "github.com/pingcap/tidb/parser" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/stmt" @@ -78,13 +79,49 @@ func (ts *testSuite) TestT(c *C) { tbStmt = statement("create table t2 (a int unique not null)").(*stmts.CreateTableStmt) err = sessionctx.GetDomain(ctx).DDL().CreateTable(ctx, tbIdent2, tbStmt.Cols, tbStmt.Constraints) c.Assert(err, IsNil) + tb, err := sessionctx.GetDomain(ctx).InfoSchema().TableByName(tbIdent2.Schema, tbIdent2.Name) + c.Assert(err, IsNil) + c.Assert(tb, NotNil) + rid0, err := tb.AddRecord(ctx, []interface{}{1}) + c.Assert(err, IsNil) + rid1, err := tb.AddRecord(ctx, []interface{}{2}) + c.Assert(err, IsNil) + alterStmt := statement(`alter table t2 add b enum("bb") first`).(*stmts.AlterTableStmt) + sessionctx.GetDomain(ctx).DDL().AlterTable(ctx, tbIdent2, alterStmt.Specs) + c.Assert(alterStmt.Specs[0].String(), Not(Equals), "") + cols, err := tb.Row(ctx, rid0) + c.Assert(err, IsNil) + c.Assert(len(cols), Equals, 2) + c.Assert(cols[0], Equals, nil) + c.Assert(cols[1], Equals, int64(1)) + alterStmt = statement("alter table t2 add c varchar(255) after b").(*stmts.AlterTableStmt) + sessionctx.GetDomain(ctx).DDL().AlterTable(ctx, tbIdent2, alterStmt.Specs) + c.Assert(alterStmt.Specs[0].String(), Not(Equals), "") + tb, err = sessionctx.GetDomain(ctx).InfoSchema().TableByName(tbIdent2.Schema, tbIdent2.Name) + c.Assert(err, IsNil) + c.Assert(tb, NotNil) + cols, err = tb.Row(ctx, rid1) + c.Assert(err, IsNil) + c.Assert(len(cols), Equals, 3) + c.Assert(cols[0], Equals, nil) + c.Assert(cols[1], Equals, nil) + c.Assert(cols[2], Equals, int64(2)) + rid3, err := tb.AddRecord(ctx, []interface{}{mysql.Enum{Name: "bb", Value: 1}, "c", 3}) + c.Assert(err, IsNil) + cols, err = tb.Row(ctx, rid3) + c.Assert(err, IsNil) + c.Assert(len(cols), Equals, 3) + c.Assert(cols[0], Equals, mysql.Enum{Name: "bb", Value: 1}) + c.Assert(cols[1], Equals, "c") + c.Assert(cols[2], Equals, int64(3)) - tb, err := sessionctx.GetDomain(ctx).InfoSchema().TableByName(tbIdent.Schema, tbIdent.Name) + tb, err = sessionctx.GetDomain(ctx).InfoSchema().TableByName(tbIdent.Schema, tbIdent.Name) c.Assert(err, IsNil) + c.Assert(tb, NotNil) _, err = tb.AddRecord(ctx, []interface{}{1, "b", 2, 4}) c.Assert(err, IsNil) - alterStmt := statement("alter table t add column aa int first").(*stmts.AlterTableStmt) + alterStmt = statement("alter table t add column aa int first").(*stmts.AlterTableStmt) sessionctx.GetDomain(ctx).DDL().AlterTable(ctx, tbIdent, alterStmt.Specs) c.Assert(alterStmt.Specs[0].String(), Not(Equals), "") // Check indices info diff --git a/domain/domain.go b/domain/domain.go index 38a3dc794467e..8addb77d8fb93 100644 --- a/domain/domain.go +++ b/domain/domain.go @@ -52,7 +52,7 @@ func (do *Domain) loadInfoSchema(txn kv.Transaction) (err error) { if err != nil { return } - log.Info("loadInfoSchema %d", schemaMetaVersion) + log.Infof("loadInfoSchema %d", schemaMetaVersion) do.infoHandle.Set(schemas, schemaMetaVersion) return } diff --git a/table/tables/tables.go b/table/tables/tables.go index 901a7c77eecd6..fdb3f1e2f066f 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -263,21 +263,28 @@ func (t *Table) setOnUpdateData(ctx context.Context, touched []bool, data []inte return nil } +// SetColValue sets the column value. +// If the column untouched, we don't need to do this. +func (t *Table) SetColValue(txn kv.Transaction, key []byte, data interface{}) error { + v, err := t.EncodeValue(data) + if err != nil { + return errors.Trace(err) + } + if err := txn.Set(key, v); err != nil { + return errors.Trace(err) + } + return nil +} + func (t *Table) setNewData(ctx context.Context, h int64, data []interface{}) error { txn, err := ctx.GetTxn(false) if err != nil { return err } for _, col := range t.Cols() { - // set new value - // If column untouched, we do not need to do this k := t.RecordKey(h, col) - v, err := t.EncodeValue(data[col.Offset]) - if err != nil { - return err - } - if err := txn.Set([]byte(k), v); err != nil { - return err + if err := t.SetColValue(txn, k, data[col.Offset]); err != nil { + return errors.Trace(err) } } return nil @@ -365,13 +372,8 @@ func (t *Table) AddRecord(ctx context.Context, r []interface{}) (recordID int64, } // column key -> column value for _, c := range t.Cols() { - colKey := t.RecordKey(recordID, c) - data, err := t.EncodeValue(r[c.Offset]) - if err != nil { - return 0, err - } - err = txn.Set([]byte(colKey), data) - if err != nil { + k := t.RecordKey(recordID, c) + if err := t.SetColValue(txn, k, r[c.Offset]); err != nil { return 0, err } }