func (d *ddl) deleteTableData(ctx context.Context, t table.Table) error { // Remove data err := t.Truncate(ctx) if err != nil { return errors.Trace(err) } txn, err := ctx.GetTxn(false) if err != nil { return errors.Trace(err) } // Remove indices for _, v := range t.Indices() { if v != nil && v.X != nil { if err = v.X.Drop(txn); err != nil { return errors.Trace(err) } } } // Remove auto ID key err = txn.Delete([]byte(meta.AutoIDKey(t.TableID()))) // Auto ID meta is created when the first time used, so it may not exist. if errors2.ErrorEqual(err, kv.ErrNotExist) { return nil } return errors.Trace(err) }
// Add a column into table func (d *ddl) addColumn(ctx context.Context, schema model.CIStr, tbl table.Table, spec *AlterSpecification) error { // Find position cols := tbl.Cols() position := len(cols) name := spec.Column.Name // Check column name duplicate dc := column.FindCol(cols, name) if dc != nil { return errors.Errorf("Try to add a column with the same name of an already exists column.") } if spec.Position.Type == ColumnPositionFirst { position = 0 } else if spec.Position.Type == ColumnPositionAfter { // Find the mentioned column c := column.FindCol(cols, spec.Position.RelativeColumn) if c == nil { return errors.Errorf("No such column: %v", name) } // insert position is after the mentioned column position = c.Offset + 1 } // TODO: Set constraint col, _, err := d.buildColumnAndConstraint(position, spec.Column) if err != nil { return errors.Trace(err) } // insert col into the right place of the column list newCols := make([]*column.Col, 0, len(cols)+1) newCols = append(newCols, cols[:position]...) newCols = append(newCols, col) newCols = append(newCols, cols[position:]...) // adjust position if position != len(cols) { offsetChange := make(map[int]int) for i := position + 1; i < len(newCols); i++ { offsetChange[newCols[i].Offset] = i newCols[i].Offset = i } // Update index offset info for _, idx := range tbl.Indices() { for _, c := range idx.Columns { newOffset, ok := offsetChange[c.Offset] if ok { c.Offset = newOffset } } } } tb := tbl.(*tables.Table) tb.Columns = newCols // TODO: update index // TODO: update default value // update infomation schema err = d.updateInfoSchema(ctx, schema, tb.Meta()) return errors.Trace(err) }
func getIndex(t table.Table, name string) *column.IndexedCol { for _, idx := range t.Indices() { // only public index can be read. if len(idx.Columns) == 1 && strings.EqualFold(idx.Columns[0].Name.L, name) { return idx } } return nil }
func getAnonymousIndex(t table.Table, colName model.CIStr) model.CIStr { id := 2 l := len(t.Indices()) indexName := colName for i := 0; i < l; i++ { if t.Indices()[i].Meta().Name.L == indexName.L { indexName = model.NewCIStr(fmt.Sprintf("%s_%d", colName.O, id)) i = -1 id++ } } return indexName }
// FindIndexByColName implements table.Table FindIndexByColName interface. func FindIndexByColName(t table.Table, name string) table.Index { for _, idx := range t.Indices() { // only public index can be read. if idx.Meta().State != model.StatePublic { continue } if len(idx.Meta().Columns) == 1 && strings.EqualFold(idx.Meta().Columns[0].Name.L, name) { return idx } } return nil }
func checkIndexExists(ctx context.Context, tbl table.Table, indexValue interface{}, handle int64, exists bool) error { txn, err := ctx.GetTxn(true) if err != nil { return errors.Trace(err) } idx := tbl.Indices()[0] doesExist, _, err := idx.Exist(txn, types.MakeDatums(indexValue), handle) if err != nil { return errors.Trace(err) } if exists != doesExist { if exists { return errors.New("index should exists") } return errors.New("index should not exists") } return nil }
func (d *ddl) deleteTableData(ctx context.Context, t table.Table) error { // Remove data. err := t.Truncate(ctx) if err != nil { return errors.Trace(err) } txn, err := ctx.GetTxn(false) if err != nil { return errors.Trace(err) } // Remove indices. for _, v := range t.Indices() { if v != nil && v.X != nil { if err = v.X.Drop(txn); err != nil { return errors.Trace(err) } } } return nil }
// prefetchIndices uses a BatchPrefetch to load all unique indices that very likely // will be checked later. The fetched data will be stored in kv cache. func (s *InsertIntoStmt) prefetchIndices(ctx context.Context, t table.Table, rows [][]interface{}) error { var keys []kv.Key for _, index := range t.Indices() { if !index.Unique { continue } for _, row := range rows { colVals, err := index.FetchValues(row) if err != nil { return errors.Trace(err) } key, distinct, err := index.X.GenIndexKey(colVals, 0) if err != nil { return errors.Trace(err) } if distinct { keys = append(keys, key) } } } // Only activate if we got more than 1 distinct index. if len(keys) <= 1 { return nil } txn, err := ctx.GetTxn(false) if err != nil { return errors.Trace(err) } err = txn.BatchPrefetch(keys) if err != nil { return errors.Trace(err) } return nil }
// Add a column into table func (d *ddl) addColumn(ctx context.Context, schema *model.DBInfo, tbl table.Table, spec *AlterSpecification, schemaMetaVersion int64) error { // Find position cols := tbl.Cols() position := len(cols) name := spec.Column.Name // Check column name duplicate. dc := column.FindCol(cols, name) if dc != nil { return errors.Errorf("Try to add a column with the same name of an already exists column.") } if spec.Position.Type == ColumnPositionFirst { position = 0 } else if spec.Position.Type == ColumnPositionAfter { // Find the mentioned column. c := column.FindCol(cols, spec.Position.RelativeColumn) if c == nil { return errors.Errorf("No such column: %v", name) } // Insert position is after the mentioned column. position = c.Offset + 1 } // TODO: set constraint col, _, err := d.buildColumnAndConstraint(position, spec.Column) if err != nil { return errors.Trace(err) } // insert col into the right place of the column list newCols := make([]*column.Col, 0, len(cols)+1) newCols = append(newCols, cols[:position]...) newCols = append(newCols, col) newCols = append(newCols, cols[position:]...) // adjust position if position != len(cols) { offsetChange := make(map[int]int) for i := position + 1; i < len(newCols); i++ { offsetChange[newCols[i].Offset] = i newCols[i].Offset = i } // Update index offset info for _, idx := range tbl.Indices() { for _, c := range idx.Columns { newOffset, ok := offsetChange[c.Offset] if ok { c.Offset = newOffset } } } } tb := tbl.(*tables.Table) tb.Columns = newCols // TODO: update index 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 { t := meta.NewMeta(txn) err := d.verifySchemaMetaVersion(t, schemaMetaVersion) if err != nil { return errors.Trace(err) } err = t.UpdateTable(schema.ID, tb.Meta()) return errors.Trace(err) }) if d.onDDLChange != nil { err = d.onDDLChange(err) } return errors.Trace(err) }
func (s *testIndexChangeSuite) TestIndexChange(c *C) { defer testleak.AfterTest(c)() d := newDDL(s.store, nil, nil, testLease) // create table t (c1 int primary key, c2 int); tblInfo := testTableInfo(c, d, "t", 2) tblInfo.Columns[0].Flag = mysql.PriKeyFlag | mysql.NotNullFlag tblInfo.PKIsHandle = true ctx := testNewContext(c, d) _, err := ctx.GetTxn(true) c.Assert(err, IsNil) testCreateTable(c, ctx, d, s.dbInfo, tblInfo) originTable := testGetTable(c, d, s.dbInfo.ID, tblInfo.ID) // insert t values (1, 1), (2, 2), (3, 3) _, err = originTable.AddRecord(ctx, types.MakeDatums(1, 1)) c.Assert(err, IsNil) _, err = originTable.AddRecord(ctx, types.MakeDatums(2, 2)) c.Assert(err, IsNil) _, err = originTable.AddRecord(ctx, types.MakeDatums(3, 3)) 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 checkErr error ) tc.onJobUpdated = func(job *model.Job) { if job.SchemaState == prevState { return } ctx1 := testNewContext(c, d) 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, ctx1, deleteOnlyTable, writeOnlyTable) if err != nil { checkErr = errors.Trace(err) } case model.StatePublic: publicTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) if err != nil { checkErr = errors.Trace(err) } err = s.checkAddPublic(d, ctx1, writeOnlyTable, publicTable) if err != nil { checkErr = errors.Trace(err) } } } d.setHook(tc) testCreateIndex(c, ctx, d, s.dbInfo, originTable.Meta(), false, "c2", "c2") c.Check(errors.ErrorStack(checkErr), Equals, "") d.Stop() prevState = model.StatePublic var noneTable table.Table tc.onJobUpdated = func(job *model.Job) { if job.SchemaState == prevState { return } prevState = job.SchemaState var err error switch job.SchemaState { case model.StateWriteOnly: writeOnlyTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) if err != nil { checkErr = errors.Trace(err) } err = s.checkDropWriteOnly(d, ctx, publicTable, writeOnlyTable) if err != nil { checkErr = errors.Trace(err) } case model.StateDeleteOnly: deleteOnlyTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) if err != nil { checkErr = errors.Trace(err) } err = s.checkDropDeleteOnly(d, ctx, writeOnlyTable, deleteOnlyTable) if err != nil { checkErr = errors.Trace(err) } case model.StateNone: noneTable, err = getCurrentTable(d, s.dbInfo.ID, tblInfo.ID) if err != nil { checkErr = errors.Trace(err) } if len(noneTable.Indices()) != 0 { checkErr = errors.New("index should have been dropped") } } } d.start() testDropIndex(c, ctx, d, s.dbInfo, publicTable.Meta(), "c2") c.Check(errors.ErrorStack(checkErr), Equals, "") }