func (d *ddl) getReorgInfo(t *meta.Meta, job *model.Job) (*reorgInfo, error) { var err error info := &reorgInfo{ Job: job, d: d, first: job.SnapshotVer == 0, } if info.first { // get the current version for reorganization if we don't have var ver kv.Version ver, err = d.store.CurrentVersion() if err != nil { return nil, errors.Trace(err) } else if ver.Ver <= 0 { return nil, errors.Errorf("invalid storage current version %d", ver.Ver) } job.SnapshotVer = ver.Ver } else { info.Handle, err = t.GetDDLReorgHandle(job) if err != nil { return nil, errors.Trace(err) } } if info.Handle > 0 { // we have already handled this handle, so use next info.Handle++ } return info, errors.Trace(err) }
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) 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) 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) } }