update_null_preserve_test.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. package core
  2. import (
  3. "testing"
  4. "time"
  5. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  6. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  7. )
  8. // TestUpdateNullPreserve 部分更新必须保留旧行的 NULL 列:
  9. // deserializeRow 不产出 NULL 列, 旧实现"未提供→零值兜底"会把旧行 NULL
  10. // 静默写成零值 (deleted_at 被污染 → 软删除作用域永久丢失该行,
  11. // 实测报告 #1/#3 根因)。
  12. func TestUpdateNullPreserve(t *testing.T) {
  13. db := fixOpenDB(t)
  14. tbl, err := db.CreateTable("tp", []types.FieldInfo{
  15. {FieldType: types.FieldType_Uint64, FieldName: []byte("id")},
  16. {FieldType: types.FieldType_DateTime, FieldName: []byte("created_at")},
  17. {FieldType: types.FieldType_DateTime, FieldName: []byte("updated_at")},
  18. {FieldType: types.FieldType_DateTime, Flag: types.FlagNullable, FieldName: []byte("deleted_at")},
  19. {FieldType: types.FieldType_String, Flag: types.WidthCodeFor(32) | types.FlagNullable, FieldName: []byte("title")},
  20. {FieldType: types.FieldType_Int64, Flag: types.FlagNullable, FieldName: []byte("port")},
  21. })
  22. if err != nil {
  23. t.Fatal(err)
  24. }
  25. if _, err := tbl.CreateIndex("pk", page.IndexTypePrimary, []byte{0}); err != nil {
  26. t.Fatal(err)
  27. }
  28. if _, err := tbl.InsertRowWithID(1, []types.FieldValue{
  29. {FieldName: []byte("id"), Value: uint64(1)},
  30. {FieldName: []byte("created_at"), Value: time.Now()},
  31. {FieldName: []byte("updated_at"), Value: time.Now()},
  32. {FieldName: []byte("deleted_at"), Value: nil},
  33. {FieldName: []byte("title"), Value: "原始"},
  34. {FieldName: []byte("port"), Value: int64(11)},
  35. }); err != nil {
  36. t.Fatal(err)
  37. }
  38. db.Commit()
  39. if err := tbl.UpdateRowByPK(1, []types.FieldValue{
  40. {FieldName: []byte("title"), Value: "字面量A"},
  41. {FieldName: []byte("updated_at"), Value: time.Now()},
  42. }); err != nil {
  43. t.Fatal(err)
  44. }
  45. storeRow, _ := tbl.indexMeta.ResolveByPK(1)
  46. rowData := storeRow[8:]
  47. t.Logf("rowData=%x", rowData)
  48. layout, _ := tbl.parseRowLayout(rowData)
  49. bm, _ := types.RowNullBitmap(rowData, layout.ends, len(layout.sv.FieldIndices))
  50. t.Logf("ends=%v bm=%x", layout.ends, bm)
  51. // deleted_at 的位 (fieldIndex 3 → pos 3)
  52. t.Logf("deleted_at NullBit=%v", types.NullBitSet(bm, 3))
  53. }