github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/ddl/index.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/infoschema" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/meta" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 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 "github.com/insionng/yougam/libraries/pingcap/tidb/util" 29 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 30 ) 31 32 func buildIndexInfo(tblInfo *model.TableInfo, unique bool, indexName model.CIStr, indexID int64, 33 idxColNames []*ast.IndexColName) (*model.IndexInfo, error) { 34 // build offsets 35 idxColumns := make([]*model.IndexColumn, 0, len(idxColNames)) 36 for _, ic := range idxColNames { 37 col := findCol(tblInfo.Columns, ic.Column.Name.O) 38 if col == nil { 39 return nil, infoschema.ErrColumnNotExists.Gen("CREATE INDEX: column does not exist: %s", 40 ic.Column.Name.O) 41 } 42 43 idxColumns = append(idxColumns, &model.IndexColumn{ 44 Name: col.Name, 45 Offset: col.Offset, 46 Length: ic.Length, 47 }) 48 } 49 // create index info 50 idxInfo := &model.IndexInfo{ 51 ID: indexID, 52 Name: indexName, 53 Columns: idxColumns, 54 Unique: unique, 55 State: model.StateNone, 56 } 57 return idxInfo, nil 58 } 59 60 func addIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { 61 col := indexInfo.Columns[0] 62 63 if indexInfo.Unique && len(indexInfo.Columns) == 1 { 64 tblInfo.Columns[col.Offset].Flag |= mysql.UniqueKeyFlag 65 } else { 66 tblInfo.Columns[col.Offset].Flag |= mysql.MultipleKeyFlag 67 } 68 } 69 70 func dropIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { 71 col := indexInfo.Columns[0] 72 73 if indexInfo.Unique && len(indexInfo.Columns) == 1 { 74 tblInfo.Columns[col.Offset].Flag &= ^uint(mysql.UniqueKeyFlag) 75 } else { 76 tblInfo.Columns[col.Offset].Flag &= ^uint(mysql.MultipleKeyFlag) 77 } 78 79 // other index may still cover this col 80 for _, index := range tblInfo.Indices { 81 if index.Name.L == indexInfo.Name.L { 82 continue 83 } 84 85 if index.Columns[0].Name.L != col.Name.L { 86 continue 87 } 88 89 addIndexColumnFlag(tblInfo, index) 90 } 91 } 92 93 func (d *ddl) onCreateIndex(t *meta.Meta, job *model.Job) error { 94 schemaID := job.SchemaID 95 tblInfo, err := d.getTableInfo(t, job) 96 if err != nil { 97 return errors.Trace(err) 98 } 99 100 var ( 101 unique bool 102 indexName model.CIStr 103 indexID int64 104 idxColNames []*ast.IndexColName 105 ) 106 107 err = job.DecodeArgs(&unique, &indexName, &indexID, &idxColNames) 108 if err != nil { 109 job.State = model.JobCancelled 110 return errors.Trace(err) 111 } 112 113 var indexInfo *model.IndexInfo 114 for _, idx := range tblInfo.Indices { 115 if idx.Name.L == indexName.L { 116 if idx.State == model.StatePublic { 117 // we already have a index with same index name 118 job.State = model.JobCancelled 119 return infoschema.ErrIndexExists.Gen("CREATE INDEX: index already exist %s", indexName) 120 } 121 122 indexInfo = idx 123 } 124 } 125 126 if indexInfo == nil { 127 indexInfo, err = buildIndexInfo(tblInfo, unique, indexName, indexID, idxColNames) 128 if err != nil { 129 job.State = model.JobCancelled 130 return errors.Trace(err) 131 } 132 tblInfo.Indices = append(tblInfo.Indices, indexInfo) 133 } 134 135 _, err = t.GenSchemaVersion() 136 if err != nil { 137 return errors.Trace(err) 138 } 139 140 switch indexInfo.State { 141 case model.StateNone: 142 // none -> delete only 143 job.SchemaState = model.StateDeleteOnly 144 indexInfo.State = model.StateDeleteOnly 145 err = t.UpdateTable(schemaID, tblInfo) 146 return errors.Trace(err) 147 case model.StateDeleteOnly: 148 // delete only -> write only 149 job.SchemaState = model.StateWriteOnly 150 indexInfo.State = model.StateWriteOnly 151 err = t.UpdateTable(schemaID, tblInfo) 152 return errors.Trace(err) 153 case model.StateWriteOnly: 154 // write only -> reorganization 155 job.SchemaState = model.StateWriteReorganization 156 indexInfo.State = model.StateWriteReorganization 157 // initialize SnapshotVer to 0 for later reorganization check. 158 job.SnapshotVer = 0 159 err = t.UpdateTable(schemaID, tblInfo) 160 return errors.Trace(err) 161 case model.StateWriteReorganization: 162 // reorganization -> public 163 reorgInfo, err := d.getReorgInfo(t, job) 164 if err != nil || reorgInfo.first { 165 // if we run reorg firstly, we should update the job snapshot version 166 // and then run the reorg next time. 167 return errors.Trace(err) 168 } 169 170 var tbl table.Table 171 tbl, err = d.getTable(schemaID, tblInfo) 172 if err != nil { 173 return errors.Trace(err) 174 } 175 176 err = d.runReorgJob(func() error { 177 return d.addTableIndex(tbl, indexInfo, reorgInfo) 178 }) 179 180 if terror.ErrorEqual(err, errWaitReorgTimeout) { 181 // if timeout, we should return, check for the owner and re-wait job done. 182 return nil 183 } 184 if err != nil { 185 return errors.Trace(err) 186 } 187 188 indexInfo.State = model.StatePublic 189 // set column index flag. 190 addIndexColumnFlag(tblInfo, indexInfo) 191 if err = t.UpdateTable(schemaID, tblInfo); err != nil { 192 return errors.Trace(err) 193 } 194 195 // finish this job 196 job.SchemaState = model.StatePublic 197 job.State = model.JobDone 198 return nil 199 default: 200 return ErrInvalidIndexState.Gen("invalid index state %v", tblInfo.State) 201 } 202 } 203 204 func (d *ddl) onDropIndex(t *meta.Meta, job *model.Job) error { 205 schemaID := job.SchemaID 206 tblInfo, err := d.getTableInfo(t, job) 207 if err != nil { 208 return errors.Trace(err) 209 } 210 211 var indexName model.CIStr 212 if err = job.DecodeArgs(&indexName); err != nil { 213 job.State = model.JobCancelled 214 return errors.Trace(err) 215 } 216 217 var indexInfo *model.IndexInfo 218 for _, idx := range tblInfo.Indices { 219 if idx.Name.L == indexName.L { 220 indexInfo = idx 221 } 222 } 223 224 if indexInfo == nil { 225 job.State = model.JobCancelled 226 return ErrCantDropFieldOrKey.Gen("index %s doesn't exist", indexName) 227 } 228 229 _, err = t.GenSchemaVersion() 230 if err != nil { 231 return errors.Trace(err) 232 } 233 234 switch indexInfo.State { 235 case model.StatePublic: 236 // public -> write only 237 job.SchemaState = model.StateWriteOnly 238 indexInfo.State = model.StateWriteOnly 239 err = t.UpdateTable(schemaID, tblInfo) 240 return errors.Trace(err) 241 case model.StateWriteOnly: 242 // write only -> delete only 243 job.SchemaState = model.StateDeleteOnly 244 indexInfo.State = model.StateDeleteOnly 245 err = t.UpdateTable(schemaID, tblInfo) 246 return errors.Trace(err) 247 case model.StateDeleteOnly: 248 // delete only -> reorganization 249 job.SchemaState = model.StateDeleteReorganization 250 indexInfo.State = model.StateDeleteReorganization 251 err = t.UpdateTable(schemaID, tblInfo) 252 return errors.Trace(err) 253 case model.StateDeleteReorganization: 254 // reorganization -> absent 255 tbl, err := d.getTable(schemaID, tblInfo) 256 if err != nil { 257 return errors.Trace(err) 258 } 259 260 err = d.runReorgJob(func() error { 261 return d.dropTableIndex(tbl, indexInfo) 262 }) 263 264 if terror.ErrorEqual(err, errWaitReorgTimeout) { 265 // if timeout, we should return, check for the owner and re-wait job done. 266 return nil 267 } 268 if err != nil { 269 return errors.Trace(err) 270 } 271 272 // all reorganization jobs done, drop this index 273 newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) 274 for _, idx := range tblInfo.Indices { 275 if idx.Name.L != indexName.L { 276 newIndices = append(newIndices, idx) 277 } 278 } 279 tblInfo.Indices = newIndices 280 // set column index flag. 281 dropIndexColumnFlag(tblInfo, indexInfo) 282 if err = t.UpdateTable(schemaID, tblInfo); err != nil { 283 return errors.Trace(err) 284 } 285 286 // finish this job 287 job.SchemaState = model.StateNone 288 job.State = model.JobDone 289 return nil 290 default: 291 return ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) 292 } 293 } 294 295 func checkRowExist(txn kv.Transaction, t table.Table, handle int64) (bool, error) { 296 _, err := txn.Get(t.RecordKey(handle, nil)) 297 if terror.ErrorEqual(err, kv.ErrNotExist) { 298 // If row doesn't exist, we may have deleted the row already, 299 // no need to add index again. 300 return false, nil 301 } else if err != nil { 302 return false, errors.Trace(err) 303 } 304 305 return true, nil 306 } 307 308 func fetchRowColVals(txn kv.Transaction, t table.Table, handle int64, indexInfo *model.IndexInfo) ([]types.Datum, error) { 309 // fetch datas 310 cols := t.Cols() 311 vals := make([]types.Datum, 0, len(indexInfo.Columns)) 312 for _, v := range indexInfo.Columns { 313 col := cols[v.Offset] 314 k := t.RecordKey(handle, col) 315 data, err := txn.Get(k) 316 if err != nil { 317 if terror.ErrorEqual(err, kv.ErrNotExist) && !mysql.HasNotNullFlag(col.Flag) { 318 vals = append(vals, types.Datum{}) 319 continue 320 } 321 return nil, errors.Trace(err) 322 } 323 val, err := tables.DecodeValue(data, &col.FieldType) 324 if err != nil { 325 return nil, errors.Trace(err) 326 } 327 vals = append(vals, val) 328 } 329 330 return vals, nil 331 } 332 333 const maxBatchSize = 1024 334 335 // How to add index in reorganization state? 336 // 1. Generate a snapshot with special version. 337 // 2. Traverse the snapshot, get every row in the table. 338 // 3. For one row, if the row has been already deleted, skip to next row. 339 // 4. If not deleted, check whether index has existed, if existed, skip to next row. 340 // 5. If index doesn't exist, create the index and then continue to handle next row. 341 func (d *ddl) addTableIndex(t table.Table, indexInfo *model.IndexInfo, reorgInfo *reorgInfo) error { 342 seekHandle := reorgInfo.Handle 343 version := reorgInfo.SnapshotVer 344 for { 345 handles, err := d.getSnapshotRows(t, version, seekHandle) 346 if err != nil { 347 return errors.Trace(err) 348 } else if len(handles) == 0 { 349 return nil 350 } 351 352 seekHandle = handles[len(handles)-1] + 1 353 354 err = d.backfillTableIndex(t, indexInfo, handles, reorgInfo) 355 if err != nil { 356 return errors.Trace(err) 357 } 358 } 359 } 360 361 func (d *ddl) getSnapshotRows(t table.Table, version uint64, seekHandle int64) ([]int64, error) { 362 ver := kv.Version{Ver: version} 363 364 snap, err := d.store.GetSnapshot(ver) 365 if err != nil { 366 return nil, errors.Trace(err) 367 } 368 369 defer snap.Release() 370 371 firstKey := t.RecordKey(seekHandle, nil) 372 373 it, err := snap.Seek(firstKey) 374 if err != nil { 375 return nil, errors.Trace(err) 376 } 377 defer it.Close() 378 379 handles := make([]int64, 0, maxBatchSize) 380 381 for it.Valid() { 382 if !it.Key().HasPrefix(t.RecordPrefix()) { 383 break 384 } 385 386 var handle int64 387 handle, err = tables.DecodeRecordKeyHandle(it.Key()) 388 if err != nil { 389 return nil, errors.Trace(err) 390 } 391 392 rk := t.RecordKey(handle, nil) 393 394 handles = append(handles, handle) 395 if len(handles) == maxBatchSize { 396 break 397 } 398 399 err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) 400 if terror.ErrorEqual(err, kv.ErrNotExist) { 401 break 402 } else if err != nil { 403 return nil, errors.Trace(err) 404 } 405 } 406 407 return handles, nil 408 } 409 410 func lockRow(txn kv.Transaction, t table.Table, h int64) error { 411 // Get row lock key 412 lockKey := t.RecordKey(h, nil) 413 // set row lock key to current txn 414 err := txn.Set(lockKey, []byte(txn.String())) 415 return errors.Trace(err) 416 } 417 418 func (d *ddl) backfillTableIndex(t table.Table, indexInfo *model.IndexInfo, handles []int64, reorgInfo *reorgInfo) error { 419 kvX := kv.NewKVIndex(t.IndexPrefix(), indexInfo.Name.L, indexInfo.ID, indexInfo.Unique) 420 421 for _, handle := range handles { 422 log.Debug("[ddl] building index...", handle) 423 424 err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error { 425 if err := d.isReorgRunnable(txn); err != nil { 426 return errors.Trace(err) 427 } 428 429 // first check row exists 430 exist, err := checkRowExist(txn, t, handle) 431 if err != nil { 432 return errors.Trace(err) 433 } else if !exist { 434 // row doesn't exist, skip it. 435 return nil 436 } 437 438 var vals []types.Datum 439 vals, err = fetchRowColVals(txn, t, handle, indexInfo) 440 if err != nil { 441 return errors.Trace(err) 442 } 443 444 exist, _, err = kvX.Exist(txn, vals, handle) 445 if err != nil { 446 return errors.Trace(err) 447 } else if exist { 448 // index already exists, skip it. 449 return nil 450 } 451 452 err = lockRow(txn, t, handle) 453 if err != nil { 454 return errors.Trace(err) 455 } 456 457 // create the index. 458 err = kvX.Create(txn, vals, handle) 459 if err != nil { 460 return errors.Trace(err) 461 } 462 463 // update reorg next handle 464 return errors.Trace(reorgInfo.UpdateHandle(txn, handle)) 465 }) 466 467 if err != nil { 468 return errors.Trace(err) 469 } 470 } 471 472 return nil 473 } 474 475 func (d *ddl) dropTableIndex(t table.Table, indexInfo *model.IndexInfo) error { 476 prefix := kv.GenIndexPrefix(t.IndexPrefix(), indexInfo.ID) 477 err := d.delKeysWithPrefix(prefix) 478 479 return errors.Trace(err) 480 }