func (d *ddl) onModifyColumn(t *meta.Meta, job *model.Job) error { tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } newCol := &model.ColumnInfo{} oldColName := &model.CIStr{} err = job.DecodeArgs(newCol, oldColName) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } oldCol := findCol(tblInfo.Columns, oldColName.L) if oldCol == nil || oldCol.State != model.StatePublic { job.State = model.JobCancelled return infoschema.ErrColumnNotExists.GenByArgs(newCol.Name, tblInfo.Name) } *oldCol = *newCol err = t.UpdateTable(job.SchemaID, tblInfo) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } job.SchemaState = model.StatePublic job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) return nil }
func (d *ddl) onDropForeignKey(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var ( fkName model.CIStr found bool fkInfo model.FKInfo ) err = job.DecodeArgs(&fkName) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } for _, fk := range tblInfo.ForeignKeys { if fk.Name.L == fkName.L { found = true fkInfo = *fk } } if !found { return infoschema.ErrForeignKeyNotExists.Gen("foreign key doesn't exist", fkName) } nfks := tblInfo.ForeignKeys[:0] for _, fk := range tblInfo.ForeignKeys { if fk.Name.L != fkName.L { nfks = append(nfks, fk) } } tblInfo.ForeignKeys = nfks _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch fkInfo.State { case model.StatePublic: // We just support record the foreign key, so we just make it none. // public -> none job.SchemaState = model.StateNone fkInfo.State = model.StateNone err = t.UpdateTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } // finish this job job.State = model.JobDone return nil default: return ErrInvalidForeignKeyState.Gen("invalid fk state %v", fkInfo.State) } }
func (d *ddl) convert2RollbackJob(t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, indexInfo *model.IndexInfo) error { job.State = model.JobRollback job.Args = []interface{}{indexInfo.Name} // If add index job rollbacks in write reorganization state, its need to delete all keys which has been added. // Its work is the same as drop index job do. // The write reorganization state in add index job that likes write only state in drop index job. // So the next state is delete only state. indexInfo.State = model.StateDeleteOnly job.SchemaState = model.StateDeleteOnly err := t.UpdateTable(job.SchemaID, tblInfo) if err != nil { return errors.Trace(err) } err = kv.ErrKeyExists.Gen("Duplicate for key %s", indexInfo.Name.O) return errors.Trace(err) }
func (d *ddl) onCreateForeignKey(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var fkInfo model.FKInfo err = job.DecodeArgs(&fkInfo) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } fkInfo.ID = allocateIndexID(tblInfo) tblInfo.ForeignKeys = append(tblInfo.ForeignKeys, &fkInfo) ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch fkInfo.State { case model.StateNone: // We just support record the foreign key, so we just make it public. // none -> public job.SchemaState = model.StatePublic fkInfo.State = model.StatePublic err = t.UpdateTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } // Finish this job. job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) return nil default: return ErrInvalidForeignKeyState.Gen("invalid fk state %v", fkInfo.State) } }
func (d *ddl) onDropTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tableID := job.TableID // Check this table's database. tblInfo, err := t.GetTable(schemaID, tableID) if terror.ErrorEqual(err, meta.ErrDBNotExists) { job.State = model.JobCancelled return errors.Trace(infoschema.ErrDatabaseNotExists) } else if err != nil { return errors.Trace(err) } // Check the table. if tblInfo == nil { job.State = model.JobCancelled return errors.Trace(infoschema.ErrTableNotExists) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch tblInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly tblInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly tblInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteOnly: tblInfo.State = model.StateNone err = t.UpdateTable(schemaID, tblInfo) if err = t.DropTable(job.SchemaID, job.TableID); err != nil { break } // Finish this job. job.State = model.JobDone job.SchemaState = model.StateNone addTableHistoryInfo(job, ver, tblInfo) startKey := tablecodec.EncodeTablePrefix(tableID) job.Args = append(job.Args, startKey) default: err = ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) } return errors.Trace(err) }
func (d *ddl) onDropTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tableID := job.TableID tblInfo, err := t.GetTable(schemaID, tableID) if terror.ErrorEqual(err, meta.ErrDBNotExists) { job.State = model.JobCancelled return errors.Trace(infoschema.ErrDatabaseNotExists) } else if err != nil { return errors.Trace(err) } if tblInfo == nil { job.State = model.JobCancelled return errors.Trace(infoschema.ErrTableNotExists) } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch tblInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly tblInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly tblInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteOnly: tblInfo.State = model.StateNone err = t.UpdateTable(schemaID, tblInfo) if err = t.DropTable(job.SchemaID, job.TableID); err != nil { break } // finish this job job.Args = []interface{}{tblInfo} job.State = model.JobDone job.SchemaState = model.StateNone default: err = ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) } return errors.Trace(err) }
func (d *ddl) onDropIndex(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var indexName model.CIStr if err = job.DecodeArgs(&indexName); err != nil { job.State = model.JobCancelled return errors.Trace(err) } var indexInfo *model.IndexInfo for _, idx := range tblInfo.Indices { if idx.Name.L == indexName.L { indexInfo = idx } } if indexInfo == nil { job.State = model.JobCancelled return ErrCantDropFieldOrKey.Gen("index %s doesn't exist", indexName) } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch indexInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly indexInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly indexInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> reorganization job.SchemaState = model.StateDeleteReorganization indexInfo.State = model.StateDeleteReorganization err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteReorganization: // reorganization -> absent tbl, err := d.getTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } err = d.runReorgJob(func() error { return d.dropTableIndex(tbl, indexInfo) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // if timeout, we should return, check for the owner and re-wait job done. return nil } if err != nil { return errors.Trace(err) } // all reorganization jobs done, drop this index newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) for _, idx := range tblInfo.Indices { if idx.Name.L != indexName.L { newIndices = append(newIndices, idx) } } tblInfo.Indices = newIndices // set column index flag. dropIndexColumnFlag(tblInfo, indexInfo) if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // finish this job job.SchemaState = model.StateNone job.State = model.JobDone return nil default: return ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) } }
func (d *ddl) onCreateIndex(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var ( unique bool indexName model.CIStr indexID int64 idxColNames []*ast.IndexColName ) err = job.DecodeArgs(&unique, &indexName, &indexID, &idxColNames) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } var indexInfo *model.IndexInfo for _, idx := range tblInfo.Indices { if idx.Name.L == indexName.L { if idx.State == model.StatePublic { // we already have a index with same index name job.State = model.JobCancelled return infoschema.ErrIndexExists.Gen("CREATE INDEX: index already exist %s", indexName) } indexInfo = idx } } if indexInfo == nil { indexInfo, err = buildIndexInfo(tblInfo, unique, indexName, indexID, idxColNames) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } tblInfo.Indices = append(tblInfo.Indices, indexInfo) } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch indexInfo.State { case model.StateNone: // none -> delete only job.SchemaState = model.StateDeleteOnly indexInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> write only job.SchemaState = model.StateWriteOnly indexInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> reorganization job.SchemaState = model.StateWriteReorganization indexInfo.State = model.StateWriteReorganization // initialize SnapshotVer to 0 for later reorganization check. job.SnapshotVer = 0 err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteReorganization: // reorganization -> public reorgInfo, err := d.getReorgInfo(t, job) if err != nil || reorgInfo.first { // if we run reorg firstly, we should update the job snapshot version // and then run the reorg next time. return errors.Trace(err) } var tbl table.Table tbl, err = d.getTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } err = d.runReorgJob(func() error { return d.addTableIndex(tbl, indexInfo, reorgInfo) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // if timeout, we should return, check for the owner and re-wait job done. return nil } if err != nil { return errors.Trace(err) } indexInfo.State = model.StatePublic // set column index flag. addIndexColumnFlag(tblInfo, indexInfo) if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // finish this job job.SchemaState = model.StatePublic job.State = model.JobDone return nil default: return ErrInvalidIndexState.Gen("invalid index state %v", tblInfo.State) } }
func (d *ddl) onDropTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tableID := job.TableID tblInfo, err := t.GetTable(schemaID, tableID) if terror.ErrorEqual(err, meta.ErrDBNotExists) { job.State = model.JobCancelled return errors.Trace(terror.DatabaseNotExists) } else if err != nil { return errors.Trace(err) } if tblInfo == nil { job.State = model.JobCancelled return errors.Trace(ErrNotExists) } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch tblInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly tblInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly tblInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> reorganization job.SchemaState = model.StateDeleteReorganization tblInfo.State = model.StateDeleteReorganization err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteReorganization: // reorganization -> absent var tbl table.Table tbl, err = d.getTable(t, schemaID, tblInfo) if err != nil { return errors.Trace(err) } err = d.runReorgJob(func() error { return d.dropTableData(tbl) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // if timeout, we should return, check for the owner and re-wait job done. return nil } if err != nil { return errors.Trace(err) } // all reorganization jobs done, drop this database if err = t.DropTable(schemaID, tableID); err != nil { return errors.Trace(err) } // finish this job job.SchemaState = model.StateNone job.State = model.JobDone return nil default: return errors.Errorf("invalid table state %v", tblInfo.State) } }
func (d *ddl) onAddColumn(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } col := &model.ColumnInfo{} pos := &ColumnPosition{} offset := 0 err = job.DecodeArgs(col, pos, &offset) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } columnInfo := findCol(tblInfo.Columns, col.Name.L) if columnInfo != nil { if columnInfo.State == model.StatePublic { // we already have a column with same column name job.State = model.JobCancelled return errors.Errorf("ADD COLUMN: column already exist %s", col.Name.L) } } else { columnInfo, offset, err = d.addColumn(tblInfo, col, pos) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } // Set offset arg to job. if offset != 0 { job.Args = []interface{}{columnInfo, pos, offset} } } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch columnInfo.State { case model.StateNone: // none -> delete only job.SchemaState = model.StateDeleteOnly columnInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> write only job.SchemaState = model.StateWriteOnly columnInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> reorganization job.SchemaState = model.StateWriteReorganization columnInfo.State = model.StateWriteReorganization // initialize SnapshotVer to 0 for later reorganization check. job.SnapshotVer = 0 err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteReorganization: // reorganization -> public // get the current version for reorganization if we don't have reorgInfo, err := d.getReorgInfo(t, job) if err != nil || reorgInfo.first { // if we run reorg firstly, we should update the job snapshot version // and then run the reorg next time. return errors.Trace(err) } tbl, err := d.getTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } err = d.runReorgJob(func() error { return d.backfillColumn(tbl, columnInfo, reorgInfo) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // if timeout, we should return, check for the owner and re-wait job done. return nil } if err != nil { return errors.Trace(err) } // Adjust column offset. d.adjustColumnOffset(tblInfo.Columns, tblInfo.Indices, offset, true) columnInfo.State = model.StatePublic if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // finish this job job.SchemaState = model.StatePublic job.State = model.JobDone return nil default: return errors.Errorf("invalid column state %v", columnInfo.State) } }
func (d *ddl) onDropColumn(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var colName model.CIStr err = job.DecodeArgs(&colName) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } colInfo := findCol(tblInfo.Columns, colName.L) if colInfo == nil { job.State = model.JobCancelled return errors.Errorf("column %s doesn't exist", colName) } if len(tblInfo.Columns) == 1 { job.State = model.JobCancelled return errors.Errorf("can't drop only column %s in table %s", colName, tblInfo.Name) } // we don't support drop column with index covered now. // we must drop the index first, then drop the column. for _, indexInfo := range tblInfo.Indices { for _, col := range indexInfo.Columns { if col.Name.L == colName.L { job.State = model.JobCancelled return errors.Errorf("can't drop column %s with index %s covered now", colName, indexInfo.Name) } } } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch colInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly colInfo.State = model.StateWriteOnly // set this column's offset to the last and reset all following columns' offset d.adjustColumnOffset(tblInfo.Columns, tblInfo.Indices, colInfo.Offset, false) err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly colInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> reorganization job.SchemaState = model.StateDeleteReorganization colInfo.State = model.StateDeleteReorganization // initialize SnapshotVer to 0 for later reorganization check. job.SnapshotVer = 0 err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteReorganization: // reorganization -> absent reorgInfo, err := d.getReorgInfo(t, job) if err != nil || reorgInfo.first { // if we run reorg firstly, we should update the job snapshot version // and then run the reorg next time. return errors.Trace(err) } tbl, err := d.getTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } err = d.runReorgJob(func() error { return d.dropTableColumn(tbl, colInfo, reorgInfo) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // if timeout, we should return, check for the owner and re-wait job done. return nil } if err != nil { return errors.Trace(err) } // all reorganization jobs done, drop this column newColumns := make([]*model.ColumnInfo, 0, len(tblInfo.Columns)) for _, col := range tblInfo.Columns { if col.Name.L != colName.L { newColumns = append(newColumns, col) } } tblInfo.Columns = newColumns if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // finish this job job.SchemaState = model.StateNone job.State = model.JobDone return nil default: return errors.Errorf("invalid table state %v", tblInfo.State) } }
func (d *ddl) onAddColumn(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } col := &model.ColumnInfo{} pos := &ast.ColumnPosition{} offset := 0 err = job.DecodeArgs(col, pos, &offset) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } columnInfo := findCol(tblInfo.Columns, col.Name.L) if columnInfo != nil { if columnInfo.State == model.StatePublic { // We already have a column with the same column name. job.State = model.JobCancelled return infoschema.ErrColumnExists.GenByArgs(col.Name) } } else { columnInfo, offset, err = d.createColumnInfo(tblInfo, col, pos) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } // Set offset arg to job. if offset != 0 { job.Args = []interface{}{columnInfo, pos, offset} } } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch columnInfo.State { case model.StateNone: // none -> delete only job.SchemaState = model.StateDeleteOnly columnInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteOnly: // delete only -> write only job.SchemaState = model.StateWriteOnly columnInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteOnly: // write only -> reorganization job.SchemaState = model.StateWriteReorganization columnInfo.State = model.StateWriteReorganization // Initialize SnapshotVer to 0 for later reorganization check. job.SnapshotVer = 0 err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteReorganization: // reorganization -> public // Get the current version for reorganization if we don't have it. reorgInfo, err := d.getReorgInfo(t, job) if err != nil || reorgInfo.first { // If we run reorg firstly, we should update the job snapshot version // and then run the reorg next time. return errors.Trace(err) } tbl, err := d.getTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } if columnInfo.DefaultValue != nil || mysql.HasNotNullFlag(columnInfo.Flag) { err = d.runReorgJob(func() error { return d.addTableColumn(tbl, columnInfo, reorgInfo, job) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // If the timeout happens, we should return. // Then check for the owner and re-wait job to finish. return nil } if err != nil { return errors.Trace(err) } } // Adjust column offset. d.adjustColumnOffset(tblInfo.Columns, tblInfo.Indices, offset, true) columnInfo.State = model.StatePublic if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // Finish this job. job.SchemaState = model.StatePublic job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) default: err = ErrInvalidColumnState.Gen("invalid column state %v", columnInfo.State) } return errors.Trace(err) }
func (d *ddl) onDropColumn(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var colName model.CIStr err = job.DecodeArgs(&colName) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } colInfo := findCol(tblInfo.Columns, colName.L) if colInfo == nil { job.State = model.JobCancelled return ErrCantDropFieldOrKey.Gen("column %s doesn't exist", colName) } if len(tblInfo.Columns) == 1 { job.State = model.JobCancelled return ErrCantRemoveAllFields.Gen("can't drop only column %s in table %s", colName, tblInfo.Name) } // We don't support dropping column with index covered now. if isColumnWithIndex(colName.L, tblInfo.Indices) { job.State = model.JobCancelled return errCantDropColWithIndex.Gen("can't drop column %s with index covered now", colName) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch colInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly colInfo.State = model.StateWriteOnly // Set this column's offset to the last and reset all following columns' offsets. d.adjustColumnOffset(tblInfo.Columns, tblInfo.Indices, colInfo.Offset, false) err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly colInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteOnly: // delete only -> reorganization job.SchemaState = model.StateDeleteReorganization colInfo.State = model.StateDeleteReorganization // Initialize SnapshotVer to 0 for later reorganization check. job.SnapshotVer = 0 err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteReorganization: // reorganization -> absent reorgInfo, err := d.getReorgInfo(t, job) if err != nil || reorgInfo.first { // If we run reorg firstly, we should update the job snapshot version // and then run the reorg next time. return errors.Trace(err) } // All reorganization jobs are done, drop this column. newColumns := make([]*model.ColumnInfo, 0, len(tblInfo.Columns)) for _, col := range tblInfo.Columns { if col.Name.L != colName.L { newColumns = append(newColumns, col) } } tblInfo.Columns = newColumns if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // Finish this job. job.SchemaState = model.StateNone job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) default: err = ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) } return errors.Trace(err) }
func (d *ddl) onCreateTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tbInfo := &model.TableInfo{} if err := job.DecodeArgs(tbInfo); err != nil { // arg error, cancel this job. job.State = model.JobCancelled return errors.Trace(err) } tbInfo.State = model.StateNone tables, err := t.ListTables(schemaID) if terror.ErrorEqual(err, meta.ErrDBNotExists) { job.State = model.JobCancelled return errors.Trace(terror.DatabaseNotExists) } else if err != nil { return errors.Trace(err) } for _, tbl := range tables { if tbl.Name.L == tbInfo.Name.L { if tbl.ID != tbInfo.ID { // table exists, can't create, we should cancel this job now. job.State = model.JobCancelled return errors.Trace(ErrExists) } tbInfo = tbl } } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch tbInfo.State { case model.StateNone: // none -> delete only job.SchemaState = model.StateDeleteOnly tbInfo.State = model.StateDeleteOnly err = t.CreateTable(schemaID, tbInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> write only job.SchemaState = model.StateWriteOnly tbInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tbInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> public job.SchemaState = model.StatePublic tbInfo.State = model.StatePublic err = t.UpdateTable(schemaID, tbInfo) if err != nil { return errors.Trace(err) } // finish this job job.State = model.JobDone return nil default: return errors.Errorf("invalid table state %v", tbInfo.State) } }
func (d *ddl) onDropIndex(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var indexName model.CIStr if err = job.DecodeArgs(&indexName); err != nil { job.State = model.JobCancelled return errors.Trace(err) } indexInfo := findIndexByName(indexName.L, tblInfo.Indices) if indexInfo == nil { job.State = model.JobCancelled return ErrCantDropFieldOrKey.Gen("index %s doesn't exist", indexName) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch indexInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly indexInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly indexInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteOnly: // delete only -> reorganization job.SchemaState = model.StateDeleteReorganization indexInfo.State = model.StateDeleteReorganization err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteReorganization: // reorganization -> absent err = d.runReorgJob(func() error { return d.dropTableIndex(indexInfo, job) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // If the timeout happens, we should return. // Then check for the owner and re-wait job to finish. return nil } if err != nil { return errors.Trace(err) } // All reorganization jobs are done, drop this index. newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) for _, idx := range tblInfo.Indices { if idx.Name.L != indexName.L { newIndices = append(newIndices, idx) } } tblInfo.Indices = newIndices // Set column index flag. dropIndexColumnFlag(tblInfo, indexInfo) if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // Finish this job. job.SchemaState = model.StateNone if job.State == model.JobRollback { job.State = model.JobRollbackDone } else { job.State = model.JobDone } addTableHistoryInfo(job, ver, tblInfo) default: err = ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) } return errors.Trace(err) }
func (d *ddl) onCreateIndex(t *meta.Meta, job *model.Job) error { // Handle rollback job. if job.State == model.JobRollback { err := d.onDropIndex(t, job) if err != nil { return errors.Trace(err) } return nil } // Handle normal job. schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var ( unique bool indexName model.CIStr idxColNames []*ast.IndexColName ) err = job.DecodeArgs(&unique, &indexName, &idxColNames) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } indexInfo := findIndexByName(indexName.L, tblInfo.Indices) if indexInfo != nil && indexInfo.State == model.StatePublic { job.State = model.JobCancelled return errDupKeyName.Gen("index already exist %s", indexName) } if indexInfo == nil { indexInfo, err = buildIndexInfo(tblInfo, unique, indexName, idxColNames) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } indexInfo.ID = allocateIndexID(tblInfo) tblInfo.Indices = append(tblInfo.Indices, indexInfo) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch indexInfo.State { case model.StateNone: // none -> delete only job.SchemaState = model.StateDeleteOnly indexInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateDeleteOnly: // delete only -> write only job.SchemaState = model.StateWriteOnly indexInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteOnly: // write only -> reorganization job.SchemaState = model.StateWriteReorganization indexInfo.State = model.StateWriteReorganization // Initialize SnapshotVer to 0 for later reorganization check. job.SnapshotVer = 0 err = t.UpdateTable(schemaID, tblInfo) return errors.Trace(err) case model.StateWriteReorganization: // reorganization -> public reorgInfo, err := d.getReorgInfo(t, job) if err != nil || reorgInfo.first { // If we run reorg firstly, we should update the job snapshot version // and then run the reorg next time. return errors.Trace(err) } var tbl table.Table tbl, err = d.getTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } err = d.runReorgJob(func() error { return d.addTableIndex(tbl, indexInfo, reorgInfo, job) }) if terror.ErrorEqual(err, errWaitReorgTimeout) { // if timeout, we should return, check for the owner and re-wait job done. return nil } if err != nil { if terror.ErrorEqual(err, kv.ErrKeyExists) { log.Warnf("[ddl] run DDL job %v err %v, convert job to rollback job", job, err) err = d.convert2RollbackJob(t, job, tblInfo, indexInfo) } return errors.Trace(err) } indexInfo.State = model.StatePublic // Set column index flag. addIndexColumnFlag(tblInfo, indexInfo) if err = t.UpdateTable(schemaID, tblInfo); err != nil { return errors.Trace(err) } // Finish this job. job.SchemaState = model.StatePublic job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) return nil default: return ErrInvalidIndexState.Gen("invalid index state %v", tblInfo.State) } }