feat: 完善扫描器、exec 及 schema 相关功能
- exec/scanner: 用 *interface{} 替换 **json.RawMessage 扫描目标,兼容 DuckDB 返回 map[string]interface{} 的场景;新增 toJSONRawMessage 转换函数
- exec/scanner: ScanVal 支持结构体指针,通过 JSON 中间层转换(DuckDB STRUCT 列)
- exec/scanner: 将 *sql.RawBytes 和 *[]byte 的处理从 ScanValContext 移入 scanner.ScanVal
- exec/query_executor: 简化 ScanValContext,移除私有 scan 方法
- exec: 补充 scanner 级别 ScanVal 测试用例
- internal/util/reflect: 重写 SafeSetVarValue,修复非指针 src 及 nil 指针字段的 panic
- internal/util/column_map: 恢复非匿名带标签结构体字段的展开逻辑
- schema: 新增 vector 列类型支持
- engine: 补充 DuckDB 相关配置
- dialect/sqlite3/vtab: 完善虚拟表适配器
- 各方言测试改用 sqlmock 虚拟连接
This commit is contained in:
+80
-9
@@ -147,9 +147,9 @@ func (s *scanner) ScanStruct(i interface{}) error {
|
||||
|
||||
// 补全未知字段类型
|
||||
if len(cols) != len(cm) {
|
||||
colTypes, err := s.rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
colTypes, ctErr := s.rows.ColumnTypes()
|
||||
if ctErr != nil {
|
||||
return ctErr
|
||||
}
|
||||
for _, t := range colTypes {
|
||||
if _, ok := cm[t.Name()]; !ok {
|
||||
@@ -166,7 +166,6 @@ func (s *scanner) ScanStruct(i interface{}) error {
|
||||
}
|
||||
|
||||
scans, err := createColumnScans(s.columns, s.columnMap)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -177,7 +176,12 @@ func (s *scanner) ScanStruct(i interface{}) error {
|
||||
|
||||
record := map[string]interface{}{}
|
||||
for index, col := range s.columns {
|
||||
record[col] = scans[index]
|
||||
if pi, ok := scans[index].(*interface{}); ok {
|
||||
raw := toJSONRawMessage(*pi)
|
||||
record[col] = &raw
|
||||
} else {
|
||||
record[col] = scans[index]
|
||||
}
|
||||
}
|
||||
|
||||
util.AssignStructVals(i, record, s.columnMap)
|
||||
@@ -198,10 +202,59 @@ func (s *scanner) ScanStructs(i interface{}) error {
|
||||
|
||||
// ScanVal will scan the current row and column into i.
|
||||
func (s *scanner) ScanVal(i interface{}) error {
|
||||
if err := s.rows.Scan(i); err != nil {
|
||||
return err
|
||||
switch v := i.(type) {
|
||||
case *sql.RawBytes:
|
||||
// 零拷贝扫描,rows.Close 前立即拷贝防止驱动回收缓冲区
|
||||
if err := s.rows.Scan(v); err != nil {
|
||||
return err
|
||||
}
|
||||
buf := make(sql.RawBytes, len(*v))
|
||||
copy(buf, *v)
|
||||
*v = buf
|
||||
case *[]byte:
|
||||
// 先扫描到 interface{},驱动可能返回 []byte/string/map 等任意类型
|
||||
var raw interface{}
|
||||
if err := s.rows.Scan(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
switch rv := raw.(type) {
|
||||
case []byte:
|
||||
*v = append([]byte(nil), rv...)
|
||||
case sql.RawBytes:
|
||||
*v = append([]byte(nil), []byte(rv)...)
|
||||
case string:
|
||||
*v = []byte(rv)
|
||||
default:
|
||||
if raw != nil {
|
||||
var err error
|
||||
*v, err = json.Marshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
// 指针-结构体且未实现 sql.Scanner:通过 JSON 中间层转换
|
||||
if rv := reflect.ValueOf(i); rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct {
|
||||
if _, ok := i.(sql.Scanner); !ok {
|
||||
var raw interface{}
|
||||
if err := s.rows.Scan(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if raw == nil {
|
||||
return s.Err()
|
||||
}
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, i)
|
||||
}
|
||||
}
|
||||
if err := s.rows.Scan(i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return s.Err()
|
||||
}
|
||||
|
||||
@@ -260,6 +313,23 @@ func checkScanValsTarget(i interface{}) (reflect.Value, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func toJSONRawMessage(v interface{}) *json.RawMessage {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var raw json.RawMessage
|
||||
switch s := v.(type) {
|
||||
case []byte:
|
||||
raw = append(json.RawMessage(nil), s...)
|
||||
case string:
|
||||
raw = json.RawMessage(s)
|
||||
default:
|
||||
b, _ := json.Marshal(v)
|
||||
raw = b
|
||||
}
|
||||
return &raw
|
||||
}
|
||||
|
||||
func createColumnScans(cols []string, cm util.ColumnMap) (scans []interface{}, err error) {
|
||||
scans = make([]interface{}, 0, len(cols))
|
||||
|
||||
@@ -278,7 +348,8 @@ func createColumnScans(cols []string, cm util.ColumnMap) (scans []interface{}, e
|
||||
reflect.Bool:
|
||||
scans = append(scans, reflect.New(reflect.PointerTo(data.GoType)).Interface())
|
||||
case reflect.Map, reflect.Slice, reflect.Struct:
|
||||
scans = append(scans, reflect.New(reflect.PointerTo(reflect.TypeOf(json.RawMessage{}))).Interface())
|
||||
// 使用 *interface{} 接受任意驱动值(兼容 DuckDB 返回 map[string]interface{})
|
||||
scans = append(scans, new(interface{}))
|
||||
default:
|
||||
scans = append(scans, reflect.New(data.GoType).Interface())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user