| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- package vrtxsql
- // 值与类型的双向翻译:
- // - SQL 字面量/driver.Value → 引擎列类型 (写入路径)
- // - 引擎值 → driver.Value (结果集路径, database/sql 只接受基础类型)
- // - SQL 类型名 → types.FieldInfo (CREATE TABLE 路径)
- import (
- "database/sql/driver"
- "fmt"
- "math"
- "strconv"
- "strings"
- "time"
- "git.ycl.pub/yl32940/TsDB2/DB/core"
- "git.ycl.pub/yl32940/TsDB2/DB/core/types"
- )
- // =============================================================================
- // driver.Value → 引擎值 (按列类型)
- // =============================================================================
- func driverToEngineValue(fi *types.FieldInfo, v driver.Value) (interface{}, error) {
- if v == nil {
- return nil, nil // NULL: 由引擎按 FlagNullable 裁决
- }
- switch fi.FieldType {
- case types.FieldType_DateTime:
- // time.Time 原样 (引擎编码层转毫秒); 数值已是毫秒; 字符串走字面量解析,
- // 其中零时间 (0001-01-01...) 解析出 nil → 返回 (nil, nil) = NULL,
- // 与 buildFieldValue 的 NULL 语义汇合 (软删除 UPDATE SET deleted_at=零时间 ⇒ NULL)。
- switch t := v.(type) {
- case time.Time:
- return t, nil
- case int64:
- if t < 0 {
- return nil, fmt.Errorf("时间戳不能为负: %d", t)
- }
- return t, nil // 已是毫秒
- case uint64:
- return t, nil // 已是毫秒
- case float64:
- if t < 0 {
- return nil, fmt.Errorf("时间戳不能为负: %v", t)
- }
- return int64(t), nil // 已是毫秒
- case string:
- return parseDateTimeLiteral(t)
- case []byte:
- return parseDateTimeLiteral(string(t))
- }
- return nil, typeMismatch(fi, v)
- case types.FieldType_Uint64:
- // 旧版驱动把 DATETIME 建成 Uint64 列: 整数之外宽容接受 time.Time /
- // 时间文本 / 毫秒串 (toDateTimeMillis 统一按毫秒), 保证老表时间读写不回退。
- switch v.(type) {
- case time.Time, string, []byte:
- return toDateTimeMillis(v)
- }
- return toInt64(v)
- case types.FieldType_Int64,
- types.FieldType_Uint32, types.FieldType_Int32,
- types.FieldType_Uint16, types.FieldType_Int16,
- types.FieldType_Uint8, types.FieldType_Int8,
- types.FieldType_Year:
- return toInt64(v)
- case types.FieldType_Float64, types.FieldType_Float32:
- return toFloat64(v)
- case types.FieldType_Bool:
- switch t := v.(type) {
- case bool:
- return t, nil
- case int64:
- return t != 0, nil
- case string:
- return strings.EqualFold(t, "true") || t == "1", nil
- }
- return nil, typeMismatch(fi, v)
- case types.FieldType_String:
- switch t := v.(type) {
- case string:
- return t, nil
- case []byte:
- return string(t), nil
- }
- return nil, typeMismatch(fi, v)
- case types.FieldType_Dynamicbyte:
- switch t := v.(type) {
- case []byte:
- return t, nil
- case string:
- return []byte(t), nil
- }
- return nil, typeMismatch(fi, v)
- case types.FieldType_Decimal:
- switch t := v.(type) {
- case string:
- return types.MustParseDecimal(t), nil
- case []byte:
- return types.MustParseDecimal(string(t)), nil
- case float64:
- return types.MustParseDecimal(strconv.FormatFloat(t, 'f', -1, 64)), nil
- }
- return nil, typeMismatch(fi, v)
- case types.FieldType_UUID:
- switch t := v.(type) {
- case string:
- return types.MustParseUUID(t), nil // 若存在解析入口则用之
- case []byte:
- if len(t) == 16 {
- var u [16]byte
- copy(u[:], t)
- return u, nil
- }
- return types.MustParseUUID(string(t)), nil
- }
- return nil, typeMismatch(fi, v)
- case types.FieldType_Blob:
- switch t := v.(type) {
- case []byte:
- return t, nil
- case string:
- return []byte(t), nil
- }
- return nil, typeMismatch(fi, v)
- }
- return nil, typeMismatch(fi, v)
- }
- func typeMismatch(fi *types.FieldInfo, v driver.Value) error {
- return fmt.Errorf("值类型 %T 与列 %q 的类型 %d 不匹配", v, fi.FieldName, fi.FieldType)
- }
- func toInt64(v driver.Value) (interface{}, error) {
- switch t := v.(type) {
- case int64:
- return t, nil
- case float64:
- return int64(t), nil
- case bool:
- if t {
- return int64(1), nil
- }
- return int64(0), nil
- case string:
- n, err := strconv.ParseInt(strings.TrimSpace(t), 10, 64)
- if err == nil {
- return n, nil
- }
- f, ferr := strconv.ParseFloat(strings.TrimSpace(t), 64)
- if ferr == nil {
- return int64(f), nil
- }
- case []byte:
- return toInt64(string(t))
- case time.Time:
- return int64(timeToMillis(t)), nil // 时间列: time.Time → 毫秒
- }
- return nil, fmt.Errorf("无法把 %T 转成整数", v)
- }
- func toFloat64(v driver.Value) (interface{}, error) {
- switch t := v.(type) {
- case float64:
- return t, nil
- case int64:
- return float64(t), nil
- case string:
- f, err := strconv.ParseFloat(strings.TrimSpace(t), 64)
- if err == nil {
- return f, nil
- }
- case []byte:
- return toFloat64(string(t))
- }
- return nil, fmt.Errorf("无法把 %T 转成浮点", v)
- }
- // =============================================================================
- // 字面量 → 引擎值 (WHERE/VALUES 里的纯文本数字/字符串)
- // =============================================================================
- // parseLiteralAsType 按列的实际类型解释字面量文本。数字字面量到整型列按 int64,
- // 到浮点按 float64; 字符串字面量按列类型解析 (数字字符串转数值, 其余原样)。
- func parseLiteralAsType(fi *types.FieldInfo, s string) (interface{}, error) {
- switch fi.FieldType {
- case types.FieldType_Float64, types.FieldType_Float32:
- if f, err := strconv.ParseFloat(s, 64); err == nil {
- return f, nil
- }
- case types.FieldType_Uint64:
- // 整数优先; 兼容旧版 DATETIME 建的 Uint64 列: 时间文本/毫秒串按毫秒解释。
- if n, err := strconv.ParseInt(s, 10, 64); err == nil {
- return n, nil
- }
- if f, ferr := strconv.ParseFloat(s, 64); ferr == nil {
- return int64(f), nil
- }
- if ms, terr := parseDateTimeText(s); terr == nil {
- return ms, nil
- }
- case types.FieldType_Int64,
- types.FieldType_Uint32, types.FieldType_Int32,
- types.FieldType_Uint16, types.FieldType_Int16,
- types.FieldType_Uint8, types.FieldType_Int8,
- types.FieldType_Year:
- if n, err := strconv.ParseInt(s, 10, 64); err == nil {
- return n, nil
- }
- if f, ferr := strconv.ParseFloat(s, 64); ferr == nil {
- return int64(f), nil
- }
- case types.FieldType_Bool:
- if b, err := strconv.ParseBool(s); err == nil {
- return b, nil
- }
- case types.FieldType_Decimal:
- return types.MustParseDecimal(s), nil
- case types.FieldType_DateTime:
- return parseDateTimeLiteral(s)
- default:
- // 字符串/动态列: 原样
- return s, nil
- }
- return nil, fmt.Errorf("字面量 %q 无法解释为列 %q 的类型 %d", s, fi.FieldName, fi.FieldType)
- }
- // =============================================================================
- // 引擎值 → driver.Value
- // =============================================================================
- // blobReaderOf 绑定某张表的 BlobRef 物化入口: 行内 8B 链引用 → 内容字节。
- //
- // compress 固定 CompressNone, 与写入侧严格一致 —— 引擎写路径 (Table.blobFieldRef)
- // 把驱动/migrate 写入的 []byte / string 内容按 CompressNone 落链 (FieldInfo 不持久化
- // 压缩标志, 压缩只是 Blob 载体 types.Blob 的运行时参数; 库内两处读回 —
- // DB.readBlobBytes 与 migrate 的 VRTX.Scan —— 都是这个取值)。空引用 (空内容, 非
- // NULL —— NULL 走行内位图、根本不会以 BlobRef 形态出现) 归一成空切片, Scan 到
- // []byte 得到 len==0 而不是 nil。
- func blobReaderOf(tbl *core.Table) func(types.BlobRef) ([]byte, error) {
- return func(ref types.BlobRef) ([]byte, error) {
- data, err := tbl.BlobRead(ref, types.CompressNone)
- if err != nil {
- return nil, err
- }
- if data == nil {
- return []byte{}, nil
- }
- return data, nil
- }
- }
- func fieldValueToDriver(v interface{}) (driver.Value, error) {
- switch t := v.(type) {
- case nil:
- return nil, nil
- case int64:
- return t, nil
- case int:
- return int64(t), nil
- case uint64:
- if t > math.MaxInt64 {
- return nil, fmt.Errorf("uint64 值 %d 超出 database/sql 的 int64 表示范围", t)
- }
- return int64(t), nil
- case uint32:
- return int64(t), nil
- case uint16:
- return int64(t), nil
- case uint8:
- return int64(t), nil
- case float64:
- return t, nil
- case float32:
- return float64(t), nil
- case bool:
- return t, nil
- case string:
- return []byte(t), nil // 字符串统一以 []byte 出 (database/sql Scan 兼容)
- case []byte:
- return t, nil
- case time.Time:
- return t, nil
- case types.BlobRef:
- // Blob 列行内只有 8B 链引用; 结果集物化由持有表句柄的路径经 blobReaderOf
- // 解引用 (流式 rows.start / JOIN scanJoinTable, 见 driver.go / join.go) ——
- // 这一层没有读取入口。仍带着引用走到这里 (聚合 MIN/MAX 命中 blob 列等)
- // 只能明确报错, 不能静默吐出引用结构或 fmt.Sprint 的杂串。
- return nil, fmt.Errorf("BLOB 列的值 (引用 %v) 没有读取入口, 聚合函数不支持 BLOB 列", t)
- default:
- // Decimal / UUID 等其余类型: 文本化出 (Scan 到 string 可用)
- return []byte(fmt.Sprint(v)), nil
- }
- }
- // fieldValueToDriverTyped 同 fieldValueToDriver, 但按列类型做语义转换:
- // DateTime 列把 uint64 毫秒还原成 time.Time —— 与写入 time.Time 对称。
- func fieldValueToDriverTyped(ft types.FieldType, v interface{}) (driver.Value, error) {
- if ft == types.FieldType_DateTime {
- switch x := v.(type) {
- case nil:
- return nil, nil
- case time.Time:
- return x, nil
- case uint64:
- return millisToTime(x), nil
- case int64:
- if x < 0 {
- return nil, fmt.Errorf("时间戳不能为负: %d", x)
- }
- return millisToTime(uint64(x)), nil
- }
- return nil, fmt.Errorf("无法把 %T 作为日期时间返回", v)
- }
- return fieldValueToDriver(v)
- }
- // =============================================================================
- // SQL 类型名 → types.FieldInfo (CREATE TABLE)
- // =============================================================================
- // sqlTypeToFieldInfo 映射 ORM 常见列类型。未覆盖的类型返回明确错误。
- func sqlTypeToFieldInfo(c colDef) (types.FieldInfo, error) {
- fi := types.FieldInfo{FieldName: []byte(c.name)}
- if !c.notNull {
- fi.Flag |= types.FlagNullable // SQL 默认可空
- }
- name := c.typeName
- switch name {
- case "BIGINT", "INT8":
- fi.FieldType = types.FieldType_Int64
- case "INT", "INTEGER", "INT4", "MEDIUMINT":
- fi.FieldType = types.FieldType_Int32
- case "SMALLINT", "INT2":
- fi.FieldType = types.FieldType_Int16
- case "TINYINT", "INT1":
- fi.FieldType = types.FieldType_Int8
- case "BIGINT UNSIGNED":
- fi.FieldType = types.FieldType_Uint64
- case "INT UNSIGNED", "INTEGER UNSIGNED":
- fi.FieldType = types.FieldType_Uint32
- case "DOUBLE", "DOUBLE PRECISION", "REAL":
- fi.FieldType = types.FieldType_Float64
- case "FLOAT":
- fi.FieldType = types.FieldType_Float32
- case "BOOL", "BOOLEAN":
- fi.FieldType = types.FieldType_Bool
- case "VARCHAR", "CHAR", "CHARACTER":
- w := 32
- if c.arg > 0 {
- w = c.arg
- }
- if w > 2048 {
- return fi, fmt.Errorf("VARCHAR(%d) 超过引擎定长字符串上限 2048, 请用 TEXT (动态列)", w)
- }
- fi.FieldType = types.FieldType_String
- fi.Flag |= types.WidthCodeFor(w)
- case "TEXT", "LONGTEXT", "MEDIUMTEXT":
- fi.FieldType = types.FieldType_Dynamicbyte
- case "BLOB", "LONGBLOB", "MEDIUMBLOB", "BINARY", "VARBINARY":
- fi.FieldType = types.FieldType_Blob
- case "DECIMAL", "NUMERIC":
- fi.FieldType = types.FieldType_Decimal
- if c.arg >= 0 {
- fi.Flag |= byte(c.arg) // DECIMAL(p,s) 的 s 落 Flag 低 5 位
- }
- case "DATETIME", "TIMESTAMP":
- fi.FieldType = types.FieldType_DateTime // 行内 8B 毫秒, 结果路径还原 time.Time
- case "UUID":
- fi.FieldType = types.FieldType_UUID
- default:
- return fi, fmt.Errorf("不支持的列类型 %q", c.typeName)
- }
- return fi, nil
- }
- // =============================================================================
- // DateTime: driver 值 / 文本 → uint64 毫秒
- // =============================================================================
- // dateTimeLiteralLayouts parseDateTimeLiteral 尝试的时间文本布局 (按顺序;
- // 无时区者按本地时区解释)。
- var dateTimeLiteralLayouts = []string{
- "2006-01-02 15:04:05.999999999",
- "2006-01-02 15:04:05",
- "2006-01-02T15:04:05.999999999",
- "2006-01-02",
- }
- // parseDateTimeLiteral 解析 SQL 文本里的日期时间字面量。GORM 会把 time.Time
- // 族字段的值【内联成 SQL 字符串字面量】而不是 ? 参数, 零值 gorm.DeletedAt
- // 更是被序列化成 "{0001-01-01 00:00:00 +0000 UTC false}" 这样的垃圾串。
- //
- // 约定:
- // - 剥掉首尾空白与 {} 包裹;
- // - 以 "0001-01-01" 开头 = 零时间, 返回 (nil, nil) → 调用方按 NULL 处理
- // (GORM 软删除的未删除态正是零 DeletedAt ⇔ deleted_at IS NULL);
- // - 其余按 dateTimeLiteralLayouts 解析, 成功返回 time.Time (引擎编码层
- // 负责 time.Time → uint64 毫秒);
- // - 全部失败返回含原文的明确错误。
- func parseDateTimeLiteral(s string) (interface{}, error) {
- t := strings.TrimSpace(s)
- if len(t) >= 2 && t[0] == '{' && t[len(t)-1] == '}' {
- t = strings.TrimSpace(t[1 : len(t)-1])
- }
- if strings.HasPrefix(t, "0001-01-01") {
- return nil, nil // 零时间 = NULL
- }
- for _, layout := range dateTimeLiteralLayouts {
- if ts, err := time.ParseInLocation(layout, t, time.Local); err == nil {
- return ts, nil
- }
- }
- return nil, fmt.Errorf("无法把 %q 解析为日期时间字面量 (支持 'YYYY-MM-DD HH:MM:SS[.小数]'、'YYYY-MM-DDThh:mm:ss[.小数]'、'YYYY-MM-DD'; 0001-01-01 零时间视为 NULL)", s)
- }
- // timeLayouts 接受的时间文本格式 (按尝试顺序; 无时区者按本地时区解释)。
- var timeLayouts = []string{
- time.RFC3339Nano,
- time.RFC3339,
- "2006-01-02 15:04:05.999999999",
- "2006-01-02 15:04:05.999",
- "2006-01-02 15:04:05",
- "2006-01-02T15:04:05.999999999",
- "2006-01-02T15:04:05",
- "2006-01-02 15:04",
- "2006-01-02",
- }
- // parseTimeFlexible 解析常见时间文本, 成功返回 time.Time。
- func parseTimeFlexible(s string) (time.Time, bool) {
- t := strings.TrimSpace(s)
- for _, layout := range timeLayouts {
- if ts, err := time.ParseInLocation(layout, t, time.Local); err == nil {
- return ts, true
- }
- }
- return time.Time{}, false
- }
- // parseDateTimeText 把时间文本或纯毫秒数字解析成引擎 uint64 毫秒。
- func parseDateTimeText(s string) (interface{}, error) {
- t := strings.TrimSpace(s)
- if ts, ok := parseTimeFlexible(t); ok {
- return uint64(timeToMillis(ts)), nil
- }
- if n, err := strconv.ParseInt(t, 10, 64); err == nil {
- if n < 0 {
- return nil, fmt.Errorf("时间戳不能为负: %d", n)
- }
- return uint64(n), nil
- }
- return nil, fmt.Errorf("无法把 %q 解析为日期时间 (支持 'YYYY-MM-DD HH:MM:SS'、RFC3339 或毫秒数字)", s)
- }
- // toDateTimeMillis 把 driver 参数转成引擎 uint64 毫秒 (time.Time/数字/文本)。
- func toDateTimeMillis(v driver.Value) (interface{}, error) {
- if v == nil {
- return nil, nil
- }
- switch t := v.(type) {
- case time.Time:
- return uint64(timeToMillis(t)), nil
- case int64:
- if t < 0 {
- return nil, fmt.Errorf("时间戳不能为负: %d", t)
- }
- return uint64(t), nil
- case float64:
- if t < 0 {
- return nil, fmt.Errorf("时间戳不能为负: %v", t)
- }
- return uint64(t), nil
- case string:
- return parseDateTimeText(t)
- case []byte:
- return parseDateTimeText(string(t))
- }
- return nil, fmt.Errorf("无法把 %T 转成日期时间", v)
- }
|