package core import ( "testing" "git.ycl.pub/yl32940/TsDB2/DB/core/types" ) // TestScanWithDeleteDuringBatchResolve 扫描回调里逐行删除 (demo/stress_test 第 12 步 // 的回归): 二级索引扫描物化 pkID 后走批量回表, 回调里的删除会改写主键 B+ 树 // (叶合并/NextPage 重接)。批量回表必须"分批物化、批后回调" —— 绝不能让用户回调 // 踩坏正在推进的叶链游标 (旧实现因此 index out of range)。 func TestScanWithDeleteDuringBatchResolve(t *testing.T) { db := fixOpenDB(t) tbl := qopNewTable(t, db, "t") qopSeed(t, tbl, 1000) if err := db.Commit(); err != nil { t.Fatalf("commit: %v", err) } deleted := 0 err := tbl.Query().Where(Ge("age", int64(0), 0)).ScanWithPKID(func(pkID uint64, _ []types.FieldValue) bool { if err := tbl.DeleteRow("pk", qopPKKey(pkID)); err != nil { t.Fatalf("删除 %d: %v", pkID, err) } deleted++ return true }) if err != nil { t.Fatalf("scan+delete: %v", err) } if deleted != 1000 { t.Fatalf("应删 1000 行, 实删 %d", deleted) } cnt, err := tbl.Query().Count() if err != nil || cnt != 0 { t.Fatalf("删尽后 count=%d err=%v, 想要 0", cnt, err) } } // TestScanWithUpdateDuringBatchResolve 扫描回调里逐行更新 (改索引列): // 更新会改写二级索引与行数据, 正在分批回表的游标必须不受影响, 且每行都被回调到。 func TestScanWithUpdateDuringBatchResolve(t *testing.T) { db := fixOpenDB(t) tbl := qopNewTable(t, db, "t") qopSeed(t, tbl, 500) if err := db.Commit(); err != nil { t.Fatalf("commit: %v", err) } updated := 0 err := tbl.Query().Where(In("age", []interface{}{int64(7), int64(8), int64(9)}, 0)). ScanWithPKID(func(pkID uint64, _ []types.FieldValue) bool { if err := tbl.UpdateRowByPK(pkID, []types.FieldValue{ {FieldName: []byte("age"), Value: int64(99)}, {FieldName: []byte("score"), Value: int64(1)}, }); err != nil { t.Fatalf("更新 %d: %v", pkID, err) } updated++ return true }) if err != nil { t.Fatalf("scan+update: %v", err) } // age ∈ {7,8,9} 且 id<=500: 每值 10 个 → 30 行 if updated != 30 { t.Fatalf("应更新 30 行, 实更新 %d", updated) } cnt, err := tbl.Query().Where(Eq("age", int64(99), 0)).Count() if err != nil || cnt != 30 { t.Fatalf("更新后 age=99 的 count=%d err=%v, 想要 30", cnt, err) } }