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  }