|
|
@@ -79,6 +79,50 @@ func TestInferSQLType(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// TestMySQLColumnDecl 钉住"字符列的容量按字节算"这条规则 (information_schema 里
|
|
|
+// CHARACTER_OCTET_LENGTH 那一列)。
|
|
|
+//
|
|
|
+// 这条是真实踩出来的: resource_info.resAlias 是 varchar(50) utf8mb4, 库里存了 73 字节
|
|
|
+// 的中文别名。老代码拿 50 (字符数) 去取档得到 64byte, 整表迁移失败 —— 而 50 个字符在
|
|
|
+// utf8mb4 下最多是 200 字节, 只有拿 200 去取档才装得下。
|
|
|
+func TestMySQLColumnDecl(t *testing.T) {
|
|
|
+ cases := []struct {
|
|
|
+ name string
|
|
|
+ dataType string
|
|
|
+ // information_schema 给的三组长度: 字节容量 / 精度 / 小数位
|
|
|
+ charBytes, numPrec, numScale int64
|
|
|
+ want types.FieldType
|
|
|
+ wantParam byte
|
|
|
+ }{
|
|
|
+ // varchar(50) utf8mb4 → octet 200 → 256byte (老算法 50 → 64byte, 装不下中文)
|
|
|
+ {"utf8mb4 varchar(50)", "varchar", 200, 0, 0, types.FieldType_256byte, 0},
|
|
|
+ // varchar(12) utf8mb4 → octet 48 → 64byte
|
|
|
+ {"utf8mb4 varchar(12)", "varchar", 48, 0, 0, types.FieldType_64byte, 0},
|
|
|
+ // 单字节字符集不背 4 倍: latin1 的 varchar(50) 就是 50 字节
|
|
|
+ {"latin1 varchar(50)", "varchar", 50, 0, 0, types.FieldType_64byte, 0},
|
|
|
+ // 正好卡在档位边界上: varchar(64) utf8mb4 = 256 字节, 用满最后一档
|
|
|
+ {"utf8mb4 varchar(64)", "varchar", 256, 0, 0, types.FieldType_256byte, 0},
|
|
|
+ // 再多一个字符就超出所有定长档: 宁可变长也不开窄列 (变长只是没索引, 不会丢数据)
|
|
|
+ {"utf8mb4 varchar(65)", "varchar", 260, 0, 0, types.FieldType_Dynamicbyte, 0},
|
|
|
+ {"utf8mb4 char(1)", "char", 4, 0, 0, types.FieldType_16byte, 0},
|
|
|
+ {"utf8mb4 text", "text", 65535, 0, 0, types.FieldType_Dynamicbyte, 0},
|
|
|
+ {"blob", "blob", 65535, 0, 0, types.FieldType_Blob, 0},
|
|
|
+ // 定点: (p,s) 要读进来, 小数位不能被抹平
|
|
|
+ {"decimal(18,2)", "decimal", 20, 18, 2, types.FieldType_Decimal, 2},
|
|
|
+ // 长度信息为 0 (数值/时间列没有 octet length) 时退回纯类型名
|
|
|
+ {"int", "int", 0, 10, 0, types.FieldType_Int32, 0},
|
|
|
+ {"datetime", "datetime", 0, 0, 0, types.FieldType_8byte, 0},
|
|
|
+ }
|
|
|
+ for _, c := range cases {
|
|
|
+ decl := mysqlColumnDecl(c.dataType, c.charBytes, c.numPrec, c.numScale)
|
|
|
+ ft, param := inferSQLType(decl, true)
|
|
|
+ if ft != c.want || param != c.wantParam {
|
|
|
+ t.Errorf("%s: %q → (%v, %d), 期望 (%v, %d)",
|
|
|
+ c.name, decl, ft, param, c.want, c.wantParam)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// TestSQLTypeRoundTrip 声明出去的类型名必须能被自己读回同一个类型。
|
|
|
//
|
|
|
// 例外的几种都【不依赖 meta 就得靠宽度/语义兜底】, 这里显式列出来当文档:
|