// addTableHistoryInfo adds schema version and table information that are used for binlog. // tblInfo is added except for the following operations: create database, drop database. func addTableHistoryInfo(job *model.Job, ver int64, tblInfo *model.TableInfo) { // TODO: Remove it. // This is for compatibility with previous version. job.Args = []interface{}{ver, tblInfo} if job.BinlogInfo != nil { job.BinlogInfo.AddTableInfo(ver, tblInfo) } }
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) onDropSchema(t *meta.Meta, job *model.Job) error { dbInfo, err := t.GetDatabase(job.SchemaID) if err != nil { return errors.Trace(err) } if dbInfo == nil { job.State = model.JobCancelled return errors.Trace(infoschema.ErrDatabaseDropExists) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch dbInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly dbInfo.State = model.StateWriteOnly err = t.UpdateDatabase(dbInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly dbInfo.State = model.StateDeleteOnly err = t.UpdateDatabase(dbInfo) case model.StateDeleteOnly: dbInfo.State = model.StateNone tables, err := t.ListTables(job.SchemaID) if err != nil { return errors.Trace(err) } err = t.UpdateDatabase(dbInfo) if err = t.DropDatabase(dbInfo.ID); err != nil { break } // finish this job addDBHistoryInfo(job, ver, dbInfo) if len(tables) > 0 { job.Args = append(job.Args, getIDs(tables)) } job.State = model.JobDone job.SchemaState = model.StateNone default: // we can't enter here. err = errors.Errorf("invalid db state %v", dbInfo.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) 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) onDropSchema(t *meta.Meta, job *model.Job) error { dbInfo, err := t.GetDatabase(job.SchemaID) if err != nil { return errors.Trace(err) } if dbInfo == nil { job.State = model.JobCancelled return errors.Trace(infoschema.ErrDatabaseNotExists) } _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch dbInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly dbInfo.State = model.StateWriteOnly err = t.UpdateDatabase(dbInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly dbInfo.State = model.StateDeleteOnly err = t.UpdateDatabase(dbInfo) case model.StateDeleteOnly: dbInfo.State = model.StateDeleteReorganization err = t.UpdateDatabase(dbInfo) if err = t.DropDatabase(dbInfo.ID); err != nil { break } // finish this job job.Args = []interface{}{dbInfo} job.State = model.JobDone job.SchemaState = model.StateNone default: // we can't enter here. err = errors.Errorf("invalid db state %v", dbInfo.State) } return errors.Trace(err) }
// onTruncateTable delete old table meta, and creates a new table identical to old table except for table ID. // As all the old data is encoded with old table ID, it can not be accessed any more. // A background job will be created to delete old data. func (d *ddl) onTruncateTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tableID := job.TableID var newTableID int64 err := job.DecodeArgs(&newTableID) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } 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.DropTable(schemaID, tableID) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } tblInfo.ID = newTableID err = t.CreateTable(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.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) startKey := tablecodec.EncodeTablePrefix(tableID) job.Args = append(job.Args, startKey) return nil }
func (d *ddl) dropSchemaData(tIDs []int64, startKey kv.Key, job *model.Job, m *meta.Meta) (bool, error) { if len(tIDs) == 0 { return true, nil } var isFinished bool var nextStartKey kv.Key for i, id := range tIDs { job.TableID = id if startKey == nil { startKey = tablecodec.EncodeTablePrefix(id) } limit := defaultBatchSize delCount, err := d.dropTableData(startKey, job, limit) if err != nil { return false, errors.Trace(err) } if delCount == limit { isFinished = false nextStartKey = job.Args[len(job.Args)-1].(kv.Key) break } if i < len(tIDs)-1 { tIDs = tIDs[i+1:] } else { tIDs = nil } startKey = nil isFinished = true continue } job.TableID = 0 job.Args = []interface{}{tIDs, nextStartKey} return isFinished, nil }
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) } }
// dropTableData deletes data in a limited number. If limit < 0, deletes all data. func (d *ddl) dropTableData(startKey kv.Key, job *model.Job, limit int) (int, error) { prefix := tablecodec.EncodeTablePrefix(job.TableID) delCount, nextStartKey, err := d.delKeysWithStartKey(prefix, startKey, bgJobFlag, job, limit) job.Args = []interface{}{nextStartKey} return delCount, errors.Trace(err) }
// addTableHistoryInfo adds schema version and table information that are used for binlog. // tblInfo is added except for the following operations: create database, drop database. func addTableHistoryInfo(job *model.Job, ver int64, tblInfo *model.TableInfo) { job.Args = []interface{}{ver, tblInfo} }
// addDBHistoryInfo adds schema version and schema information that are used for binlog. // dbInfo is added in the following operations: create database, drop database. func addDBHistoryInfo(job *model.Job, ver int64, dbInfo *model.DBInfo) { job.Args = []interface{}{ver, dbInfo} }
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) }