Files
MyGoNavi/internal/db/connection_params.go
Syngnat c92959f3e8 feat(connection): 支持多数据源额外连接参数配置
- 前端连接表单新增额外连接参数入口,支持 URI query 格式录入与解析回填
- MySQL 兼容驱动支持 JDBC 常见参数映射,修复 UTF-8 字符集与 serverTimezone 兼容问题
- 扩展 Oracle、PostgreSQL 兼容、SQL Server、ClickHouse、MongoDB、达梦、TDengine 参数合并
- 按不同驱动通道处理 DSN、URI、Options 与 Settings,避免统一透传导致连接异常
- 修复编辑已保存连接时解析无认证 URI 会清空已有账号密码的问题
- 补充连接参数透传、缓存隔离、DSN 合并与 URI 回填回归测试
2026-04-30 10:57:52 +08:00

118 lines
2.7 KiB
Go

package db
import (
"net/url"
"sort"
"strings"
"GoNavi-Wails/internal/connection"
)
func parseConnectionURI(raw string, allowedSchemes ...string) (*url.URL, bool) {
text := strings.TrimSpace(raw)
if text == "" {
return nil, false
}
if strings.HasPrefix(strings.ToLower(text), "jdbc:") {
text = strings.TrimSpace(text[len("jdbc:"):])
}
parsed, err := url.Parse(text)
if err != nil {
return nil, false
}
scheme := strings.ToLower(strings.TrimSpace(parsed.Scheme))
for _, allowed := range allowedSchemes {
if scheme == strings.ToLower(strings.TrimSpace(allowed)) {
return parsed, true
}
}
return nil, false
}
func connectionParamsFromText(raw string) url.Values {
text := strings.TrimSpace(raw)
if text == "" {
return nil
}
if queryIndex := strings.Index(text, "?"); queryIndex >= 0 {
text = text[queryIndex+1:]
}
if hashIndex := strings.Index(text, "#"); hashIndex >= 0 {
text = text[:hashIndex]
}
text = strings.TrimLeft(strings.TrimSpace(text), "?&")
if text == "" {
return nil
}
values, err := url.ParseQuery(text)
if err != nil {
return nil
}
return values
}
func connectionParamsFromURI(raw string, allowedSchemes ...string) url.Values {
parsed, ok := parseConnectionURI(raw, allowedSchemes...)
if !ok {
return nil
}
return parsed.Query()
}
func mergeConnectionParamValues(params url.Values, values url.Values) {
if len(values) == 0 {
return
}
keys := make([]string, 0, len(values))
for key := range values {
if strings.TrimSpace(key) != "" {
keys = append(keys, key)
}
}
sort.Strings(keys)
for _, key := range keys {
for _, value := range values[key] {
params.Set(key, value)
}
}
}
func mergeConnectionParamsFromConfig(params url.Values, config connection.ConnectionConfig, allowedSchemes ...string) {
mergeConnectionParamValues(params, connectionParamsFromURI(config.URI, allowedSchemes...))
mergeConnectionParamValues(params, connectionParamsFromText(config.ConnectionParams))
}
func mergeConnectionParamsIntoRawURI(raw string, connectionParams string, allowedSchemes ...string) string {
text := strings.TrimSpace(raw)
if text == "" {
return text
}
parsed, ok := parseConnectionURI(text, allowedSchemes...)
if !ok {
return text
}
params := parsed.Query()
mergeConnectionParamValues(params, connectionParamsFromText(connectionParams))
parsed.RawQuery = params.Encode()
return parsed.String()
}
func isSafeConnectionParamKey(key string) bool {
text := strings.TrimSpace(key)
if text == "" {
return false
}
for _, r := range text {
if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
continue
}
switch r {
case '_', '-', '.', ' ':
continue
default:
return false
}
}
return true
}