mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-07 11:01:33 +08:00
♻️ refactor(database/ssh): SSH隧道架构重构与多数据源适配
- 架构升级:从driver专属拨号器改为通用本地端口转发模式
- 并发安全:sync.Once保护Close操作,RWMutex保护状态访问,双向errc等待
- 连接池化:GetOrCreateLocalForwarder/GetOrCreateSSHClient实现缓存复用
- SQL安全:kingbase_impl.go引入esc函数,防止双引号注入(""ldf_server""问题)
- Schema动态化:三级fallback(schema.table解析→dbName参数→current_schema())
- 代码复用:scanRows统一行扫描逻辑,normalizeQueryValueWithDBType增强类型处理
Close #40
This commit is contained in:
@@ -2,6 +2,8 @@ package db
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
@@ -9,13 +11,17 @@ import (
|
||||
// normalizeQueryValue normalizes driver-returned values for UI/JSON transport.
|
||||
// 当前主要处理 []byte:如果是可读文本则转为 string,否则转为十六进制字符串,避免前端出现“空白值”。
|
||||
func normalizeQueryValue(v interface{}) interface{} {
|
||||
return normalizeQueryValueWithDBType(v, "")
|
||||
}
|
||||
|
||||
func normalizeQueryValueWithDBType(v interface{}, databaseTypeName string) interface{} {
|
||||
if b, ok := v.([]byte); ok {
|
||||
return bytesToReadableString(b)
|
||||
return bytesToDisplayValue(b, databaseTypeName)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func bytesToReadableString(b []byte) interface{} {
|
||||
func bytesToDisplayValue(b []byte, databaseTypeName string) interface{} {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -23,6 +29,18 @@ func bytesToReadableString(b []byte) interface{} {
|
||||
return ""
|
||||
}
|
||||
|
||||
dbType := strings.ToUpper(strings.TrimSpace(databaseTypeName))
|
||||
if isBitLikeDBType(dbType) {
|
||||
if u, ok := bytesToUint64(b); ok {
|
||||
// JS number precision is limited; keep large bitmasks as string.
|
||||
const maxSafeInteger = 9007199254740991 // 2^53 - 1
|
||||
if u <= maxSafeInteger {
|
||||
return int64(u)
|
||||
}
|
||||
return fmt.Sprintf("%d", u)
|
||||
}
|
||||
}
|
||||
|
||||
if utf8.Valid(b) {
|
||||
s := string(b)
|
||||
if isMostlyPrintable(s) {
|
||||
@@ -30,9 +48,47 @@ func bytesToReadableString(b []byte) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: some drivers return BIT(1) as []byte{0} / []byte{1} without type info.
|
||||
if dbType == "" && len(b) == 1 && (b[0] == 0 || b[0] == 1) {
|
||||
return int64(b[0])
|
||||
}
|
||||
|
||||
return bytesToReadableString(b)
|
||||
}
|
||||
|
||||
func bytesToReadableString(b []byte) interface{} {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return ""
|
||||
}
|
||||
return "0x" + hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
func isBitLikeDBType(typeName string) bool {
|
||||
if typeName == "" {
|
||||
return false
|
||||
}
|
||||
switch typeName {
|
||||
case "BIT", "VARBIT":
|
||||
return true
|
||||
default:
|
||||
}
|
||||
return strings.HasPrefix(typeName, "BIT")
|
||||
}
|
||||
|
||||
func bytesToUint64(b []byte) (uint64, bool) {
|
||||
if len(b) == 0 || len(b) > 8 {
|
||||
return 0, false
|
||||
}
|
||||
var u uint64
|
||||
for _, v := range b {
|
||||
u = (u << 8) | uint64(v)
|
||||
}
|
||||
return u, true
|
||||
}
|
||||
|
||||
func isMostlyPrintable(s string) bool {
|
||||
if s == "" {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user