func rowWithCols(txn kv.Retriever, t table.Table, h int64, cols []*table.Column) ([]types.Datum, error) { v := make([]types.Datum, len(cols)) for i, col := range cols { if col.State != model.StatePublic { return nil, errInvalidColumnState.Gen("Cannot use none public column - %v", cols) } if col.IsPKHandleColumn(t.Meta()) { v[i].SetInt64(h) continue } k := t.RecordKey(h, col) data, err := txn.Get(k) if terror.ErrorEqual(err, kv.ErrNotExist) && !mysql.HasNotNullFlag(col.Flag) { continue } else if err != nil { return nil, errors.Trace(err) } val, err := tables.DecodeValue(data, &col.FieldType) if err != nil { return nil, errors.Trace(err) } v[i] = val } return v, nil }
func scanTableData(retriever kv.Retriever, t table.Table, cols []*table.Column, startHandle, limit int64) ( []*RecordData, int64, error) { var records []*RecordData startKey := t.RecordKey(startHandle, nil) filterFunc := func(h int64, d []types.Datum, cols []*table.Column) (bool, error) { if limit != 0 { r := &RecordData{ Handle: h, Values: d, } records = append(records, r) limit-- return true, nil } return false, nil } err := iterRecords(retriever, t, startKey, cols, filterFunc) if err != nil { return nil, 0, errors.Trace(err) } if len(records) == 0 { return records, startHandle, nil } nextHandle := records[len(records)-1].Handle + 1 return records, nextHandle, nil }
func (d *ddl) dropTableColumn(t table.Table, colInfo *model.ColumnInfo, reorgInfo *reorgInfo) error { version := reorgInfo.SnapshotVer seekHandle := reorgInfo.Handle col := &column.Col{ColumnInfo: *colInfo} for { handles, err := d.getSnapshotRows(t, version, seekHandle) if err != nil { return errors.Trace(err) } else if len(handles) == 0 { return nil } seekHandle = handles[len(handles)-1] + 1 err = kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error { if err1 := d.isReorgRunnable(txn); err1 != nil { return errors.Trace(err1) } var h int64 for _, h = range handles { key := t.RecordKey(h, col) err1 := txn.Delete(key) if err1 != nil && !terror.ErrorEqual(err1, kv.ErrNotExist) { return errors.Trace(err1) } } return errors.Trace(reorgInfo.UpdateHandle(txn, h)) }) if err != nil { return errors.Trace(err) } } }
func checkRecordAndIndex(txn kv.Transaction, t table.Table, idx *column.IndexedCol) error { cols := make([]*column.Col, len(idx.Columns)) for i, col := range idx.Columns { cols[i] = t.Cols()[col.Offset] } startKey := t.RecordKey(0, nil) kvIndex := kv.NewKVIndex(t.IndexPrefix(), idx.Name.L, idx.ID, idx.Unique) filterFunc := func(h1 int64, vals1 []interface{}, cols []*column.Col) (bool, error) { isExist, h2, err := kvIndex.Exist(txn, vals1, h1) if terror.ErrorEqual(err, kv.ErrKeyExists) { record1 := &RecordData{Handle: h1, Values: vals1} record2 := &RecordData{Handle: h2, Values: vals1} return false, errors.Errorf("index:%v != record:%v", record2, record1) } if err != nil { return false, errors.Trace(err) } if !isExist { record := &RecordData{Handle: h1, Values: vals1} return false, errors.Errorf("index:%v != record:%v", nil, record) } return true, nil } err := t.IterRecords(txn, startKey, cols, filterFunc) if err != nil { return errors.Trace(err) } return nil }
func rowWithCols(txn kv.Retriever, t table.Table, h int64, cols []*column.Col) ([]types.Datum, error) { v := make([]types.Datum, len(cols)) for i, col := range cols { if col.State != model.StatePublic { return nil, errors.Errorf("Cannot use none public column - %v", cols) } if col.IsPKHandleColumn(t.Meta()) { v[i].SetInt64(h) continue } k := t.RecordKey(h, col) data, err := txn.Get(k) if err != nil { return nil, errors.Trace(err) } val, err := tables.DecodeValue(data, &col.FieldType) if err != nil { return nil, errors.Trace(err) } v[i] = val } return v, nil }
// GetTableRecordsCount returns the total number of table records from startHandle. // If startHandle = 0, returns the total number of table records. func GetTableRecordsCount(txn kv.Transaction, t table.Table, startHandle int64) (int64, error) { startKey := t.RecordKey(startHandle, nil) it, err := txn.Seek(startKey) if err != nil { return 0, errors.Trace(err) } var cnt int64 prefix := t.RecordPrefix() for it.Valid() && it.Key().HasPrefix(prefix) { handle, err := tables.DecodeRecordKeyHandle(it.Key()) if err != nil { return 0, errors.Trace(err) } it.Close() rk := t.RecordKey(handle+1, nil) it, err = txn.Seek(rk) if err != nil { return 0, errors.Trace(err) } cnt++ } it.Close() return cnt, nil }
func lockRow(txn kv.Transaction, t table.Table, h int64) error { // Get row lock key lockKey := t.RecordKey(h, nil) // set row lock key to current txn err := txn.Set(lockKey, []byte(txn.String())) return errors.Trace(err) }
func checkRecordAndIndex(txn kv.Transaction, t table.Table, idx table.Index) error { cols := make([]*table.Column, len(idx.Meta().Columns)) for i, col := range idx.Meta().Columns { cols[i] = t.Cols()[col.Offset] } startKey := t.RecordKey(0, nil) filterFunc := func(h1 int64, vals1 []types.Datum, cols []*table.Column) (bool, error) { isExist, h2, err := idx.Exist(txn, vals1, h1) if terror.ErrorEqual(err, kv.ErrKeyExists) { record1 := &RecordData{Handle: h1, Values: vals1} record2 := &RecordData{Handle: h2, Values: vals1} return false, errDateNotEqual.Gen("index:%v != record:%v", record2, record1) } if err != nil { return false, errors.Trace(err) } if !isExist { record := &RecordData{Handle: h1, Values: vals1} return false, errDateNotEqual.Gen("index:%v != record:%v", nil, record) } return true, nil } err := iterRecords(txn, t, startKey, cols, filterFunc) if err != nil { return errors.Trace(err) } return nil }
func (s *testColumnSuite) checkColumnKVExist(ctx context.Context, t table.Table, handle int64, col *table.Column, columnValue interface{}, isExist bool) error { txn, err := ctx.GetTxn(true) if err != nil { return errors.Trace(err) } defer ctx.CommitTxn() key := t.RecordKey(handle) data, err := txn.Get(key) if !isExist { if terror.ErrorEqual(err, kv.ErrNotExist) { return nil } } if err != nil { return errors.Trace(err) } colMap := make(map[int64]*types.FieldType) colMap[col.ID] = &col.FieldType rowMap, err := tablecodec.DecodeRow(data, colMap) if err != nil { return errors.Trace(err) } val, ok := rowMap[col.ID] if isExist { if !ok || val.GetValue() != columnValue { return errors.Errorf("%v is not equal to %v", val.GetValue(), columnValue) } } else { if ok { return errors.Errorf("column value should not exists") } } return nil }
func (d *ddl) backfillColumnData(t table.Table, columnInfo *model.ColumnInfo, handles []int64, reorgInfo *reorgInfo) error { for _, handle := range handles { log.Info("[ddl] backfill column...", handle) err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error { if err := d.isReorgRunnable(txn); err != nil { return errors.Trace(err) } // First check if row exists. exist, err := checkRowExist(txn, t, handle) if err != nil { return errors.Trace(err) } else if !exist { // If row doesn't exist, skip it. return nil } backfillKey := t.RecordKey(handle, &column.Col{ColumnInfo: *columnInfo}) backfillValue, err := txn.Get(backfillKey) if err != nil && !kv.IsErrNotFound(err) { return errors.Trace(err) } if backfillValue != nil { return nil } value, _, err := tables.GetColDefaultValue(nil, columnInfo) if err != nil { return errors.Trace(err) } // must convert to the column field type. v, err := types.Convert(value, &columnInfo.FieldType) if err != nil { return errors.Trace(err) } err = lockRow(txn, t, handle) if err != nil { return errors.Trace(err) } err = t.SetColValue(txn, backfillKey, v) if err != nil { return errors.Trace(err) } return errors.Trace(reorgInfo.UpdateHandle(txn, handle)) }) if err != nil { return errors.Trace(err) } } return nil }
func (d *ddl) backfillColumnData(t table.Table, columnInfo *model.ColumnInfo, handles []int64, reorgInfo *reorgInfo) error { defaultVal, _, err := table.GetColDefaultValue(nil, columnInfo) if err != nil { return errors.Trace(err) } colMap := make(map[int64]*types.FieldType) for _, col := range t.Meta().Columns { colMap[col.ID] = &col.FieldType } for _, handle := range handles { log.Info("[ddl] backfill column...", handle) err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error { if err := d.isReorgRunnable(txn); err != nil { return errors.Trace(err) } rowKey := t.RecordKey(handle) rowVal, err := txn.Get(rowKey) if terror.ErrorEqual(err, kv.ErrNotExist) { // If row doesn't exist, skip it. return nil } if err != nil { return errors.Trace(err) } rowColumns, err := tablecodec.DecodeRow(rowVal, colMap) if err != nil { return errors.Trace(err) } if _, ok := rowColumns[columnInfo.ID]; ok { // The column is already added by update or insert statement, skip it. return nil } newColumnIDs := make([]int64, 0, len(rowColumns)+1) newRow := make([]types.Datum, 0, len(rowColumns)+1) for colID, val := range rowColumns { newColumnIDs = append(newColumnIDs, colID) newRow = append(newRow, val) } newColumnIDs = append(newColumnIDs, columnInfo.ID) newRow = append(newRow, defaultVal) newRowVal, err := tablecodec.EncodeRow(newRow, newColumnIDs) if err != nil { return errors.Trace(err) } err = txn.Set(rowKey, newRowVal) if err != nil { return errors.Trace(err) } return errors.Trace(reorgInfo.UpdateHandle(txn, handle)) }) if err != nil { return errors.Trace(err) } } return nil }
func (d *ddl) buildIndex(ctx context.Context, t table.Table, idxInfo *model.IndexInfo, unique bool) error { firstKey := t.FirstKey() prefix := t.KeyPrefix() txn, err := ctx.GetTxn(false) if err != nil { return errors.Trace(err) } it, err := txn.Seek([]byte(firstKey)) if err != nil { return errors.Trace(err) } defer it.Close() for it.Valid() && strings.HasPrefix(it.Key(), prefix) { var err error handle, err := util.DecodeHandleFromRowKey(it.Key()) log.Info("building index...", handle) if err != nil { return errors.Trace(err) } // TODO: v is timestamp ? // fetch datas cols := t.Cols() var vals []interface{} for _, v := range idxInfo.Columns { var ( data []byte val interface{} ) col := cols[v.Offset] k := t.RecordKey(handle, col) data, err = txn.Get([]byte(k)) if err != nil { return errors.Trace(err) } val, err = t.DecodeValue(data, col) if err != nil { return errors.Trace(err) } vals = append(vals, val) } // build index kvX := kv.NewKVIndex(t.IndexPrefix(), idxInfo.Name.L, unique) err = kvX.Create(txn, vals, handle) if err != nil { return errors.Trace(err) } rk := []byte(t.RecordKey(handle, nil)) it, err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) if err != nil { return errors.Trace(err) } } return nil }
func iterRecords(retriever kv.Retriever, t table.Table, startKey kv.Key, cols []*table.Column, fn table.RecordIterFunc) error { it, err := retriever.Seek(startKey) if err != nil { return errors.Trace(err) } defer it.Close() if !it.Valid() { return nil } log.Debugf("startKey:%q, key:%q, value:%q", startKey, it.Key(), it.Value()) colMap := make(map[int64]*types.FieldType, len(cols)) for _, col := range cols { colMap[col.ID] = &col.FieldType } prefix := t.RecordPrefix() for it.Valid() && it.Key().HasPrefix(prefix) { // first kv pair is row lock information. // TODO: check valid lock // get row handle handle, err := tablecodec.DecodeRowKey(it.Key()) if err != nil { return errors.Trace(err) } rowMap, err := tablecodec.DecodeRow(it.Value(), colMap) if err != nil { return errors.Trace(err) } data := make([]types.Datum, 0, len(cols)) for _, col := range cols { if col.IsPKHandleColumn(t.Meta()) { data = append(data, types.NewIntDatum(handle)) } else { data = append(data, rowMap[col.ID]) } } more, err := fn(handle, data, cols) if !more || err != nil { return errors.Trace(err) } rk := t.RecordKey(handle) err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) if err != nil { return errors.Trace(err) } } return nil }
func checkRowExist(txn kv.Transaction, t table.Table, handle int64) (bool, error) { _, err := txn.Get(t.RecordKey(handle, nil)) if terror.ErrorEqual(err, kv.ErrNotExist) { // If row doesn't exist, we may have deleted the row already, // no need to add index again. return false, nil } else if err != nil { return false, errors.Trace(err) } return true, nil }
func (d *ddl) getSnapshotRows(t table.Table, version uint64, seekHandle int64) ([]int64, error) { ver := kv.Version{Ver: version} snap, err := d.store.GetSnapshot(ver) if err != nil { return nil, errors.Trace(err) } defer snap.Release() firstKey := t.RecordKey(seekHandle, nil) it, err := snap.Seek(firstKey) if err != nil { return nil, errors.Trace(err) } defer it.Close() handles := make([]int64, 0, maxBatchSize) for it.Valid() { if !it.Key().HasPrefix(t.RecordPrefix()) { break } var handle int64 handle, err = tables.DecodeRecordKeyHandle(it.Key()) if err != nil { return nil, errors.Trace(err) } rk := t.RecordKey(handle, nil) handles = append(handles, handle) if len(handles) == maxBatchSize { seekHandle = handle + 1 break } err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) if terror.ErrorEqual(err, kv.ErrNotExist) { break } else if err != nil { return nil, errors.Trace(err) } } return handles, nil }
func (d *ddl) getSnapshotRows(t table.Table, version uint64, seekHandle int64) ([]int64, error) { ver := kv.Version{Ver: version} snap, err := d.store.GetSnapshot(ver) if err != nil { return nil, errors.Trace(err) } defer snap.MvccRelease() firstKey := t.RecordKey(seekHandle, nil) prefix := []byte(t.KeyPrefix()) it := snap.NewMvccIterator([]byte(firstKey), ver) defer it.Close() handles := make([]int64, 0, maxBatchSize) for it.Valid() { key := []byte(it.Key()) if !bytes.HasPrefix(key, prefix) { break } var handle int64 handle, err = util.DecodeHandleFromRowKey(string(key)) if err != nil { return nil, errors.Trace(err) } rk := t.RecordKey(handle, nil) handles = append(handles, handle) if len(handles) == maxBatchSize { seekHandle = handle + 1 break } err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) if terror.ErrorEqual(err, kv.ErrNotExist) { break } else if err != nil { return nil, errors.Trace(err) } } return handles, nil }
func rowWithCols(txn kv.Retriever, t table.Table, h int64, cols []*table.Column) ([]types.Datum, error) { key := t.RecordKey(h) value, err := txn.Get(key) if err != nil { return nil, errors.Trace(err) } v := make([]types.Datum, len(cols)) colTps := make(map[int64]*types.FieldType, len(cols)) for i, col := range cols { if col == nil { continue } if col.State != model.StatePublic { return nil, errInvalidColumnState.Gen("Cannot use none public column - %v", cols) } if col.IsPKHandleColumn(t.Meta()) { if mysql.HasUnsignedFlag(col.Flag) { v[i].SetUint64(uint64(h)) } else { v[i].SetInt64(h) } continue } colTps[col.ID] = &col.FieldType } row, err := tablecodec.DecodeRow(value, colTps) if err != nil { return nil, errors.Trace(err) } for i, col := range cols { if col == nil { continue } if col.State != model.StatePublic { // TODO: check this return nil, errInvalidColumnState.Gen("Cannot use none public column - %v", cols) } if col.IsPKHandleColumn(t.Meta()) { continue } ri, ok := row[col.ID] if !ok && mysql.HasNotNullFlag(col.Flag) { return nil, errors.New("Miss") } v[i] = ri } return v, nil }
// backfillColumnInTxn deals with a part of backfilling column data in a Transaction. // This part of the column data rows is defaultSmallBatchCnt. func (d *ddl) backfillColumnInTxn(t table.Table, colID int64, handles []int64, colMap map[int64]*types.FieldType, defaultVal types.Datum, txn kv.Transaction) (int64, error) { nextHandle := handles[0] for _, handle := range handles { log.Debug("[ddl] backfill column...", handle) rowKey := t.RecordKey(handle) rowVal, err := txn.Get(rowKey) if terror.ErrorEqual(err, kv.ErrNotExist) { // If row doesn't exist, skip it. continue } if err != nil { return 0, errors.Trace(err) } rowColumns, err := tablecodec.DecodeRow(rowVal, colMap) if err != nil { return 0, errors.Trace(err) } if _, ok := rowColumns[colID]; ok { // The column is already added by update or insert statement, skip it. continue } newColumnIDs := make([]int64, 0, len(rowColumns)+1) newRow := make([]types.Datum, 0, len(rowColumns)+1) for colID, val := range rowColumns { newColumnIDs = append(newColumnIDs, colID) newRow = append(newRow, val) } newColumnIDs = append(newColumnIDs, colID) newRow = append(newRow, defaultVal) newRowVal, err := tablecodec.EncodeRow(newRow, newColumnIDs) if err != nil { return 0, errors.Trace(err) } err = txn.Set(rowKey, newRowVal) if err != nil { return 0, errors.Trace(err) } } return nextHandle, nil }
// CompareTableRecord compares data and the corresponding table data one by one. // It returns nil if data is equal to the data that scans from table, otherwise // it returns an error with a different set of records. If exact is false, only compares handle. func CompareTableRecord(txn kv.Transaction, t table.Table, data []*RecordData, exact bool) error { m := make(map[int64][]types.Datum, len(data)) for _, r := range data { if _, ok := m[r.Handle]; ok { return errRepeatHandle.Gen("handle:%d is repeated in data", r.Handle) } m[r.Handle] = r.Values } startKey := t.RecordKey(0, nil) filterFunc := func(h int64, vals []types.Datum, cols []*table.Column) (bool, error) { vals2, ok := m[h] if !ok { record := &RecordData{Handle: h, Values: vals} return false, errDateNotEqual.Gen("data:%v != record:%v", nil, record) } if !exact { delete(m, h) return true, nil } if !reflect.DeepEqual(vals, vals2) { record1 := &RecordData{Handle: h, Values: vals2} record2 := &RecordData{Handle: h, Values: vals} return false, errDateNotEqual.Gen("data:%v != record:%v", record1, record2) } delete(m, h) return true, nil } err := iterRecords(txn, t, startKey, t.Cols(), filterFunc) if err != nil { return errors.Trace(err) } for h, vals := range m { record := &RecordData{Handle: h, Values: vals} return errDateNotEqual.Gen("data:%v != record:%v", record, nil) } return nil }
func iterRecords(retriever kv.Retriever, t table.Table, startKey kv.Key, cols []*table.Column, fn table.RecordIterFunc) error { it, err := retriever.Seek(startKey) if err != nil { return errors.Trace(err) } defer it.Close() if !it.Valid() { return nil } log.Debugf("startKey:%q, key:%q, value:%q", startKey, it.Key(), it.Value()) prefix := t.RecordPrefix() for it.Valid() && it.Key().HasPrefix(prefix) { // first kv pair is row lock information. // TODO: check valid lock // get row handle handle, err := tables.DecodeRecordKeyHandle(it.Key()) if err != nil { return errors.Trace(err) } data, err := rowWithCols(retriever, t, handle, cols) if err != nil { return errors.Trace(err) } more, err := fn(handle, data, cols) if !more || err != nil { return errors.Trace(err) } rk := t.RecordKey(handle, nil) err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) if err != nil { return errors.Trace(err) } } return nil }
func fetchRowColVals(txn kv.Transaction, t table.Table, handle int64, indexInfo *model.IndexInfo) ([]types.Datum, error) { // fetch datas cols := t.Cols() vals := make([]types.Datum, 0, len(indexInfo.Columns)) for _, v := range indexInfo.Columns { col := cols[v.Offset] k := t.RecordKey(handle, col) data, err := txn.Get(k) if err != nil { return nil, errors.Trace(err) } val, err := tables.DecodeValue(data, &col.FieldType) if err != nil { return nil, errors.Trace(err) } vals = append(vals, val) } return vals, nil }
func (s *testColumnSuite) checkColumnKVExist(c *C, ctx context.Context, t table.Table, handle int64, col *column.Col, columnValue interface{}, isExist bool) { txn, err := ctx.GetTxn(true) c.Assert(err, IsNil) key := t.RecordKey(handle, col) data, err := txn.Get(key) if isExist { c.Assert(err, IsNil) v, err1 := tables.DecodeValue(data, &col.FieldType) c.Assert(err1, IsNil) value, err1 := v.ConvertTo(&col.FieldType) c.Assert(err1, IsNil) c.Assert(value.GetValue(), Equals, columnValue) } else { c.Assert(err, NotNil) } err = ctx.FinishTxn(false) c.Assert(err, IsNil) }
func fetchRowColVals(txn kv.Transaction, t table.Table, handle int64, indexInfo *model.IndexInfo) ([]interface{}, error) { // fetch datas cols := t.Cols() var vals []interface{} for _, v := range indexInfo.Columns { var val interface{} col := cols[v.Offset] k := t.RecordKey(handle, col) data, err := txn.Get(k) if err != nil { return nil, errors.Trace(err) } val, err = t.DecodeValue(data, col) if err != nil { return nil, errors.Trace(err) } vals = append(vals, val) } return vals, nil }
func fetchRowColVals(txn kv.Transaction, t table.Table, handle int64, indexInfo *model.IndexInfo) ([]types.Datum, error) { // fetch datas cols := t.Cols() vals := make([]types.Datum, 0, len(indexInfo.Columns)) for _, v := range indexInfo.Columns { col := cols[v.Offset] k := t.RecordKey(handle, col) data, err := txn.Get(k) if err != nil { if terror.ErrorEqual(err, kv.ErrNotExist) && !mysql.HasNotNullFlag(col.Flag) { vals = append(vals, types.Datum{}) continue } return nil, errors.Trace(err) } val, err := tables.DecodeValue(data, &col.FieldType) if err != nil { return nil, errors.Trace(err) } vals = append(vals, val) } return vals, nil }
func (s *testSuite) testIndex(c *C, tb table.Table, idx table.Index) { txn, err := s.store.Begin() c.Assert(err, IsNil) err = CompareIndexData(txn, tb, idx) c.Assert(err, IsNil) cnt, err := GetIndexRecordsCount(txn, idx, nil) c.Assert(err, IsNil) c.Assert(cnt, Equals, int64(2)) // current index data: // index data (handle, data): (1, 10), (2, 20), (3, 30) // index col data (handle, data): (1, 10), (2, 20), (4, 40) err = idx.Create(txn, types.MakeDatums(int64(30)), 3) c.Assert(err, IsNil) col := tb.Cols()[idx.Meta().Columns[0].Offset] key := tb.RecordKey(4, col) err = tables.SetColValue(txn, key, types.NewDatum(int64(40))) c.Assert(err, IsNil) err = txn.Commit() c.Assert(err, IsNil) txn, err = s.store.Begin() c.Assert(err, IsNil) err = CompareIndexData(txn, tb, idx) c.Assert(err, NotNil) record1 := &RecordData{Handle: int64(3), Values: types.MakeDatums(int64(30))} diffMsg := newDiffRetError("index", record1, &RecordData{Handle: int64(3), Values: types.MakeDatums(nil)}) c.Assert(err.Error(), DeepEquals, diffMsg) // current index data: // index data (handle, data): (1, 10), (2, 20), (3, 30), (4, 40) // index col data (handle, data): (1, 10), (2, 20), (4, 40), (3, 31) err = idx.Create(txn, types.MakeDatums(int64(40)), 4) c.Assert(err, IsNil) key = tb.RecordKey(3, col) err = tables.SetColValue(txn, key, types.NewDatum(int64(31))) c.Assert(err, IsNil) err = txn.Commit() c.Assert(err, IsNil) txn, err = s.store.Begin() c.Assert(err, IsNil) err = CompareIndexData(txn, tb, idx) c.Assert(err, NotNil) record2 := &RecordData{Handle: int64(3), Values: types.MakeDatums(int64(31))} diffMsg = newDiffRetError("index", record1, record2) c.Assert(err.Error(), DeepEquals, diffMsg) // current index data: // index data (handle, data): (1, 10), (2, 20), (3, 30), (4, 40) // index col data (handle, data): (1, 10), (2, 20), (4, 40), (5, 30) key = tb.RecordKey(3, col) txn.Delete(key) key = tb.RecordKey(5, col) err = tables.SetColValue(txn, key, types.NewDatum(int64(30))) c.Assert(err, IsNil) err = txn.Commit() c.Assert(err, IsNil) txn, err = s.store.Begin() c.Assert(err, IsNil) err = checkRecordAndIndex(txn, tb, idx) c.Assert(err, NotNil) record2 = &RecordData{Handle: int64(5), Values: types.MakeDatums(int64(30))} diffMsg = newDiffRetError("index", record1, record2) c.Assert(err.Error(), DeepEquals, diffMsg) // current index data: // index data (handle, data): (1, 10), (2, 20), (3, 30), (4, 40) // index col data (handle, data): (1, 10), (2, 20), (3, 30) key = tb.RecordKey(4, col) txn.Delete(key) key = tb.RecordKey(3, col) err = tables.SetColValue(txn, key, types.NewDatum(int64(30))) c.Assert(err, IsNil) err = txn.Commit() c.Assert(err, IsNil) txn, err = s.store.Begin() c.Assert(err, IsNil) err = CompareIndexData(txn, tb, idx) c.Assert(err, NotNil) record1 = &RecordData{Handle: int64(4), Values: types.MakeDatums(int64(40))} diffMsg = newDiffRetError("index", record1, &RecordData{Handle: int64(4), Values: types.MakeDatums(nil)}) c.Assert(err.Error(), DeepEquals, diffMsg) // current index data: // index data (handle, data): (1, 10), (2, 20), (3, 30) // index col data (handle, data): (1, 10), (2, 20), (3, 30), (4, 40) err = idx.Delete(txn, types.MakeDatums(int64(40)), 4) c.Assert(err, IsNil) key = tb.RecordKey(4, col) err = tables.SetColValue(txn, key, types.NewDatum(int64(40))) c.Assert(err, IsNil) err = txn.Commit() c.Assert(err, IsNil) txn, err = s.store.Begin() c.Assert(err, IsNil) err = CompareIndexData(txn, tb, idx) c.Assert(err, NotNil) diffMsg = newDiffRetError("index", nil, record1) c.Assert(err.Error(), DeepEquals, diffMsg) }