pk_int64_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package core
  2. // Int64 (及更窄整型) 主键镜像列的回归测试。
  3. //
  4. // 背景: 主键 B+ 树的键必须是 pkID(BE) —— 查询/删除/更新侧都按 pkID 定位。
  5. // 旧实现插入/删除/更新统一用 extractIndexKey (取镜像列的【存储字节】) 当树键:
  6. // Uint64 镜像"存储字节 == BE"侥幸正确, 而 Int64 等带 sort-key 编码的类型
  7. // 存储字节 ≠ pkID-BE —— 插入把行挂到错键上, 按 pkID 查找退化成全叶扫描,
  8. // 删除/更新按错键静默失效 (ErrNotFound 按索引逐个吞掉, 不报错也不动数据)。
  9. // 修复: 主键行存储的键恒为 pkID(BE) (rowStoreInsertKey), 与时序索引的
  10. // "时间分量"键源区分; 三侧 (插/删/改) 同一键源。
  11. import (
  12. "testing"
  13. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  14. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  15. )
  16. func int64PKTable(t *testing.T, db *DB, name string) *Table {
  17. t.Helper()
  18. tbl, err := db.CreateTable(name, []types.FieldInfo{
  19. {FieldType: types.FieldType_Int64, FieldName: []byte("id")},
  20. {FieldType: types.FieldType_Int64, FieldName: []byte("v")},
  21. })
  22. if err != nil {
  23. t.Fatalf("建表: %v", err)
  24. }
  25. if _, err := tbl.CreateIndex("pk", page.IndexTypePrimary, []byte{0}); err != nil {
  26. t.Fatalf("建索引: %v", err)
  27. }
  28. return tbl
  29. }
  30. func TestInt64PKDeleteUpdate(t *testing.T) {
  31. db := fixOpenDB(t)
  32. tbl := int64PKTable(t, db, "t")
  33. // 单行 + 批量两条路径都覆盖
  34. tbl.InsertRowWithID(1, []types.FieldValue{
  35. {FieldName: []byte("id"), Value: int64(1)},
  36. {FieldName: []byte("v"), Value: int64(10)},
  37. })
  38. if _, err := tbl.InsertRowsWithIDs([]uint64{2, 3}, [][]types.FieldValue{
  39. {{FieldName: []byte("id"), Value: int64(2)}, {FieldName: []byte("v"), Value: int64(20)}},
  40. {{FieldName: []byte("id"), Value: int64(3)}, {FieldName: []byte("v"), Value: int64(30)}},
  41. }); err != nil {
  42. t.Fatalf("批量插入: %v", err)
  43. }
  44. if err := db.Commit(); err != nil {
  45. t.Fatal(err)
  46. }
  47. // 按 pkID 删除: 必须真正消失 (修复前: 静默失效)
  48. if err := tbl.DeleteRowByPK(2); err != nil {
  49. t.Fatalf("delete: %v", err)
  50. }
  51. if _, err := tbl.FindByPK(2); err == nil {
  52. t.Fatalf("pkID=2 删除后仍可见 — Int64 主键删除静默失效")
  53. }
  54. if _, err := tbl.FindByPK(1); err != nil {
  55. t.Fatalf("pkID=1 被误删: %v", err)
  56. }
  57. if got := tbl.TotalRows(); got != 2 {
  58. t.Fatalf("TotalRows=%d, 想要 2", got)
  59. }
  60. // 更新: 修复前同样按错键静默失效
  61. if err := tbl.UpdateRowByPK(1, []types.FieldValue{
  62. {FieldName: []byte("v"), Value: int64(99)},
  63. }); err != nil {
  64. t.Fatalf("update: %v", err)
  65. }
  66. vals, err := tbl.FindByPK(1)
  67. if err != nil {
  68. t.Fatalf("find: %v", err)
  69. }
  70. var v int64
  71. for _, fv := range vals {
  72. if string(fv.FieldName) == "v" {
  73. v, _ = fv.Value.(int64)
  74. }
  75. }
  76. if v != 99 {
  77. t.Fatalf("更新未生效: v=%d, 想要 99", v)
  78. }
  79. // 二级索引不受键源修复影响 (仍按分量列字节编键, 插入/查询同侧一致)
  80. if _, err := tbl.CreateIndex("idx_v", page.IndexTypeBTree, []byte{1}); err != nil {
  81. t.Fatalf("建二级索引: %v", err)
  82. }
  83. if _, err := tbl.InsertRowWithID(4, []types.FieldValue{
  84. {FieldName: []byte("id"), Value: int64(4)},
  85. {FieldName: []byte("v"), Value: int64(99)}, // 与 id=1 同 v, 非唯一索引允许
  86. }); err != nil {
  87. t.Fatalf("insert 4: %v", err)
  88. }
  89. n := 0
  90. if err := tbl.Query().Where(Eq("v", int64(99), types.FieldType(0))).Scan(func(row []types.FieldValue) bool {
  91. n++
  92. return true
  93. }); err != nil {
  94. t.Fatal(err)
  95. }
  96. if n != 2 {
  97. t.Fatalf("二级索引应命中 2 行 (v=99), 实际 %d", n)
  98. }
  99. }