Files
MyGoNavi/internal/db/scan_rows.go
Syngnat 938bc53966 🐛 fix(mysql): 修复 DATE 字段显示为 datetime
- 查询扫描链路透传数据库方言,区分 MySQL 与 Oracle DATE 语义
- MySQL/MariaDB/自定义 mysql 驱动的 DATE/NEWDATE 只展示 YYYY-MM-DD
- 保留 DATETIME/TIMESTAMP 和 Oracle DATE 的时间信息
- 补充值规整与扫描链路回归测试
Close #565
2026-06-16 09:25:16 +08:00

134 lines
3.1 KiB
Go

package db
import (
"database/sql"
"fmt"
"GoNavi-Wails/internal/connection"
)
func scanRows(rows *sql.Rows) ([]map[string]interface{}, []string, error) {
return scanRowsForDialect(rows, "")
}
func scanRowsForDialect(rows *sql.Rows, dialect string) ([]map[string]interface{}, []string, error) {
columns, err := rows.Columns()
if err != nil {
return nil, nil, err
}
columns = ensureUniqueQueryColumnNames(columns)
colTypes, err := rows.ColumnTypes()
if err != nil || len(colTypes) != len(columns) {
colTypes = nil
}
resultData := make([]map[string]interface{}, 0)
for rows.Next() {
values := make([]interface{}, len(columns))
valuePtrs := make([]interface{}, len(columns))
for i := range columns {
valuePtrs[i] = &values[i]
}
if err := rows.Scan(valuePtrs...); err != nil {
continue
}
entry := make(map[string]interface{}, len(columns))
for i, col := range columns {
dbTypeName := ""
if colTypes != nil && i < len(colTypes) && colTypes[i] != nil {
dbTypeName = colTypes[i].DatabaseTypeName()
}
entry[col] = normalizeQueryValueWithDBTypeAndDialect(values[i], dbTypeName, dialect)
}
resultData = append(resultData, entry)
}
if err := rows.Err(); err != nil {
return resultData, columns, err
}
return resultData, columns, nil
}
func ensureUniqueQueryColumnNames(columns []string) []string {
if len(columns) == 0 {
return columns
}
uniqueColumns := make([]string, len(columns))
taken := make(map[string]struct{}, len(columns))
nextSuffix := make(map[string]int, len(columns))
for idx, column := range columns {
base := column
if base == "" {
base = fmt.Sprintf("column_%d", idx+1)
}
candidate := base
if _, exists := taken[candidate]; exists {
suffix := nextSuffix[base]
if suffix < 2 {
suffix = 2
}
for {
candidate = fmt.Sprintf("%s_%d", base, suffix)
if _, exists := taken[candidate]; !exists {
break
}
suffix++
}
nextSuffix[base] = suffix + 1
} else {
nextSuffix[base] = 2
}
uniqueColumns[idx] = candidate
taken[candidate] = struct{}{}
}
return uniqueColumns
}
// scanMultiRows 遍历 sql.Rows 中的所有结果集,将每个结果集作为 ResultSetData 返回。
// 利用 rows.NextResultSet() 支持一次 query 返回多个结果集的场景。
func scanMultiRows(rows *sql.Rows) ([]connection.ResultSetData, error) {
return scanMultiRowsForDialect(rows, "")
}
func scanMultiRowsForDialect(rows *sql.Rows, dialect string) ([]connection.ResultSetData, error) {
var results []connection.ResultSetData
for {
data, cols, err := scanRowsForDialect(rows, dialect)
if err != nil {
return results, err
}
if data == nil {
data = make([]map[string]interface{}, 0)
}
if cols == nil {
cols = []string{}
}
results = append(results, connection.ResultSetData{
Rows: data,
Columns: cols,
})
if !rows.NextResultSet() {
break
}
}
if len(results) == 0 {
results = []connection.ResultSetData{{
Rows: make([]map[string]interface{}, 0),
Columns: []string{},
}}
}
if err := rows.Err(); err != nil {
return results, err
}
return results, nil
}