types_test.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. package DB
  2. import (
  3. "strconv"
  4. "strings"
  5. "testing"
  6. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  7. )
  8. // TestParseModelMaxlenCap `maxlen` 声明超过单值上限的字符串列必须在建模阶段就被拒。
  9. //
  10. // 这一层是唯一能拦它的地方: 声明超过 MaxInlineColumnWidth 的列落动态列, 而动态列【不保留
  11. // 声明长度】(Flag 低 5 位必须为 0), 表一旦建出来, 那个 N 就不在盘上了 —— 事后再想校验
  12. // "这一列声明的容量兑现得了吗"已无据可查。放过去的后果正是声明侧闸门要拦的那种失败:
  13. // 建表成功、跑了一阵、某天写入某个大值时才撞 ErrHeapPageTooLarge, 用户看不出因果。
  14. func TestParseModelMaxlenCap(t *testing.T) {
  15. // 下面 tag 里的字面量必须与实现同步 (单值上限 = core.MaxBlobSize, 由 core 侧的
  16. // 编译期断言钉住 types 那一份)。对不上就在这里编译失败, 而不是变成一个"碰巧还过"的测试。
  17. const atCap, overCap = 16360, 16361
  18. var (
  19. _ = [1]struct{}{}[atCap-types.MaxStringSize] // 正好用满 = 上限
  20. _ = [1]struct{}{}[overCap-types.MaxStringSize-1] // 比上限多一个字节
  21. )
  22. find := func(t *testing.T, mi *ModelInfo, col string) FieldDef {
  23. t.Helper()
  24. for _, fd := range mi.Fields {
  25. if fd.ColName == col {
  26. return fd
  27. }
  28. }
  29. t.Fatalf("模型里没有列 %s", col)
  30. return FieldDef{}
  31. }
  32. // 正好用满: 动态列装得下, 必须放行 —— 而且必须落成【动态列 + 宽度码 0】:
  33. // 这个宽度码是行内布局的唯一依据, 若它被写成非 0, FieldSize 就会按档位算宽度,
  34. // 整行字段从此错位 (比拒绝声明危险得多)。
  35. t.Run("正好用满放行", func(t *testing.T) {
  36. type AtCap struct {
  37. BaseModel
  38. Full string `vrtx:"maxlen:16360"`
  39. }
  40. mi, err := ParseModel(&AtCap{})
  41. if err != nil {
  42. t.Fatalf("声明 %d 字节应当放行: %v", atCap, err)
  43. }
  44. fd := find(t, mi, "Full")
  45. if fd.FieldType != types.FieldType_Dynamicbyte {
  46. t.Errorf("超过 %d 字节的声明应落动态列, 得到 %s",
  47. types.MaxInlineColumnWidth, types.TypeNameOf(fd.FieldType))
  48. }
  49. if fd.Param() != 0 {
  50. t.Errorf("动态列的宽度码必须留在 0 (Flag 低 5 位), 得到 %d", fd.Param())
  51. }
  52. })
  53. // 多一个字节: 兑现不了, 必须拒。
  54. t.Run("超过一个字节就拒", func(t *testing.T) {
  55. type OverCap struct {
  56. BaseModel
  57. Big string `vrtx:"maxlen:16361"`
  58. }
  59. _, err := ParseModel(&OverCap{})
  60. if err == nil {
  61. t.Fatalf("声明 %d 字节必须被拒 (动态列装不下)", overCap)
  62. }
  63. // 报错要能直接定位: 哪个字段、用户写下的数、引擎能给的上限。
  64. for _, want := range []string{"Big", strconv.Itoa(overCap), strconv.Itoa(types.MaxStringSize)} {
  65. if !strings.Contains(err.Error(), want) {
  66. t.Errorf("错误信息里缺少 %q: %v", want, err)
  67. }
  68. }
  69. })
  70. // 闸门只管字符串列: 数值列的 maxlen 本来就被忽略 (用 Go 类型自动推断), 不该因此报错。
  71. t.Run("数值列不受影响", func(t *testing.T) {
  72. type NumWide struct {
  73. BaseModel
  74. N int64 `vrtx:"maxlen:16361"`
  75. }
  76. if _, err := ParseModel(&NumWide{}); err != nil {
  77. t.Errorf("数值列的 maxlen 应当被忽略, 不该报错: %v", err)
  78. }
  79. })
  80. }