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 }
// TableFromMeta creates a Table instance from model.TableInfo. func TableFromMeta(alloc autoid.Allocator, tblInfo *model.TableInfo) (table.Table, error) { if tblInfo.State == model.StateNone { return nil, errors.Errorf("table %s can't be in none state", tblInfo.Name) } columns := make([]*column.Col, 0, len(tblInfo.Columns)) for _, colInfo := range tblInfo.Columns { if colInfo.State == model.StateNone { return nil, errors.Errorf("column %s can't be in none state", colInfo.Name) } col := &column.Col{ColumnInfo: *colInfo} columns = append(columns, col) } t := newTable(tblInfo.ID, tblInfo.Name.O, columns, alloc) for _, idxInfo := range tblInfo.Indices { if idxInfo.State == model.StateNone { return nil, errors.Errorf("index %s can't be in none state", idxInfo.Name) } idx := &column.IndexedCol{ IndexInfo: *idxInfo, } idx.X = kv.NewKVIndex(t.IndexPrefix(), idxInfo.Name.L, idxInfo.ID, idxInfo.Unique) t.AddIndex(idx) } t.meta = tblInfo return t, nil }
// TableFromMeta creates a Table instance from model.TableInfo. func TableFromMeta(alloc autoid.Allocator, tblInfo *model.TableInfo) table.Table { if tblInfo.State == model.StateNone { log.Fatalf("table %s can't be in none state", tblInfo.Name) } columns := make([]*column.Col, 0, len(tblInfo.Columns)) for _, colInfo := range tblInfo.Columns { if colInfo.State == model.StateNone { log.Fatalf("column %s can't be in none state", colInfo.Name) } col := &column.Col{ColumnInfo: *colInfo} columns = append(columns, col) } t := NewTable(tblInfo.ID, tblInfo.Name.O, columns, alloc) for _, idxInfo := range tblInfo.Indices { if idxInfo.State == model.StateNone { log.Fatalf("index %s can't be in none state", idxInfo.Name) } idx := &column.IndexedCol{ IndexInfo: *idxInfo, X: kv.NewKVIndex(t.indexPrefix, idxInfo.Name.L, idxInfo.Unique), } t.AddIndex(idx) } t.state = tblInfo.State return t }
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 (d *ddl) backfillTableIndex(t table.Table, indexInfo *model.IndexInfo, handles []int64, reorgInfo *reorgInfo) error { kvX := kv.NewKVIndex(t.IndexPrefix(), indexInfo.Name.L, indexInfo.ID, indexInfo.Unique) for _, handle := range handles { log.Debug("[ddl] building index...", 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 row exists exist, err := checkRowExist(txn, t, handle) if err != nil { return errors.Trace(err) } else if !exist { // row doesn't exist, skip it. return nil } var vals []interface{} vals, err = fetchRowColVals(txn, t, handle, indexInfo) if err != nil { return errors.Trace(err) } exist, _, err = kvX.Exist(txn, vals, handle) if err != nil { return errors.Trace(err) } else if exist { // index already exists, skip it. return nil } err = lockRow(txn, t, handle) if err != nil { return errors.Trace(err) } // create the index. err = kvX.Create(txn, vals, handle) if err != nil { return errors.Trace(err) } // update reorg next handle return errors.Trace(reorgInfo.UpdateHandle(txn, handle)) }) if err != nil { return errors.Trace(err) } } return nil }
func (s *testIndexSuite) TestCombineIndexSeek(c *C) { index := kv.NewKVIndex([]byte("i"), "test", 1, false) txn, err := s.s.Begin() c.Assert(err, IsNil) values := types.MakeDatums("abc", "def") err = index.Create(txn, values, 1) c.Assert(err, IsNil) index2 := kv.NewKVIndex([]byte("i"), "test", 1, false) iter, hit, err := index2.Seek(txn, types.MakeDatums("abc", nil)) c.Assert(err, IsNil) defer iter.Close() c.Assert(hit, IsFalse) _, h, err := iter.Next() c.Assert(err, IsNil) c.Assert(h, Equals, int64(1)) }
func (p *testIndexSuit) SetUpSuite(c *C) { store, err := tidb.NewStore(tidb.EngineGoLevelDBMemory) c.Assert(err, IsNil) p.store = store p.vars = map[string]interface{}{} p.txn, _ = p.store.Begin() p.cols = []*column.Col{ { ColumnInfo: model.ColumnInfo{ ID: 0, Name: model.NewCIStr("id"), Offset: 0, DefaultValue: 0, FieldType: *types.NewFieldType(mysql.TypeLonglong), }, }, { ColumnInfo: model.ColumnInfo{ ID: 1, Name: model.NewCIStr("name"), Offset: 1, DefaultValue: nil, FieldType: *types.NewFieldType(mysql.TypeVarchar), }, }, } p.tbl = tables.NewTable(2, "t2", "test", p.cols, &simpleAllocator{}) idxCol := &column.IndexedCol{ IndexInfo: model.IndexInfo{ Name: model.NewCIStr("id"), Table: model.NewCIStr("t2"), Columns: []*model.IndexColumn{ { Name: model.NewCIStr("id"), Offset: 0, Length: 0, }, }, Unique: false, Primary: false, }, X: kv.NewKVIndex("i", "id", false), } p.tbl.AddIndex(idxCol) variable.BindSessionVars(p) var i int64 for i = 0; i < 10; i++ { p.tbl.AddRecord(p, []interface{}{i * 10, "hello"}) } }
func (p *testIndexSuit) SetUpSuite(c *C) { store, err := tidb.NewStore(tidb.EngineGoLevelDBMemory) c.Assert(err, IsNil) p.store = store se, _ := tidb.CreateSession(store) p.ctx = se.(context.Context) p.cols = []*column.Col{ { ColumnInfo: model.ColumnInfo{ ID: 0, Name: model.NewCIStr("id"), Offset: 0, DefaultValue: 0, FieldType: *types.NewFieldType(mysql.TypeLonglong), State: model.StatePublic, }, }, { ColumnInfo: model.ColumnInfo{ ID: 1, Name: model.NewCIStr("name"), Offset: 1, DefaultValue: nil, FieldType: *types.NewFieldType(mysql.TypeVarchar), State: model.StatePublic, }, }, } p.tbl = tables.NewTable(2, "t2", p.cols, &simpleAllocator{}) idxCol := &column.IndexedCol{ IndexInfo: model.IndexInfo{ Name: model.NewCIStr("id"), Table: model.NewCIStr("t2"), Columns: []*model.IndexColumn{ { Name: model.NewCIStr("id"), Offset: 0, Length: 0, }, }, Unique: false, Primary: false, State: model.StatePublic, }, X: kv.NewKVIndex("i", "id", 0, false), } p.tbl.AddIndex(idxCol) var i int64 for i = 0; i < 10; i++ { p.tbl.AddRecord(p.ctx, []interface{}{i * 10, "hello"}, 0) } }
// TableFromMeta creates a Table instance from model.TableInfo. func TableFromMeta(dbname string, alloc autoid.Allocator, tblInfo *model.TableInfo) table.Table { t := NewTable(tblInfo.ID, tblInfo.Name.O, dbname, nil, alloc) for _, colInfo := range tblInfo.Columns { c := column.Col{ColumnInfo: *colInfo} t.Columns = append(t.Columns, &c) } for _, idxInfo := range tblInfo.Indices { idx := &column.IndexedCol{ IndexInfo: *idxInfo, X: kv.NewKVIndex(t.indexPrefix, idxInfo.Name.L, idxInfo.Unique), } t.AddIndex(idx) } return t }
func checkIndexAndRecord(txn kv.Transaction, t table.Table, idx *column.IndexedCol) error { kvIndex := kv.NewKVIndex(t.IndexPrefix(), idx.Name.L, idx.ID, idx.Unique) it, err := kvIndex.SeekFirst(txn) if err != nil { return errors.Trace(err) } defer it.Close() cols := make([]*column.Col, len(idx.Columns)) for i, col := range idx.Columns { cols[i] = t.Cols()[col.Offset] } for { vals1, h, err := it.Next() if terror.ErrorEqual(err, io.EOF) { break } else if err != nil { return errors.Trace(err) } vals2, err := t.RowWithCols(txn, h, cols) if terror.ErrorEqual(err, kv.ErrNotExist) { record := &RecordData{Handle: h, Values: vals1} err = errors.Errorf("index:%v != record:%v", record, nil) } if err != nil { return errors.Trace(err) } if !reflect.DeepEqual(vals1, vals2) { record1 := &RecordData{Handle: h, Values: vals1} record2 := &RecordData{Handle: h, Values: vals2} return errors.Errorf("index:%v != record:%v", record1, record2) } } return nil }
func (s *testIndexSuite) TestIndex(c *C) { index := kv.NewKVIndex([]byte("i"), "test", 0, false) // Test ununiq index. txn, err := s.s.Begin() c.Assert(err, IsNil) values := types.MakeDatums(1, 2) err = index.Create(txn, values, 1) c.Assert(err, IsNil) it, err := index.SeekFirst(txn) c.Assert(err, IsNil) getValues, h, err := it.Next() c.Assert(err, IsNil) c.Assert(getValues, HasLen, 2) c.Assert(getValues[0].GetInt64(), Equals, int64(1)) c.Assert(getValues[1].GetInt64(), Equals, int64(2)) c.Assert(h, Equals, int64(1)) it.Close() exist, _, err := index.Exist(txn, values, 100) c.Assert(err, IsNil) c.Assert(exist, IsFalse) exist, _, err = index.Exist(txn, values, 1) c.Assert(err, IsNil) c.Assert(exist, IsTrue) err = index.Delete(txn, values, 1) c.Assert(err, IsNil) it, err = index.SeekFirst(txn) c.Assert(err, IsNil) _, _, err = it.Next() c.Assert(terror.ErrorEqual(err, io.EOF), IsTrue) it.Close() err = index.Create(txn, values, 0) c.Assert(err, IsNil) _, err = index.SeekFirst(txn) c.Assert(err, IsNil) _, hit, err := index.Seek(txn, values) c.Assert(err, IsNil) c.Assert(hit, IsTrue) err = index.Drop(txn) c.Assert(err, IsNil) it, hit, err = index.Seek(txn, values) c.Assert(err, IsNil) c.Assert(hit, IsFalse) _, _, err = it.Next() c.Assert(terror.ErrorEqual(err, io.EOF), IsTrue) it.Close() it, err = index.SeekFirst(txn) c.Assert(err, IsNil) _, _, err = it.Next() c.Assert(terror.ErrorEqual(err, io.EOF), IsTrue) it.Close() err = txn.Commit() c.Assert(err, IsNil) index = kv.NewKVIndex([]byte("j"), "test", 1, true) // Test uniq index. txn, err = s.s.Begin() c.Assert(err, IsNil) err = index.Create(txn, values, 1) c.Assert(err, IsNil) err = index.Create(txn, values, 2) c.Assert(err, NotNil) exist, h, err = index.Exist(txn, values, 1) c.Assert(err, IsNil) c.Assert(h, Equals, int64(1)) c.Assert(exist, IsTrue) exist, h, err = index.Exist(txn, values, 2) c.Assert(err, NotNil) c.Assert(h, Equals, int64(1)) c.Assert(exist, IsTrue) err = txn.Commit() c.Assert(err, IsNil) }
func (s *testDBSuite) testAddIndex(c *C) { done := make(chan struct{}, 1) num := 100 // first add some rows for i := 0; i < num; i++ { s.mustExec(c, "insert into t1 values (?, ?, ?)", i, i, i) } go func() { s.mustExec(c, "create index c3_index on t1 (c3)") done <- struct{}{} }() deletedKeys := make(map[int]struct{}) ticker := time.NewTicker(s.lease / 2) defer ticker.Stop() LOOP: for { select { case <-done: break LOOP case <-ticker.C: step := 10 // delete some rows, and add some data for i := num; i < num+step; i++ { n := rand.Intn(num) deletedKeys[n] = struct{}{} s.mustExec(c, "delete from t1 where c1 = ?", n) s.mustExec(c, "insert into t1 values (?, ?, ?)", i, i, i) } num += step } } // get exists keys keys := make([]int, 0, num) for i := 0; i < num; i++ { if _, ok := deletedKeys[i]; ok { continue } keys = append(keys, i) } // test index key for _, key := range keys { rows := s.mustQuery(c, "select c1 from t1 where c3 = ?", key) matchRows(c, rows, [][]interface{}{{key}}) } // test delete key not in index for key := range deletedKeys { rows := s.mustQuery(c, "select c1 from t1 where c3 = ?", key) matchRows(c, rows, nil) } // test index range for i := 0; i < 100; i++ { index := rand.Intn(len(keys) - 3) rows := s.mustQuery(c, "select c1 from t1 where c3 >= ? limit 3", keys[index]) matchRows(c, rows, [][]interface{}{{keys[index]}, {keys[index+1]}, {keys[index+2]}}) } rows := s.mustQuery(c, "explain select c1 from t1 where c3 >= 100") ay := dumpRows(c, rows) c.Assert(strings.Contains(fmt.Sprintf("%v", ay), "c3_index"), IsTrue) // get all row handles ctx := s.s.(context.Context) t := s.testGetTable(c, "t1") handles := make(map[int64]struct{}) err := t.IterRecords(ctx, t.FirstKey(), t.Cols(), func(h int64, data []interface{}, cols []*column.Col) (bool, error) { handles[h] = struct{}{} return true, nil }) c.Assert(err, IsNil) // check in index idx := kv.NewKVIndex(t.IndexPrefix(), "c3_index", false) txn, err := ctx.GetTxn(true) c.Assert(err, IsNil) defer ctx.FinishTxn(true) it, err := idx.SeekFirst(txn) c.Assert(err, IsNil) defer it.Close() for { _, h, err := it.Next() if terror.ErrorEqual(err, io.EOF) { break } c.Assert(err, IsNil) _, ok := handles[h] c.Assert(ok, IsTrue) delete(handles, h) } c.Assert(handles, HasLen, 0) }
func (s *testDBSuite) testDropIndex(c *C) { done := make(chan struct{}, 1) s.mustExec(c, "delete from t1") num := 100 // add some rows for i := 0; i < num; i++ { s.mustExec(c, "insert into t1 values (?, ?, ?)", i, i, i) } go func() { s.mustExec(c, "drop index c3_index on t1") done <- struct{}{} }() ticker := time.NewTicker(s.lease / 2) defer ticker.Stop() LOOP: for { select { case <-done: break LOOP case <-ticker.C: step := 10 // delete some rows, and add some data for i := num; i < num+step; i++ { n := rand.Intn(num) s.mustExec(c, "update t1 set c2 = 1 where c1 = ?", n) s.mustExec(c, "insert into t1 values (?, ?, ?)", i, i, i) } num += step } } rows := s.mustQuery(c, "explain select c1 from t1 where c3 >= 0") ay := dumpRows(c, rows) c.Assert(strings.Contains(fmt.Sprintf("%v", ay), "c3_index"), IsFalse) // check in index, must no index in kv ctx := s.s.(context.Context) handles := make(map[int64]struct{}) t := s.testGetTable(c, "t1") idx := kv.NewKVIndex(t.IndexPrefix(), "c3_index", false) txn, err := ctx.GetTxn(true) c.Assert(err, IsNil) defer ctx.FinishTxn(true) it, err := idx.SeekFirst(txn) c.Assert(err, IsNil) defer it.Close() for { _, h, err := it.Next() if terror.ErrorEqual(err, io.EOF) { break } c.Assert(err, IsNil) handles[h] = struct{}{} } c.Assert(handles, HasLen, 0) }
func (s *testIndexSuite) TestIndex(c *C) { index := kv.NewKVIndex("i", "test", false) // Test ununiq index. txn, err := s.s.Begin() c.Assert(err, IsNil) values := []interface{}{1, 2} err = index.Create(txn, values, 1) c.Assert(err, IsNil) it, err := index.SeekFirst(txn) c.Assert(err, IsNil) getValues, h, err := it.Next() c.Assert(err, IsNil) c.Assert(getValues, HasLen, 2) c.Assert(getValues[0], Equals, int64(1)) c.Assert(getValues[1], Equals, int64(2)) c.Assert(h, Equals, int64(1)) it.Close() exist, _, err := index.Exist(txn, values, 100) c.Assert(err, IsNil) c.Assert(exist, IsFalse) exist, _, err = index.Exist(txn, values, 1) c.Assert(err, IsNil) c.Assert(exist, IsTrue) err = index.Delete(txn, values, 1) c.Assert(err, IsNil) it, err = index.SeekFirst(txn) c.Assert(err, IsNil) _, _, err = it.Next() c.Assert(errors2.ErrorEqual(err, io.EOF), IsTrue) it.Close() err = index.Create(txn, values, 0) c.Assert(err, IsNil) _, err = index.SeekFirst(txn) c.Assert(err, IsNil) _, hit, err := index.Seek(txn, values) c.Assert(err, IsNil) c.Assert(hit, IsTrue) err = index.Drop(txn) c.Assert(err, IsNil) it, hit, err = index.Seek(txn, values) c.Assert(err, IsNil) c.Assert(hit, IsFalse) _, _, err = it.Next() c.Assert(errors2.ErrorEqual(err, io.EOF), IsTrue) it.Close() it, err = index.SeekFirst(txn) c.Assert(err, IsNil) _, _, err = it.Next() c.Assert(errors2.ErrorEqual(err, io.EOF), IsTrue) it.Close() err = txn.Commit() c.Assert(err, IsNil) index = kv.NewKVIndex("j", "test", true) // Test uniq index. txn, err = s.s.Begin() c.Assert(err, IsNil) err = index.Create(txn, values, 1) c.Assert(err, IsNil) err = index.Create(txn, values, 2) c.Assert(err, NotNil) exist, h, err = index.Exist(txn, values, 1) c.Assert(err, IsNil) c.Assert(h, Equals, int64(1)) c.Assert(exist, IsTrue) exist, h, err = index.Exist(txn, values, 2) c.Assert(err, NotNil) c.Assert(h, Equals, int64(1)) c.Assert(exist, IsTrue) err = txn.Commit() c.Assert(err, IsNil) }
func (p *testFromSuit) TestTableDefaultPlan(c *C) { pln := &plans.TableDefaultPlan{ T: p.tbl, Fields: []*field.ResultField{ field.ColToResultField(p.tbl.Cols()[0], "t"), field.ColToResultField(p.tbl.Cols()[1], "t"), }, } ret := map[int64][]byte{} rset := rsets.Recordset{Ctx: p, Plan: pln} rset.Do(func(data []interface{}) (bool, error) { ret[data[0].(int64)] = data[1].([]byte) return true, nil }) excepted := map[int64][]byte{} for i := 0; i < 10; i++ { excepted[int64(i*10)] = []byte("hello") } //c.Assert(reflect.DeepEqual(ret, excepted), Equals, true) c.Assert(ret, DeepEquals, excepted) // expr: id > 0 expr := &expression.BinaryOperation{ Op: opcode.GE, L: &expression.Ident{ CIStr: model.NewCIStr("id"), }, R: &expression.Value{ Val: 5, }, } _, filtered, err := pln.FilterForUpdateAndDelete(p, expr) c.Assert(err, IsNil) c.Assert(filtered, IsFalse) // with no index idxCol := &column.IndexedCol{ IndexInfo: model.IndexInfo{ Name: model.NewCIStr("id"), Table: model.NewCIStr("t"), Columns: []*model.IndexColumn{ { Name: model.NewCIStr("id"), Offset: 0, Length: 0, }, }, Unique: false, Primary: false, State: model.StatePublic, }, } idxCol.X = kv.NewKVIndex([]byte("i"), "id", 0, false) p.tbl.AddIndex(idxCol) expr4 := &expression.Ident{ CIStr: model.NewCIStr("id"), } _, filtered, err = pln.FilterForUpdateAndDelete(p, expr4) c.Assert(err, IsNil) // with no index c.Assert(filtered, IsTrue) expr5 := &expression.IsNull{ Expr: &expression.Ident{ CIStr: model.NewCIStr("id"), }, Not: true, } _, filtered, err = pln.FilterForUpdateAndDelete(p, expr5) c.Assert(err, IsNil) // with no index c.Assert(filtered, IsTrue) }
func (p *testFromSuit) TestTableDefaultPlan(c *C) { pln := &plans.TableDefaultPlan{ T: p.tbl, Fields: []*field.ResultField{ field.ColToResultField(p.cols[0], "t"), field.ColToResultField(p.cols[1], "t"), }, } ret := map[int64]string{} pln.Do(p, func(id interface{}, data []interface{}) (bool, error) { ret[data[0].(int64)] = data[1].(string) return true, nil }) excepted := map[int64]string{} for i := 0; i < 10; i++ { excepted[int64(i*10)] = "hello" } c.Assert(reflect.DeepEqual(ret, excepted), Equals, true) // expr: id > 0 expr := &expressions.BinaryOperation{ Op: opcode.GE, L: &expressions.Ident{ CIStr: model.NewCIStr("id"), }, R: &expressions.Value{ Val: 5, }, } _, filtered, err := pln.FilterForUpdateAndDelete(p, expr) c.Assert(err, IsNil) c.Assert(filtered, IsFalse) // with no index idxCol := &column.IndexedCol{ IndexInfo: model.IndexInfo{ Name: model.NewCIStr("id"), Table: model.NewCIStr("t"), Columns: []*model.IndexColumn{ { Name: model.NewCIStr("id"), Offset: 0, Length: 0, }, }, Unique: false, Primary: false, }, X: kv.NewKVIndex("i", "id", false), } p.tbl.AddIndex(idxCol) expr4 := &expressions.Ident{ CIStr: model.NewCIStr("id"), } _, filtered, err = pln.FilterForUpdateAndDelete(p, expr4) c.Assert(err, IsNil) // with no index c.Assert(filtered, IsTrue) expr5 := &expressions.IsNull{ Expr: &expressions.Ident{ CIStr: model.NewCIStr("id"), }, Not: true, } _, filtered, err = pln.FilterForUpdateAndDelete(p, expr5) c.Assert(err, IsNil) // with no index c.Assert(filtered, IsTrue) }
func (s *testSuite) TestScan(c *C) { alloc := autoid.NewAllocator(s.store, s.dbInfo.ID) tb, err := tables.TableFromMeta(alloc, s.tbInfo) c.Assert(err, IsNil) indices := tb.Indices() _, err = tb.AddRecord(s.ctx, []interface{}{10, 11}) c.Assert(err, IsNil) s.ctx.FinishTxn(false) record1 := &RecordData{Handle: int64(1), Values: []interface{}{int64(10), int64(11)}} record2 := &RecordData{Handle: int64(2), Values: []interface{}{int64(20), int64(21)}} ver, err := s.store.CurrentVersion() c.Assert(err, IsNil) records, _, err := ScanSnapshotTableRecord(s.store, ver, tb, int64(1), 1) c.Assert(err, IsNil) c.Assert(records, DeepEquals, []*RecordData{record1}) _, err = tb.AddRecord(s.ctx, record2.Values) c.Assert(err, IsNil) s.ctx.FinishTxn(false) txn, err := s.store.Begin() c.Assert(err, IsNil) records, nextHandle, err := ScanTableRecord(txn, tb, int64(1), 1) c.Assert(err, IsNil) c.Assert(records, DeepEquals, []*RecordData{record1}) records, nextHandle, err = ScanTableRecord(txn, tb, nextHandle, 1) c.Assert(err, IsNil) c.Assert(records, DeepEquals, []*RecordData{record2}) startHandle := nextHandle records, nextHandle, err = ScanTableRecord(txn, tb, startHandle, 1) c.Assert(err, IsNil) c.Assert(records, IsNil) c.Assert(nextHandle, Equals, startHandle) idxRow1 := &RecordData{Handle: int64(1), Values: []interface{}{int64(10)}} idxRow2 := &RecordData{Handle: int64(2), Values: []interface{}{int64(20)}} kvIndex := kv.NewKVIndex(tb.IndexPrefix(), indices[0].Name.L, indices[0].ID, indices[0].Unique) idxRows, nextVals, err := ScanIndexData(txn, kvIndex, idxRow1.Values, 2) c.Assert(err, IsNil) c.Assert(idxRows, DeepEquals, []*RecordData{idxRow1, idxRow2}) idxRows, nextVals, err = ScanIndexData(txn, kvIndex, idxRow1.Values, 1) c.Assert(err, IsNil) c.Assert(idxRows, DeepEquals, []*RecordData{idxRow1}) idxRows, nextVals, err = ScanIndexData(txn, kvIndex, nextVals, 1) c.Assert(err, IsNil) c.Assert(idxRows, DeepEquals, []*RecordData{idxRow2}) idxRows, nextVals, err = ScanIndexData(txn, kvIndex, nextVals, 1) c.Assert(idxRows, IsNil) c.Assert(nextVals, DeepEquals, []interface{}{nil}) c.Assert(err, IsNil) s.testTableData(c, tb, []*RecordData{record1, record2}) s.testIndex(c, tb, tb.Indices()[0]) err = tb.RemoveRecord(s.ctx, 1, record1.Values) c.Assert(err, IsNil) err = tb.RemoveRecord(s.ctx, 2, record2.Values) c.Assert(err, IsNil) }