github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/ddl/column.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package ddl 15 16 import ( 17 "github.com/insionng/yougam/libraries/juju/errors" 18 "github.com/insionng/yougam/libraries/ngaut/log" 19 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/column" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/infoschema" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/meta" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/table" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/table/tables" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/terror" 28 ) 29 30 func (d *ddl) adjustColumnOffset(columns []*model.ColumnInfo, indices []*model.IndexInfo, offset int, added bool) { 31 offsetChanged := make(map[int]int) 32 if added { 33 for i := offset + 1; i < len(columns); i++ { 34 offsetChanged[columns[i].Offset] = i 35 columns[i].Offset = i 36 } 37 columns[offset].Offset = offset 38 } else { 39 for i := offset + 1; i < len(columns); i++ { 40 offsetChanged[columns[i].Offset] = i - 1 41 columns[i].Offset = i - 1 42 } 43 columns[offset].Offset = len(columns) - 1 44 } 45 46 // TODO: index can't cover the add/remove column with offset now, we may check this later. 47 48 // Update index column offset info. 49 for _, idx := range indices { 50 for _, col := range idx.Columns { 51 newOffset, ok := offsetChanged[col.Offset] 52 if ok { 53 col.Offset = newOffset 54 } 55 } 56 } 57 } 58 59 func (d *ddl) addColumn(tblInfo *model.TableInfo, colInfo *model.ColumnInfo, pos *ast.ColumnPosition) (*model.ColumnInfo, int, error) { 60 // Check column name duplicate. 61 cols := tblInfo.Columns 62 position := len(cols) 63 64 // Get column position. 65 if pos.Tp == ast.ColumnPositionFirst { 66 position = 0 67 } else if pos.Tp == ast.ColumnPositionAfter { 68 c := findCol(cols, pos.RelativeColumn.Name.L) 69 if c == nil { 70 return nil, 0, infoschema.ErrColumnNotExists.Gen("no such column: %v", pos.RelativeColumn) 71 } 72 73 // Insert position is after the mentioned column. 74 position = c.Offset + 1 75 } 76 77 colInfo.State = model.StateNone 78 // To support add column asynchronous, we should mark its offset as the last column. 79 // So that we can use origin column offset to get value from row. 80 colInfo.Offset = len(cols) 81 82 // Insert col into the right place of the column list. 83 newCols := make([]*model.ColumnInfo, 0, len(cols)+1) 84 newCols = append(newCols, cols[:position]...) 85 newCols = append(newCols, colInfo) 86 newCols = append(newCols, cols[position:]...) 87 88 tblInfo.Columns = newCols 89 return colInfo, position, nil 90 } 91 92 func (d *ddl) onAddColumn(t *meta.Meta, job *model.Job) error { 93 schemaID := job.SchemaID 94 tblInfo, err := d.getTableInfo(t, job) 95 if err != nil { 96 return errors.Trace(err) 97 } 98 99 col := &model.ColumnInfo{} 100 pos := &ast.ColumnPosition{} 101 offset := 0 102 err = job.DecodeArgs(col, pos, &offset) 103 if err != nil { 104 job.State = model.JobCancelled 105 return errors.Trace(err) 106 } 107 108 columnInfo := findCol(tblInfo.Columns, col.Name.L) 109 if columnInfo != nil { 110 if columnInfo.State == model.StatePublic { 111 // we already have a column with same column name 112 job.State = model.JobCancelled 113 return infoschema.ErrColumnExists.Gen("ADD COLUMN: column already exist %s", col.Name.L) 114 } 115 } else { 116 columnInfo, offset, err = d.addColumn(tblInfo, col, pos) 117 if err != nil { 118 job.State = model.JobCancelled 119 return errors.Trace(err) 120 } 121 122 // Set offset arg to job. 123 if offset != 0 { 124 job.Args = []interface{}{columnInfo, pos, offset} 125 } 126 } 127 128 _, err = t.GenSchemaVersion() 129 if err != nil { 130 return errors.Trace(err) 131 } 132 133 switch columnInfo.State { 134 case model.StateNone: 135 // none -> delete only 136 job.SchemaState = model.StateDeleteOnly 137 columnInfo.State = model.StateDeleteOnly 138 err = t.UpdateTable(schemaID, tblInfo) 139 return errors.Trace(err) 140 case model.StateDeleteOnly: 141 // delete only -> write only 142 job.SchemaState = model.StateWriteOnly 143 columnInfo.State = model.StateWriteOnly 144 err = t.UpdateTable(schemaID, tblInfo) 145 return errors.Trace(err) 146 case model.StateWriteOnly: 147 // write only -> reorganization 148 job.SchemaState = model.StateWriteReorganization 149 columnInfo.State = model.StateWriteReorganization 150 // initialize SnapshotVer to 0 for later reorganization check. 151 job.SnapshotVer = 0 152 err = t.UpdateTable(schemaID, tblInfo) 153 return errors.Trace(err) 154 case model.StateWriteReorganization: 155 // reorganization -> public 156 // get the current version for reorganization if we don't have 157 reorgInfo, err := d.getReorgInfo(t, job) 158 if err != nil || reorgInfo.first { 159 // if we run reorg firstly, we should update the job snapshot version 160 // and then run the reorg next time. 161 return errors.Trace(err) 162 } 163 164 tbl, err := d.getTable(schemaID, tblInfo) 165 if err != nil { 166 return errors.Trace(err) 167 } 168 169 err = d.runReorgJob(func() error { 170 return d.backfillColumn(tbl, columnInfo, reorgInfo) 171 }) 172 173 if terror.ErrorEqual(err, errWaitReorgTimeout) { 174 // if timeout, we should return, check for the owner and re-wait job done. 175 return nil 176 } 177 if err != nil { 178 return errors.Trace(err) 179 } 180 181 // Adjust column offset. 182 d.adjustColumnOffset(tblInfo.Columns, tblInfo.Indices, offset, true) 183 184 columnInfo.State = model.StatePublic 185 186 if err = t.UpdateTable(schemaID, tblInfo); err != nil { 187 return errors.Trace(err) 188 } 189 190 // finish this job 191 job.SchemaState = model.StatePublic 192 job.State = model.JobDone 193 return nil 194 default: 195 return ErrInvalidColumnState.Gen("invalid column state %v", columnInfo.State) 196 } 197 } 198 199 func (d *ddl) onDropColumn(t *meta.Meta, job *model.Job) error { 200 schemaID := job.SchemaID 201 tblInfo, err := d.getTableInfo(t, job) 202 if err != nil { 203 return errors.Trace(err) 204 } 205 206 var colName model.CIStr 207 err = job.DecodeArgs(&colName) 208 if err != nil { 209 job.State = model.JobCancelled 210 return errors.Trace(err) 211 } 212 213 colInfo := findCol(tblInfo.Columns, colName.L) 214 if colInfo == nil { 215 job.State = model.JobCancelled 216 return infoschema.ErrColumnNotExists.Gen("column %s doesn't exist", colName) 217 } 218 219 if len(tblInfo.Columns) == 1 { 220 job.State = model.JobCancelled 221 return ErrCantRemoveAllFields.Gen("can't drop only column %s in table %s", 222 colName, tblInfo.Name) 223 } 224 225 // we don't support drop column with index covered now. 226 // we must drop the index first, then drop the column. 227 for _, indexInfo := range tblInfo.Indices { 228 for _, col := range indexInfo.Columns { 229 if col.Name.L == colName.L { 230 job.State = model.JobCancelled 231 return errCantDropColWithIndex.Gen("can't drop column %s with index %s covered now", 232 colName, indexInfo.Name) 233 } 234 } 235 } 236 237 _, err = t.GenSchemaVersion() 238 if err != nil { 239 return errors.Trace(err) 240 } 241 242 switch colInfo.State { 243 case model.StatePublic: 244 // public -> write only 245 job.SchemaState = model.StateWriteOnly 246 colInfo.State = model.StateWriteOnly 247 248 // set this column's offset to the last and reset all following columns' offset 249 d.adjustColumnOffset(tblInfo.Columns, tblInfo.Indices, colInfo.Offset, false) 250 251 err = t.UpdateTable(schemaID, tblInfo) 252 return errors.Trace(err) 253 case model.StateWriteOnly: 254 // write only -> delete only 255 job.SchemaState = model.StateDeleteOnly 256 colInfo.State = model.StateDeleteOnly 257 err = t.UpdateTable(schemaID, tblInfo) 258 return errors.Trace(err) 259 case model.StateDeleteOnly: 260 // delete only -> reorganization 261 job.SchemaState = model.StateDeleteReorganization 262 colInfo.State = model.StateDeleteReorganization 263 // initialize SnapshotVer to 0 for later reorganization check. 264 job.SnapshotVer = 0 265 err = t.UpdateTable(schemaID, tblInfo) 266 return errors.Trace(err) 267 case model.StateDeleteReorganization: 268 // reorganization -> absent 269 reorgInfo, err := d.getReorgInfo(t, job) 270 if err != nil || reorgInfo.first { 271 // if we run reorg firstly, we should update the job snapshot version 272 // and then run the reorg next time. 273 return errors.Trace(err) 274 } 275 276 tbl, err := d.getTable(schemaID, tblInfo) 277 if err != nil { 278 return errors.Trace(err) 279 } 280 281 err = d.runReorgJob(func() error { 282 return d.dropTableColumn(tbl, colInfo, reorgInfo) 283 }) 284 285 if terror.ErrorEqual(err, errWaitReorgTimeout) { 286 // if timeout, we should return, check for the owner and re-wait job done. 287 return nil 288 } 289 if err != nil { 290 return errors.Trace(err) 291 } 292 293 // all reorganization jobs done, drop this column 294 newColumns := make([]*model.ColumnInfo, 0, len(tblInfo.Columns)) 295 for _, col := range tblInfo.Columns { 296 if col.Name.L != colName.L { 297 newColumns = append(newColumns, col) 298 } 299 } 300 tblInfo.Columns = newColumns 301 if err = t.UpdateTable(schemaID, tblInfo); err != nil { 302 return errors.Trace(err) 303 } 304 305 // finish this job 306 job.SchemaState = model.StateNone 307 job.State = model.JobDone 308 return nil 309 default: 310 return ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) 311 } 312 } 313 314 // How to backfill column data in reorganization state? 315 // 1. Generate a snapshot with special version. 316 // 2. Traverse the snapshot, get every row in the table. 317 // 3. For one row, if the row has been already deleted, skip to next row. 318 // 4. If not deleted, check whether column data has existed, if existed, skip to next row. 319 // 5. If column data doesn't exist, backfill the column with default value and then continue to handle next row. 320 func (d *ddl) backfillColumn(t table.Table, columnInfo *model.ColumnInfo, reorgInfo *reorgInfo) error { 321 seekHandle := reorgInfo.Handle 322 version := reorgInfo.SnapshotVer 323 324 for { 325 handles, err := d.getSnapshotRows(t, version, seekHandle) 326 if err != nil { 327 return errors.Trace(err) 328 } else if len(handles) == 0 { 329 return nil 330 } 331 332 seekHandle = handles[len(handles)-1] + 1 333 err = d.backfillColumnData(t, columnInfo, handles, reorgInfo) 334 if err != nil { 335 return errors.Trace(err) 336 } 337 } 338 } 339 340 func (d *ddl) backfillColumnData(t table.Table, columnInfo *model.ColumnInfo, handles []int64, reorgInfo *reorgInfo) error { 341 for _, handle := range handles { 342 log.Info("[ddl] backfill column...", handle) 343 344 err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error { 345 if err := d.isReorgRunnable(txn); err != nil { 346 return errors.Trace(err) 347 } 348 349 // First check if row exists. 350 exist, err := checkRowExist(txn, t, handle) 351 if err != nil { 352 return errors.Trace(err) 353 } else if !exist { 354 // If row doesn't exist, skip it. 355 return nil 356 } 357 358 backfillKey := t.RecordKey(handle, &column.Col{ColumnInfo: *columnInfo}) 359 backfillValue, err := txn.Get(backfillKey) 360 if err != nil && !kv.IsErrNotFound(err) { 361 return errors.Trace(err) 362 } 363 if backfillValue != nil { 364 return nil 365 } 366 367 value, _, err := table.GetColDefaultValue(nil, columnInfo) 368 if err != nil { 369 return errors.Trace(err) 370 } 371 372 // must convert to the column field type. 373 v, err := value.ConvertTo(&columnInfo.FieldType) 374 if err != nil { 375 return errors.Trace(err) 376 } 377 378 err = lockRow(txn, t, handle) 379 if err != nil { 380 return errors.Trace(err) 381 } 382 383 err = tables.SetColValue(txn, backfillKey, v) 384 if err != nil { 385 return errors.Trace(err) 386 } 387 388 return errors.Trace(reorgInfo.UpdateHandle(txn, handle)) 389 }) 390 391 if err != nil { 392 return errors.Trace(err) 393 } 394 } 395 396 return nil 397 } 398 399 func (d *ddl) dropTableColumn(t table.Table, colInfo *model.ColumnInfo, reorgInfo *reorgInfo) error { 400 version := reorgInfo.SnapshotVer 401 seekHandle := reorgInfo.Handle 402 403 col := &column.Col{ColumnInfo: *colInfo} 404 for { 405 handles, err := d.getSnapshotRows(t, version, seekHandle) 406 if err != nil { 407 return errors.Trace(err) 408 } else if len(handles) == 0 { 409 return nil 410 } 411 412 seekHandle = handles[len(handles)-1] + 1 413 414 err = kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error { 415 if err1 := d.isReorgRunnable(txn); err1 != nil { 416 return errors.Trace(err1) 417 } 418 419 var h int64 420 for _, h = range handles { 421 key := t.RecordKey(h, col) 422 err1 := txn.Delete(key) 423 if err1 != nil && !terror.ErrorEqual(err1, kv.ErrNotExist) { 424 return errors.Trace(err1) 425 } 426 } 427 return errors.Trace(reorgInfo.UpdateHandle(txn, h)) 428 }) 429 if err != nil { 430 return errors.Trace(err) 431 } 432 } 433 }