value.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. package migrate
  2. import (
  3. "TsDB2/DB/core/types"
  4. "fmt"
  5. "math"
  6. "math/big"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "unicode/utf8"
  11. )
  12. // ============================ 值转换 ============================
  13. //
  14. // 两端拿到的值形态不同:
  15. // VRTX 侧 (deserializeRow 的产出): uint8 / int16 / int32 / uint64 / float64 / []byte
  16. // SQL 侧 (database/sql 的驱动值): int64 / float64 / []byte / string / nil / time.Time
  17. //
  18. // 中间表示不做统一 (会把值搬两遍), 而是让目标端按自己的列类型把来源值"收编":
  19. // 走到 VRTX 用 coerceForVRTX, 走到 SQL 用 coerceForSQL。
  20. // zeroForVRTX 某种 VRTX 列类型的零值。
  21. //
  22. // VRTX 没有 NULL 这个概念 (变长列的行内引用 PageID==0 就是"空"), 所以 SQL 的 NULL
  23. // 落到这里只能是零值 —— 这是单向的信息损失, 文档里明说。
  24. func zeroForVRTX(ft types.FieldType) any {
  25. switch ft {
  26. case types.FieldType_1byte:
  27. return uint8(0)
  28. case types.FieldType_2byte:
  29. return int16(0)
  30. case types.FieldType_4byte:
  31. return int32(0)
  32. case types.FieldType_8byte:
  33. return uint64(0)
  34. case types.FieldType_Int8:
  35. return int8(0)
  36. case types.FieldType_Int16:
  37. return int16(0)
  38. case types.FieldType_Int32:
  39. return int32(0)
  40. case types.FieldType_Int64:
  41. return int64(0)
  42. case types.FieldType_Float64:
  43. return float64(0)
  44. case types.FieldType_Float32:
  45. return float32(0)
  46. case types.FieldType_Bool:
  47. return false
  48. case types.FieldType_Decimal:
  49. return types.Decimal{}
  50. case types.FieldType_UUID:
  51. return types.UUID{}
  52. case types.FieldType_Year:
  53. return int16(0)
  54. case types.FieldType_Int128:
  55. return new(big.Int)
  56. default:
  57. return []byte{}
  58. }
  59. }
  60. // coerceForVRTX 把来源值规范成 core.writeFieldValue 认得的确切 Go 类型。
  61. //
  62. // 必须逐一对齐 (1byte→uint8, 2byte→int16, 4byte→int32, 8byte→uint64,
  63. // Int8→int8, Int16→int16, Int32→int32, Int64→int64,
  64. // float64→float64, 定长/变长→[]byte): writeFieldValue 内部是类型断言,
  65. // 给错类型不是"写进去一个错值", 而是直接 panic。
  66. //
  67. // param 是列级参数 (Decimal 的 scale): 定点列必须按它对齐小数位, 否则同一列里
  68. // 混进另一种 scale 的行, B+ 树的字节序比较会静默出错。
  69. //
  70. // nullable 决定 nil 怎么落地: 可空列写 NULL (原样把 nil 交给 core, 由它置 NULL
  71. // 位图的位), 不可空列才退化成零值。
  72. func coerceForVRTX(ft types.FieldType, param byte, v any, col string, strict bool, nullable bool) (any, error) {
  73. if v == nil {
  74. // SQL 侧的 NULL 读出来就是 nil (见 SQL.Scan 的 *any)。可空列必须把它写成
  75. // NULL —— 落成零值等于"把没有值"变成"值是 0 / 空串", 而且不可逆: 导回去
  76. // 分不出这行的 0 原本是 NULL 还是真 0。
  77. if nullable {
  78. return nil, nil
  79. }
  80. return zeroForVRTX(ft), nil
  81. }
  82. switch ft {
  83. case types.FieldType_1byte:
  84. n, err := asInt64(v)
  85. if err != nil {
  86. return nil, fmt.Errorf("列 %s: %w", col, err)
  87. }
  88. if n < 0 || n > math.MaxUint8 {
  89. if strict {
  90. return nil, fmt.Errorf("列 %s (1byte): 值 %d 超出 0..255", col, n)
  91. }
  92. n &= 0xFF
  93. }
  94. return uint8(n), nil
  95. case types.FieldType_2byte:
  96. n, err := asInt64(v)
  97. if err != nil {
  98. return nil, fmt.Errorf("列 %s: %w", col, err)
  99. }
  100. if strict && (n < math.MinInt16 || n > math.MaxInt16) {
  101. return nil, fmt.Errorf("列 %s (2byte): 值 %d 超出 int16", col, n)
  102. }
  103. return int16(n), nil
  104. case types.FieldType_4byte:
  105. n, err := asInt64(v)
  106. if err != nil {
  107. return nil, fmt.Errorf("列 %s: %w", col, err)
  108. }
  109. if strict && (n < math.MinInt32 || n > math.MaxInt32) {
  110. return nil, fmt.Errorf("列 %s (4byte): 值 %d 超出 int32", col, n)
  111. }
  112. return int32(n), nil
  113. case types.FieldType_8byte:
  114. // 有符号来源按"64 位位模式"原样搬: int64(-1) → uint64(0xFFFF...)。
  115. // 导出回 SQL 时再用 int64(u) 写, 于是 MySQL 的 -1 ↔ VRTX 的 0xFFFF...
  116. // 往返无损 —— VRTX 的 8byte 本身不分正负, 同一个位模式 Go 侧既可当
  117. // uint64 也可当 int64 读, 正是 ORM 让 int64 字段能存负数的原理。
  118. u, err := asUint64(v)
  119. if err != nil {
  120. return nil, fmt.Errorf("列 %s: %w", col, err)
  121. }
  122. return u, nil
  123. case types.FieldType_Int8:
  124. n, err := asInt64(v)
  125. if err != nil {
  126. return nil, fmt.Errorf("列 %s: %w", col, err)
  127. }
  128. if strict && (n < math.MinInt8 || n > math.MaxInt8) {
  129. return nil, fmt.Errorf("列 %s (int8): 值 %d 超出 -128..127", col, n)
  130. }
  131. return int8(n), nil
  132. case types.FieldType_Int16:
  133. n, err := asInt64(v)
  134. if err != nil {
  135. return nil, fmt.Errorf("列 %s: %w", col, err)
  136. }
  137. if strict && (n < math.MinInt16 || n > math.MaxInt16) {
  138. return nil, fmt.Errorf("列 %s (int16): 值 %d 超出 int16", col, n)
  139. }
  140. return int16(n), nil
  141. case types.FieldType_Int32:
  142. n, err := asInt64(v)
  143. if err != nil {
  144. return nil, fmt.Errorf("列 %s: %w", col, err)
  145. }
  146. if strict && (n < math.MinInt32 || n > math.MaxInt32) {
  147. return nil, fmt.Errorf("列 %s (int32): 值 %d 超出 int32", col, n)
  148. }
  149. return int32(n), nil
  150. case types.FieldType_Int64:
  151. n, err := asInt64(v)
  152. if err != nil {
  153. return nil, fmt.Errorf("列 %s: %w", col, err)
  154. }
  155. return n, nil
  156. case types.FieldType_Float64:
  157. f, err := asFloat64(v)
  158. if err != nil {
  159. return nil, fmt.Errorf("列 %s: %w", col, err)
  160. }
  161. return f, nil
  162. // ===== 扩展类型: 一律归到 core 的标准解码载体 =====
  163. // (Bool→bool, Float32→float32, Decimal→types.Decimal, UUID→types.UUID,
  164. // Year→int16, Int128→*big.Int) —— 与 core/types.DecodeValue 反着走一遍。
  165. case types.FieldType_Float32:
  166. return coerceFloat32(v, col, strict)
  167. case types.FieldType_Bool:
  168. n, err := asInt64(v)
  169. if err != nil {
  170. return nil, fmt.Errorf("列 %s: %w", col, err)
  171. }
  172. if strict && n != 0 && n != 1 {
  173. return nil, fmt.Errorf("列 %s (bool): 值 %d 不是 0/1 —— "+
  174. "bool 只认 0/1, 否则等值与索引会给出多套答案", col, n)
  175. }
  176. return n != 0, nil
  177. case types.FieldType_Decimal:
  178. d, err := types.DecimalFromValue(v)
  179. if err != nil {
  180. return nil, fmt.Errorf("列 %s: %w", col, err)
  181. }
  182. if uint16(param) != d.Scale() {
  183. d, err = d.Rescale(uint16(param))
  184. if err != nil {
  185. return nil, fmt.Errorf("列 %s: %w", col, err)
  186. }
  187. }
  188. return d, nil
  189. case types.FieldType_UUID:
  190. u, err := asUUID(v)
  191. if err != nil {
  192. return nil, fmt.Errorf("列 %s: %w", col, err)
  193. }
  194. return u, nil
  195. case types.FieldType_Year:
  196. n, err := asInt64(v)
  197. if err != nil {
  198. return nil, fmt.Errorf("列 %s: %w", col, err)
  199. }
  200. if strict && (n < 0 || n > math.MaxInt16) {
  201. return nil, fmt.Errorf("列 %s (year): 值 %d 超出 0..32767", col, n)
  202. }
  203. return int16(n), nil
  204. case types.FieldType_Int128:
  205. bi, err := asBigInt(v)
  206. if err != nil {
  207. return nil, fmt.Errorf("列 %s: %w", col, err)
  208. }
  209. return bi, nil
  210. default:
  211. // 定长字符串 / 变长列
  212. b, err := asBytes(v)
  213. if err != nil {
  214. return nil, fmt.Errorf("列 %s: %w", col, err)
  215. }
  216. if !types.IsDynamic(ft) {
  217. // 容量取列的真实宽度 (宽度码), 不是类型默认档: 对声明 1024 字节的列按 256
  218. // 判断会把合法值判成超长。
  219. size := int(types.FieldSize(ft, param))
  220. if len(b) > size {
  221. if strict {
  222. return nil, fmt.Errorf("列 %s (%s): 值 %d 字节超过定长列容量 %d 字节, "+
  223. "导入会截断 —— 请把该列改成 dynamic 或放宽源值", col, TypeName(ft), len(b), size)
  224. }
  225. b = b[:size]
  226. }
  227. }
  228. return b, nil
  229. }
  230. }
  231. // coerceFloat32 收编到单精度。来源已是 float32 时原样; float64 只在 strict 下要求
  232. // 无损 —— 让"迁移会丢精度"这件事在严格档下当场暴露, 而不是事后对不上数。
  233. func coerceFloat32(v any, col string, strict bool) (float32, error) {
  234. if f32, ok := v.(float32); ok {
  235. return f32, nil
  236. }
  237. f, err := asFloat64(v)
  238. if err != nil {
  239. return 0, fmt.Errorf("列 %s: %w", col, err)
  240. }
  241. fb := float32(f)
  242. if strict && float64(fb) != f {
  243. return 0, fmt.Errorf("列 %s (float32): 值 %v 用 float32 表示会损失精度", col, f)
  244. }
  245. return fb, nil
  246. }
  247. // asUUID 把来源值收成 UUID: UUID 值 / 16 字节 / 文本 (带或不带连字符)。
  248. func asUUID(v any) (types.UUID, error) {
  249. var u types.UUID
  250. switch x := v.(type) {
  251. case types.UUID:
  252. return x, nil
  253. case []byte:
  254. if len(x) != types.UUIDSize {
  255. return u, fmt.Errorf("UUID 需要 %d 字节, 得到 %d 字节", types.UUIDSize, len(x))
  256. }
  257. copy(u[:], x)
  258. return u, nil
  259. case string:
  260. return types.ParseUUID(x)
  261. default:
  262. return u, fmt.Errorf("无法把 %T 当 UUID", v)
  263. }
  264. }
  265. // asBigInt 把来源值收成 *big.Int (128 位整数的标准载体)。
  266. func asBigInt(v any) (*big.Int, error) {
  267. switch x := v.(type) {
  268. case *big.Int:
  269. if x == nil {
  270. return new(big.Int), nil
  271. }
  272. return new(big.Int).Set(x), nil
  273. case big.Int:
  274. return new(big.Int).Set(&x), nil
  275. case types.Decimal:
  276. if x.Scale() != 0 {
  277. return nil, fmt.Errorf("int128 列不能接收带小数的值 %s", x.String())
  278. }
  279. return x.Coefficient(), nil
  280. case []byte:
  281. return parseInt128(string(x))
  282. case string:
  283. return parseInt128(x)
  284. case uint64:
  285. return new(big.Int).SetUint64(x), nil
  286. case int64:
  287. return big.NewInt(x), nil
  288. default:
  289. n, err := asInt64(v)
  290. if err != nil {
  291. return nil, err
  292. }
  293. return big.NewInt(n), nil
  294. }
  295. }
  296. // parseInt128 解析十进制/十六进制文本的 128 位整数 (base 0 支持 0x 前缀)。
  297. func parseInt128(s string) (*big.Int, error) {
  298. s = strings.TrimSpace(s)
  299. bi, ok := new(big.Int).SetString(s, 0)
  300. if !ok {
  301. return nil, fmt.Errorf("无法把 %q 当 128 位整数", s)
  302. }
  303. return bi, nil
  304. }
  305. // coerceForSQL 把来源值规范成 database/sql 能绑定的类型。
  306. //
  307. // 三个约定:
  308. // - 8byte 必须按方言选 Go 类型 (mysql 参数), 因为两个方言的声明本就不同,
  309. // 绑定类型得跟着走 —— 见下面 FieldType_8byte 分支。
  310. // - 字节类列 (定长串 / 变长) 若是合法 UTF-8 就绑成 string, 否则绑 []byte。
  311. // 前者让 SQLite 把它存成 TEXT (BLOB 亲和性的列不会自动转换),
  312. // 后者保证二进制列不被改坏。定长列导出时去掉尾部补零。
  313. // - 时间列 (TimeKind 非空) 一律还原成时间文本, 不写纳秒整数。
  314. //
  315. // mysql 来自目标方言 (Sink), 与 sqlTypeName 的第二个参数同源。
  316. func coerceForSQL(c Column, v any, mysql bool) (any, error) {
  317. if v == nil {
  318. // 可空列: 原样写 NULL。以前所有列都不可空, 这里一路把 nil 落成零值 ——
  319. // 在可空列上那是【静默改数据】: VRTX 的 NULL 到了 SQL 变成 0 / 空串 /
  320. // 1970-01-01, 而且不可逆, 再导回来分不出它原本是 NULL。
  321. if c.Nullable {
  322. return nil, nil
  323. }
  324. if c.IsDynamic() {
  325. return []byte{}, nil
  326. }
  327. if c.IsTime() {
  328. // 时间列的零值给 Unix 0, 不是数字 0 —— 后者塞进 DATETIME 列不是
  329. // "1970" 而是让 MySQL 报错 (严格模式) 或存成 0000-00-00。
  330. return formatSQLTime(0, c.TimeKind), nil
  331. }
  332. return zeroForSQL(c), nil
  333. }
  334. switch c.Type {
  335. case types.FieldType_1byte:
  336. n, err := asInt64(v)
  337. if err != nil {
  338. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  339. }
  340. return int64(uint8(n)), nil
  341. case types.FieldType_2byte:
  342. n, err := asInt64(v)
  343. if err != nil {
  344. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  345. }
  346. return n, nil
  347. case types.FieldType_4byte:
  348. n, err := asInt64(v)
  349. if err != nil {
  350. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  351. }
  352. return n, nil
  353. case types.FieldType_8byte:
  354. u, err := asUint64(v)
  355. if err != nil {
  356. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  357. }
  358. if c.IsTime() {
  359. // 时间列: 盘上是 Unix 纳秒, 回 SQL 得还原成时间文本 —— 否则 MySQL 的
  360. // DATETIME 列会收到一个 1.7e18 的整数 (驱动不会替我们转)。
  361. return formatSQLTime(int64(u), c.TimeKind), nil
  362. }
  363. // 绑定的 Go 类型必须跟目标列的声明一致, 而两个方言的声明不同:
  364. // - MySQL: sqlTypeName 声明成 BIGINT UNSIGNED (8byte 是无符号档),
  365. // 所以绑 uint64 —— 整个 2^64 区间都写得进去。以前一律绑 int64(u),
  366. // 高位为 1 的值 (包括 VRTX 从 int64 负值搬过来的位模式) 在严格模式下
  367. // 会被 MySQL 判成 "Out of range value" 直接报错, 非严格模式则被静默改写。
  368. // - SQLite: 只有有符号的 INTEGER, 绑 int64 —— 位模式原样存进 64 位整数,
  369. // 读回来还是同一个 8 字节。
  370. // 两条路共同的保证是【位模式不变】, 这正是 VRTX 8byte 的语义 (不分正负)。
  371. if mysql {
  372. return u, nil
  373. }
  374. return int64(u), nil
  375. case types.FieldType_Int8, types.FieldType_Int16, types.FieldType_Int32, types.FieldType_Int64:
  376. // 有符号档存的就是数值本身 (盘上的保序编码由 core 负责), 直接绑 int64
  377. n, err := asInt64(v)
  378. if err != nil {
  379. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  380. }
  381. return n, nil
  382. case types.FieldType_Float64:
  383. f, err := asFloat64(v)
  384. if err != nil {
  385. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  386. }
  387. return f, nil
  388. // ===== 扩展类型 =====
  389. // Decimal / Int128 一律绑文本: 目标列分别是 DECIMAL(38,s)/VARCHAR(40) (MySQL) 与
  390. // TEXT (SQLite), 文本既能精确表达 128 位整数, 也不会被驱动转成浮点。
  391. // UUID 绑 16 字节 (BINARY(16)/BLOB)。Bool 绑 bool, Year 绑 int64。
  392. case types.FieldType_Float32:
  393. f, err := asFloat64(v)
  394. if err != nil {
  395. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  396. }
  397. return float32(f), nil
  398. case types.FieldType_Bool:
  399. n, err := asInt64(v)
  400. if err != nil {
  401. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  402. }
  403. return n != 0, nil
  404. case types.FieldType_Decimal:
  405. d, err := types.DecimalFromValue(v)
  406. if err != nil {
  407. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  408. }
  409. if uint16(c.Param) != d.Scale() {
  410. d, err = d.Rescale(uint16(c.Param))
  411. if err != nil {
  412. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  413. }
  414. }
  415. return d.String(), nil
  416. case types.FieldType_UUID:
  417. u, err := asUUID(v)
  418. if err != nil {
  419. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  420. }
  421. return append([]byte(nil), u[:]...), nil
  422. case types.FieldType_Year:
  423. n, err := asInt64(v)
  424. if err != nil {
  425. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  426. }
  427. return n, nil
  428. case types.FieldType_Int128:
  429. bi, err := asBigInt(v)
  430. if err != nil {
  431. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  432. }
  433. return bi.String(), nil
  434. default:
  435. b, err := asBytes(v)
  436. if err != nil {
  437. return nil, fmt.Errorf("列 %s: %w", c.Name, err)
  438. }
  439. if !c.IsDynamic() {
  440. b = trimTrailingNUL(b)
  441. }
  442. if utf8.Valid(b) {
  443. return string(b), nil
  444. }
  445. return b, nil
  446. }
  447. }
  448. func zeroForSQL(c Column) any {
  449. switch c.Type {
  450. case types.FieldType_Float64:
  451. return float64(0)
  452. case types.FieldType_Float32:
  453. return float32(0)
  454. case types.FieldType_Bool:
  455. return false
  456. case types.FieldType_Decimal:
  457. // 零值也要按列 scale 输出定点文本 ("0.00"), 否则 DECIMAL 列会收到 "0"。
  458. return types.NewDecimalInt64(0, uint16(c.Param)).String()
  459. case types.FieldType_UUID:
  460. return make([]byte, types.UUIDSize)
  461. case types.FieldType_Year:
  462. return int64(0)
  463. case types.FieldType_Int128:
  464. return "0"
  465. case types.FieldType_Blob:
  466. // 动态列的零值就是"空字节", 不是 6 字节的堆引用。
  467. return []byte{}
  468. case types.FieldType_1byte, types.FieldType_2byte, types.FieldType_4byte, types.FieldType_8byte,
  469. types.FieldType_Int8, types.FieldType_Int16, types.FieldType_Int32, types.FieldType_Int64:
  470. return int64(0)
  471. default:
  472. return ""
  473. }
  474. }
  475. // trimTrailingNUL 去掉定长列右补的 \x00。
  476. // VRTX 定长字符串是"右补零到固定宽度", SQL 侧不该带上这些补位 (否则 varchar
  477. // 里会多出一堆看不见的字符, 也没法用等值条件查)。
  478. func trimTrailingNUL(b []byte) []byte {
  479. end := len(b)
  480. for end > 0 && b[end-1] == 0 {
  481. end--
  482. }
  483. return b[:end]
  484. }
  485. // ============================ 底层取值 ============================
  486. func asInt64(v any) (int64, error) {
  487. switch x := v.(type) {
  488. case int64:
  489. return x, nil
  490. case int:
  491. return int64(x), nil
  492. case int32:
  493. return int64(x), nil
  494. case int16:
  495. return int64(x), nil
  496. case int8:
  497. return int64(x), nil
  498. case uint64:
  499. if x > math.MaxInt64 {
  500. return 0, fmt.Errorf("值 %d 超出 int64", x)
  501. }
  502. return int64(x), nil
  503. case uint32:
  504. return int64(x), nil
  505. case uint16:
  506. return int64(x), nil
  507. case uint8:
  508. return int64(x), nil
  509. case uint:
  510. return int64(x), nil
  511. case float64:
  512. return roundFloatToInt(x)
  513. case float32:
  514. return roundFloatToInt(float64(x))
  515. case bool:
  516. if x {
  517. return 1, nil
  518. }
  519. return 0, nil
  520. case []byte:
  521. return parseIntOrTime(string(x))
  522. case string:
  523. return parseIntOrTime(x)
  524. case time.Time:
  525. return x.UnixNano(), nil
  526. default:
  527. return 0, fmt.Errorf("无法把 %T 当整数", v)
  528. }
  529. }
  530. func roundFloatToInt(f float64) (int64, error) {
  531. if math.IsNaN(f) || math.IsInf(f, 0) {
  532. return 0, fmt.Errorf("值 %v 不是有限数", f)
  533. }
  534. // 上界必须是 >= 而不是 >: math.MaxInt64 (2^63-1) 在 float64 里存不下, 会被舍入
  535. // 到 2^63 —— 于是 f == 2^63 时 "f > MaxInt64" 是假, 放过去之后 int64(f) 越界,
  536. // Go 对越界的 float→int 转换是未定义结果 (amd64 上给 MinInt64), 静默存成反数。
  537. // 下界不受这个影响: MinInt64 = -2^63 在 float64 里可以精确表示。
  538. if f >= math.MaxInt64 || f < math.MinInt64 {
  539. return 0, fmt.Errorf("值 %v 超出 int64", f)
  540. }
  541. return int64(math.Round(f)), nil
  542. }
  543. // asUint64 与 asInt64 的差别只有一处: 有符号的负值不报错, 而是按位模式搬进 uint64。
  544. func asUint64(v any) (uint64, error) {
  545. switch x := v.(type) {
  546. case uint64:
  547. return x, nil
  548. case uint32:
  549. return uint64(x), nil
  550. case uint16:
  551. return uint64(x), nil
  552. case uint8:
  553. return uint64(x), nil
  554. case uint:
  555. return uint64(x), nil
  556. case int64:
  557. return uint64(x), nil // 位模式, 负数 → 高位全 1
  558. case int:
  559. return uint64(x), nil
  560. case int32:
  561. return uint64(x), nil
  562. case int16:
  563. return uint64(x), nil
  564. case int8:
  565. return uint64(x), nil
  566. case float64:
  567. n, err := roundFloatToInt(x)
  568. if err != nil {
  569. return 0, err
  570. }
  571. return uint64(n), nil
  572. case float32:
  573. n, err := roundFloatToInt(float64(x))
  574. if err != nil {
  575. return 0, err
  576. }
  577. return uint64(n), nil
  578. case bool:
  579. if x {
  580. return 1, nil
  581. }
  582. return 0, nil
  583. case []byte:
  584. return parseUintOrTime(string(x))
  585. case string:
  586. return parseUintOrTime(x)
  587. case time.Time:
  588. return uint64(x.UnixNano()), nil
  589. default:
  590. return 0, fmt.Errorf("无法把 %T 当整数", v)
  591. }
  592. }
  593. // ============================ 时间列 ============================
  594. //
  595. // VRTX 的时间表示只有一种: 8 字节整数 (Unix 纳秒时间戳)。SQL 的时间列迁进来
  596. // 要换算成它, 迁回去要换算回时间文本 —— 这一节就是这两个方向。
  597. // sqlTimeLayouts SQL 时间文本的常见写法。MySQL 的 DATETIME / TIMESTAMP 打出来
  598. // 就是第一个; SQLite 存的是 TEXT, 可能带 T 和时区; DATE 只有日期部分。
  599. var sqlTimeLayouts = []string{
  600. "2006-01-02 15:04:05.999999999",
  601. "2006-01-02 15:04:05",
  602. "2006-01-02T15:04:05.999999999Z07:00",
  603. "2006-01-02T15:04:05",
  604. "2006-01-02",
  605. }
  606. // parseSQLTime 把 SQL 时间文本解析成 Unix 纳秒。
  607. //
  608. // 一律按 UTC 解释 (除非文本自带偏移): MySQL 的 DATETIME 本身不带时区, 若用本地
  609. // 时区去解释, 同一份数据在不同机器上会迁出不同的时间戳 —— 迁移工具不该引入
  610. // 这种漂移。往返一致性优先于"看着像本地时间"。
  611. func parseSQLTime(s string) (int64, bool) {
  612. for _, layout := range sqlTimeLayouts {
  613. if tm, err := time.ParseInLocation(layout, s, time.UTC); err == nil {
  614. return tm.UnixNano(), true
  615. }
  616. }
  617. return 0, false
  618. }
  619. // parseIntOrTime 先按整数读, 读不动再按时间文本读。
  620. //
  621. // 时间列映射成 8byte 后, 值可能是 time.Time (MySQL 驱动开了 parseTime),
  622. // 也可能是文本 (parseTime=false 的默认档, 以及 SQLite 的 DATETIME 列) ——
  623. // 后者必须在这里换算, 否则写进 8byte 列时会被当成"不是数字"直接报错。
  624. func parseIntOrTime(s string) (int64, error) {
  625. s = strings.TrimSpace(s)
  626. if n, err := strconv.ParseInt(s, 10, 64); err == nil {
  627. return n, nil
  628. }
  629. if ns, ok := parseSQLTime(s); ok {
  630. return ns, nil
  631. }
  632. return 0, fmt.Errorf("无法把 %q 当整数 (也不是能识别的时间文本)", s)
  633. }
  634. // parseUintOrTime 同 parseIntOrTime, 只是落在无符号上。
  635. func parseUintOrTime(s string) (uint64, error) {
  636. s = strings.TrimSpace(s)
  637. if n, err := strconv.ParseUint(s, 10, 64); err == nil {
  638. return n, nil
  639. }
  640. if ns, ok := parseSQLTime(s); ok {
  641. return uint64(ns), nil
  642. }
  643. return 0, fmt.Errorf("无法把 %q 当整数 (也不是能识别的时间文本)", s)
  644. }
  645. // formatSQLTime Unix 纳秒 → SQL 时间文本。
  646. //
  647. // DATE 只留日期; DATETIME / TIMESTAMP 留到微秒 (纳秒到微秒是 SQL 时间类型本身
  648. // 的限制, MySQL 最细 DATETIME(6), 不是工具丢的)。
  649. func formatSQLTime(ns int64, kind string) string {
  650. tm := time.Unix(0, ns).UTC()
  651. if kind == "date" {
  652. return tm.Format("2006-01-02")
  653. }
  654. if ns%int64(time.Second) == 0 {
  655. return tm.Format("2006-01-02 15:04:05") // 整秒别拖一串 .000000
  656. }
  657. return tm.Format("2006-01-02 15:04:05.000000")
  658. }
  659. func asFloat64(v any) (float64, error) {
  660. switch x := v.(type) {
  661. case float64:
  662. return x, nil
  663. case float32:
  664. return float64(x), nil
  665. case int64:
  666. return float64(x), nil
  667. case int:
  668. return float64(x), nil
  669. case int32:
  670. return float64(x), nil
  671. case int16:
  672. return float64(x), nil
  673. case int8:
  674. return float64(x), nil
  675. case uint64:
  676. return float64(x), nil
  677. case uint32:
  678. return float64(x), nil
  679. case uint16:
  680. return float64(x), nil
  681. case uint8:
  682. return float64(x), nil
  683. case []byte:
  684. f, err := strconv.ParseFloat(strings.TrimSpace(string(x)), 64)
  685. if err != nil {
  686. return 0, fmt.Errorf("无法把 %q 当浮点", x)
  687. }
  688. return f, nil
  689. case string:
  690. f, err := strconv.ParseFloat(strings.TrimSpace(x), 64)
  691. if err != nil {
  692. return 0, fmt.Errorf("无法把 %q 当浮点", x)
  693. }
  694. return f, nil
  695. default:
  696. return 0, fmt.Errorf("无法把 %T 当浮点", v)
  697. }
  698. }
  699. func asBytes(v any) ([]byte, error) {
  700. switch x := v.(type) {
  701. case []byte:
  702. return x, nil
  703. case string:
  704. return []byte(x), nil
  705. case nil:
  706. return []byte{}, nil
  707. case time.Time:
  708. return []byte(x.Format("2006-01-02 15:04:05.999999999")), nil
  709. case bool:
  710. if x {
  711. return []byte{1}, nil
  712. }
  713. return []byte{0}, nil
  714. case int64:
  715. return []byte(strconv.FormatInt(x, 10)), nil
  716. case int:
  717. return []byte(strconv.Itoa(x)), nil
  718. case int32:
  719. return []byte(strconv.FormatInt(int64(x), 10)), nil
  720. case int16:
  721. return []byte(strconv.FormatInt(int64(x), 10)), nil
  722. case int8:
  723. return []byte(strconv.FormatInt(int64(x), 10)), nil
  724. case uint64:
  725. return []byte(strconv.FormatUint(x, 10)), nil
  726. case uint32:
  727. return []byte(strconv.FormatUint(uint64(x), 10)), nil
  728. case uint16:
  729. return []byte(strconv.FormatUint(uint64(x), 10)), nil
  730. case uint8:
  731. return []byte(strconv.FormatUint(uint64(x), 10)), nil
  732. case float64:
  733. return []byte(strconv.FormatFloat(x, 'g', -1, 64)), nil
  734. case float32:
  735. return []byte(strconv.FormatFloat(float64(x), 'g', -1, 32)), nil
  736. default:
  737. return nil, fmt.Errorf("无法把 %T 当字节串", v)
  738. }
  739. }