mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-29 04:41:22 +08:00
♻️ refactor(全局): 统一错误消息中文化,补充 godoc 与测试,修复横向滚动和 Vite 代理
错误消息中文化: - 19 个驱动实现文件中 connection not open / table name required 等英文消息替换为中文 - methods_file.go / methods_db.go / methods_driver.go 英文消息中文化 - 前端 App/Sidebar/DataGrid/ConnectionModal/DriverManagerModal 同步替换 "Cancelled" → "已取消" 文档与测试: - database.go Database/BatchApplier 接口、types.go 12 个类型、driver_support.go 导出函数补充中文 godoc - 新增 logger_test.go(ErrorChain 5 个用例)和 methods_db_conn_test.go(连接管理 7 个用例) Bug 修复: - DataGrid: 将 liveTargets 空检查移至非虚拟路径,修复外部横向滚动条拖动时内容不跟随 - vite.config.ts: server.host 指定 127.0.0.1,修复 IPv6 回环被拦截导致 Wails 代理 502 基础设施: - .gitignore 新增 .gemini/ 规则,tmpclaude-* 改为 **/tmpclaude-* 覆盖子目录
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,7 +19,8 @@ GoNavi-Wails.exe
|
||||
.ace-tool/
|
||||
.superpowers/
|
||||
.claude/
|
||||
tmpclaude-*
|
||||
.gemini/
|
||||
**/tmpclaude-*
|
||||
|
||||
CLAUDE.md
|
||||
**/CLAUDE.md
|
||||
|
||||
@@ -1 +1 @@
|
||||
5b8157374dae5f9340e31b2d0bd2c00e
|
||||
d0f9366af59a6367ad3c7e2d4185ead4
|
||||
@@ -953,7 +953,7 @@ function App() {
|
||||
} catch (e) {
|
||||
void message.error("解析 JSON 失败");
|
||||
}
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
void message.error("导入失败: " + res.message);
|
||||
}
|
||||
};
|
||||
@@ -966,7 +966,7 @@ function App() {
|
||||
const res = await (window as any).go.app.App.ExportData(connections, ['id','name','config','includeDatabases','includeRedisDatabases'], "connections", "json");
|
||||
if (res.success) {
|
||||
void message.success("导出成功");
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
void message.error("导出失败: " + res.message);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1013,7 +1013,7 @@ const ConnectionModal: React.FC<{
|
||||
if (selectedPath) {
|
||||
form.setFieldValue('sshKeyPath', selectedPath);
|
||||
}
|
||||
} else if (res?.message !== 'Cancelled') {
|
||||
} else if (res?.message !== '已取消') {
|
||||
message.error(`选择私钥文件失败: ${res?.message || '未知错误'}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -1037,7 +1037,7 @@ const ConnectionModal: React.FC<{
|
||||
if (selectedPath) {
|
||||
form.setFieldValue('host', normalizeFileDbPath(selectedPath));
|
||||
}
|
||||
} else if (res?.message !== 'Cancelled') {
|
||||
} else if (res?.message !== '已取消') {
|
||||
message.error(`选择数据库文件失败: ${res?.message || '未知错误'}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -1086,7 +1086,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
const res = await ExportData(cleanRows, displayColumnNames, tableName || 'export', format);
|
||||
if (res.success) {
|
||||
void message.success("导出成功");
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
void message.error("导出失败: " + res.message);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -2829,7 +2829,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
const res = await ExportQuery(config as any, dbName || '', sql, defaultName || 'export', format);
|
||||
if (res.success) {
|
||||
void message.success("导出成功");
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
void message.error("导出失败: " + res.message);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -2947,7 +2947,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
const res = await ExportTable(config as any, dbName || '', tableName, format);
|
||||
if (res.success) {
|
||||
void message.success("导出成功");
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
void message.error("导出失败: " + res.message);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -3023,7 +3023,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
if (res.success && res.data && res.data.filePath) {
|
||||
setImportFilePath(res.data.filePath);
|
||||
setImportPreviewVisible(true);
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
void message.error("选择文件失败: " + res.message);
|
||||
}
|
||||
};
|
||||
@@ -3371,11 +3371,6 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const liveTargets = tableScrollTargetsRef.current;
|
||||
if (liveTargets.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.abs(lastExternalScrollLeftRef.current - externalScroll.scrollLeft) < 1) {
|
||||
return;
|
||||
}
|
||||
@@ -3383,6 +3378,9 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
|
||||
horizontalSyncSourceRef.current = 'external';
|
||||
const tableContainer = tableContainerRef.current;
|
||||
// 虚拟表格路径:直接操作 DOM 的 marginLeft / scrollLeft,不依赖 liveTargets。
|
||||
// 将此分支提前到 liveTargets 检查之前,避免 targets 在数据加载时暂时为空
|
||||
// 导致虚拟表格的横向滚动同步被永久阻塞。
|
||||
if (enableVirtual && tableContainer instanceof HTMLElement) {
|
||||
if (applyVirtualHorizontalOffset(tableContainer, externalScroll.scrollLeft)) {
|
||||
lastTableScrollLeftRef.current = externalScroll.scrollLeft;
|
||||
@@ -3390,6 +3388,12 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
horizontalSyncSourceRef.current = '';
|
||||
return;
|
||||
}
|
||||
// 非虚拟表格路径:依赖 liveTargets 进行 scrollLeft 同步
|
||||
const liveTargets = tableScrollTargetsRef.current;
|
||||
if (liveTargets.length === 0) {
|
||||
horizontalSyncSourceRef.current = '';
|
||||
return;
|
||||
}
|
||||
liveTargets.forEach((target) => {
|
||||
if (target.scrollWidth <= target.clientWidth + 1) {
|
||||
return;
|
||||
|
||||
@@ -847,7 +847,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
|
||||
const installDriverFromLocalFile = useCallback(async (row: DriverStatusRow) => {
|
||||
const fileRes = await SelectDriverPackageFile(downloadDir);
|
||||
if (!fileRes?.success) {
|
||||
if (String(fileRes?.message || '') !== 'Cancelled') {
|
||||
if (String(fileRes?.message || '') !== '已取消') {
|
||||
message.error(fileRes?.message || '选择本地驱动包文件失败');
|
||||
}
|
||||
return;
|
||||
@@ -863,7 +863,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
|
||||
const installDriversFromDirectory = useCallback(async () => {
|
||||
const directoryRes = await SelectDriverPackageDirectory(downloadDir);
|
||||
if (!directoryRes?.success) {
|
||||
if (String(directoryRes?.message || '') !== 'Cancelled') {
|
||||
if (String(directoryRes?.message || '') !== '已取消') {
|
||||
message.error(directoryRes?.message || '选择本地驱动包目录失败');
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -1561,7 +1561,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
|
||||
hide();
|
||||
if (res.success) {
|
||||
message.success('导出成功');
|
||||
} else if (res.message !== 'Cancelled') {
|
||||
} else if (res.message !== '已取消') {
|
||||
message.error('导出失败: ' + res.message);
|
||||
}
|
||||
};
|
||||
@@ -1584,7 +1584,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
|
||||
hide();
|
||||
if (res.success) {
|
||||
message.success('导出成功');
|
||||
} else if (res.message !== 'Cancelled') {
|
||||
} else if (res.message !== '已取消') {
|
||||
message.error('导出失败: ' + res.message);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -1611,7 +1611,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
|
||||
hide();
|
||||
if (res.success) {
|
||||
message.success('导出成功');
|
||||
} else if (res.message !== 'Cancelled') {
|
||||
} else if (res.message !== '已取消') {
|
||||
message.error('导出失败: ' + res.message);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -1800,7 +1800,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
|
||||
} else {
|
||||
message.success('导出成功');
|
||||
}
|
||||
} else if (res.message !== 'Cancelled') {
|
||||
} else if (res.message !== '已取消') {
|
||||
message.error('导出失败: ' + res.message);
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -1940,7 +1940,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
|
||||
hide();
|
||||
if (res.success) {
|
||||
message.success(`${db.dbName} 导出成功`);
|
||||
} else if (res.message !== 'Cancelled') {
|
||||
} else if (res.message !== '已取消') {
|
||||
message.error(`${db.dbName} 导出失败: ` + res.message);
|
||||
break;
|
||||
} else {
|
||||
@@ -1981,7 +1981,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
|
||||
dbName: dbName,
|
||||
query: sqlContent
|
||||
});
|
||||
} else if (res.message !== "Cancelled") {
|
||||
} else if (res.message !== "已取消") {
|
||||
message.error("读取文件失败: " + res.message);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react'
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: '127.0.0.1',
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
},
|
||||
|
||||
@@ -116,7 +116,7 @@ func (a *App) CreateDatabase(config connection.ConnectionConfig, dbName string)
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
|
||||
return connection.QueryResult{Success: true, Message: "Database created successfully"}
|
||||
return connection.QueryResult{Success: true, Message: "数据库创建成功"}
|
||||
}
|
||||
|
||||
func resolveDDLDBType(config connection.ConnectionConfig) string {
|
||||
|
||||
112
internal/app/methods_db_conn_test.go
Normal file
112
internal/app/methods_db_conn_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"GoNavi-Wails/internal/connection"
|
||||
)
|
||||
|
||||
func TestNormalizeTestConnectionConfig_CapsTimeout(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{Timeout: 60}
|
||||
got := normalizeTestConnectionConfig(cfg)
|
||||
if got.Timeout != testConnectionTimeoutUpperBoundSeconds {
|
||||
t.Fatalf("timeout 应被限制为 %d, got=%d", testConnectionTimeoutUpperBoundSeconds, got.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeTestConnectionConfig_KeepSmallTimeout(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{Timeout: 5}
|
||||
got := normalizeTestConnectionConfig(cfg)
|
||||
if got.Timeout != 5 {
|
||||
t.Fatalf("timeout 不应被修改, got=%d", got.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeTestConnectionConfig_ZeroTimeout(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{Timeout: 0}
|
||||
got := normalizeTestConnectionConfig(cfg)
|
||||
if got.Timeout != testConnectionTimeoutUpperBoundSeconds {
|
||||
t.Fatalf("零值 timeout 应被修正, got=%d", got.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatConnSummary_BasicMySQL(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{
|
||||
Type: "mysql",
|
||||
Host: "127.0.0.1",
|
||||
Port: 3306,
|
||||
User: "root",
|
||||
Database: "test_db",
|
||||
Timeout: 30,
|
||||
}
|
||||
got := formatConnSummary(cfg)
|
||||
for _, want := range []string{"类型=mysql", "127.0.0.1:3306", "test_db", "root"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("formatConnSummary 应包含 %q, got=%q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatConnSummary_SQLitePath(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{
|
||||
Type: "sqlite",
|
||||
Host: "/data/test.db",
|
||||
}
|
||||
got := formatConnSummary(cfg)
|
||||
if !strings.Contains(got, "类型=sqlite") {
|
||||
t.Fatalf("formatConnSummary 缺少类型, got=%q", got)
|
||||
}
|
||||
if !strings.Contains(got, "/data/test.db") {
|
||||
t.Fatalf("formatConnSummary 缺少路径, got=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatConnSummary_SSH(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{
|
||||
Type: "mysql",
|
||||
Host: "db.internal",
|
||||
Port: 3306,
|
||||
User: "app",
|
||||
UseSSH: true,
|
||||
SSH: connection.SSHConfig{
|
||||
Host: "jump.server",
|
||||
Port: 22,
|
||||
User: "admin",
|
||||
},
|
||||
}
|
||||
got := formatConnSummary(cfg)
|
||||
if !strings.Contains(got, "SSH=jump.server:22") {
|
||||
t.Fatalf("formatConnSummary 应包含 SSH 信息, got=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatConnSummary_Proxy(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{
|
||||
Type: "mysql",
|
||||
Host: "db.internal",
|
||||
Port: 3306,
|
||||
UseProxy: true,
|
||||
Proxy: connection.ProxyConfig{
|
||||
Type: "socks5",
|
||||
Host: "proxy.local",
|
||||
Port: 1080,
|
||||
},
|
||||
}
|
||||
got := formatConnSummary(cfg)
|
||||
if !strings.Contains(got, "代理=socks5://proxy.local:1080") {
|
||||
t.Fatalf("formatConnSummary 应包含代理信息, got=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatConnSummary_DefaultTimeout(t *testing.T) {
|
||||
cfg := connection.ConnectionConfig{
|
||||
Type: "mysql",
|
||||
Host: "localhost",
|
||||
Port: 3306,
|
||||
}
|
||||
got := formatConnSummary(cfg)
|
||||
if !strings.Contains(got, "超时=30s") {
|
||||
t.Fatalf("formatConnSummary 默认超时应为30s, got=%q", got)
|
||||
}
|
||||
}
|
||||
@@ -353,7 +353,7 @@ func (a *App) SelectDriverDownloadDirectory(currentDir string) connection.QueryR
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if strings.TrimSpace(selection) == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
resolved, err := resolveDriverDownloadDirectory(selection)
|
||||
@@ -392,7 +392,7 @@ func (a *App) SelectDriverPackageFile(currentPath string) connection.QueryResult
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if strings.TrimSpace(selection) == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
if abs, err := filepath.Abs(selection); err == nil {
|
||||
@@ -423,7 +423,7 @@ func (a *App) SelectDriverPackageDirectory(currentPath string) connection.QueryR
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if strings.TrimSpace(selection) == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
if abs, err := filepath.Abs(selection); err == nil {
|
||||
selection = abs
|
||||
|
||||
@@ -48,7 +48,7 @@ func (a *App) OpenSQLFile() connection.QueryResult {
|
||||
}
|
||||
|
||||
if selection == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(selection)
|
||||
@@ -75,7 +75,7 @@ func (a *App) ImportConfigFile() connection.QueryResult {
|
||||
}
|
||||
|
||||
if selection == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(selection)
|
||||
@@ -120,7 +120,7 @@ func (a *App) SelectSSHKeyFile(currentPath string) connection.QueryResult {
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if strings.TrimSpace(selection) == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
if abs, err := filepath.Abs(selection); err == nil {
|
||||
selection = abs
|
||||
@@ -192,7 +192,7 @@ func (a *App) SelectDatabaseFile(currentPath string, driverType string) connecti
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if strings.TrimSpace(selection) == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
if abs, err := filepath.Abs(selection); err == nil {
|
||||
selection = abs
|
||||
@@ -203,7 +203,7 @@ func (a *App) SelectDatabaseFile(currentPath string, driverType string) connecti
|
||||
// PreviewImportFile 解析导入文件,返回字段列表、总行数、前 5 行预览数据
|
||||
func (a *App) PreviewImportFile(filePath string) connection.QueryResult {
|
||||
if filePath == "" {
|
||||
return connection.QueryResult{Success: false, Message: "File path required"}
|
||||
return connection.QueryResult{Success: false, Message: "文件路径不能为空"}
|
||||
}
|
||||
|
||||
rows, columns, err := parseImportFile(filePath)
|
||||
@@ -243,7 +243,7 @@ func (a *App) ImportData(config connection.ConnectionConfig, dbName, tableName s
|
||||
}
|
||||
|
||||
if selection == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
// 返回文件路径供前端预览
|
||||
@@ -492,7 +492,7 @@ func (a *App) ImportDataWithProgress(config connection.ConnectionConfig, dbName,
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
return connection.QueryResult{Success: true, Message: "No data to import"}
|
||||
return connection.QueryResult{Success: true, Message: "无可导入数据"}
|
||||
}
|
||||
|
||||
runConfig := normalizeRunConfig(config, dbName)
|
||||
@@ -584,7 +584,7 @@ func (a *App) ExportTable(config connection.ConnectionConfig, dbName string, tab
|
||||
})
|
||||
|
||||
if err != nil || filename == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
runConfig := normalizeRunConfig(config, dbName)
|
||||
@@ -616,7 +616,7 @@ func (a *App) ExportTable(config connection.ConnectionConfig, dbName string, tab
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
|
||||
return connection.QueryResult{Success: true, Message: "Export successful"}
|
||||
return connection.QueryResult{Success: true, Message: "导出完成"}
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("SELECT * FROM %s", quoteQualifiedIdentByType(runConfig.Type, tableName))
|
||||
@@ -632,10 +632,10 @@ func (a *App) ExportTable(config connection.ConnectionConfig, dbName string, tab
|
||||
}
|
||||
defer f.Close()
|
||||
if err := writeRowsToFile(f, data, columns, format); err != nil {
|
||||
return connection.QueryResult{Success: false, Message: "Write error: " + err.Error()}
|
||||
return connection.QueryResult{Success: false, Message: "写入失败:" + err.Error()}
|
||||
}
|
||||
|
||||
return connection.QueryResult{Success: true, Message: "Export successful"}
|
||||
return connection.QueryResult{Success: true, Message: "导出完成"}
|
||||
}
|
||||
|
||||
func (a *App) ExportTablesSQL(config connection.ConnectionConfig, dbName string, tableNames []string, includeData bool) connection.QueryResult {
|
||||
@@ -648,7 +648,7 @@ func (a *App) ExportTablesDataSQL(config connection.ConnectionConfig, dbName str
|
||||
|
||||
func (a *App) exportTablesSQL(config connection.ConnectionConfig, dbName string, tableNames []string, includeSchema bool, includeData bool) connection.QueryResult {
|
||||
if !includeSchema && !includeData {
|
||||
return connection.QueryResult{Success: false, Message: "invalid export mode"}
|
||||
return connection.QueryResult{Success: false, Message: "无效的导出模式"}
|
||||
}
|
||||
|
||||
safeDbName := strings.TrimSpace(dbName)
|
||||
@@ -671,7 +671,7 @@ func (a *App) exportTablesSQL(config connection.ConnectionConfig, dbName string,
|
||||
DefaultFilename: defaultFilename,
|
||||
})
|
||||
if err != nil || filename == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
runConfig := normalizeRunConfig(config, dbName)
|
||||
@@ -717,13 +717,13 @@ func (a *App) exportTablesSQL(config connection.ConnectionConfig, dbName string,
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
|
||||
return connection.QueryResult{Success: true, Message: "Export successful"}
|
||||
return connection.QueryResult{Success: true, Message: "导出完成"}
|
||||
}
|
||||
|
||||
func (a *App) ExportDatabaseSQL(config connection.ConnectionConfig, dbName string, includeData bool) connection.QueryResult {
|
||||
safeDbName := strings.TrimSpace(dbName)
|
||||
if safeDbName == "" {
|
||||
return connection.QueryResult{Success: false, Message: "dbName required"}
|
||||
return connection.QueryResult{Success: false, Message: "数据库名称不能为空"}
|
||||
}
|
||||
suffix := "schema"
|
||||
if includeData {
|
||||
@@ -735,7 +735,7 @@ func (a *App) ExportDatabaseSQL(config connection.ConnectionConfig, dbName strin
|
||||
DefaultFilename: fmt.Sprintf("%s_%s.sql", safeDbName, suffix),
|
||||
})
|
||||
if err != nil || filename == "" {
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
|
||||
runConfig := normalizeRunConfig(config, dbName)
|
||||
@@ -772,7 +772,7 @@ func (a *App) ExportDatabaseSQL(config connection.ConnectionConfig, dbName strin
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
|
||||
return connection.QueryResult{Success: true, Message: "Export successful"}
|
||||
return connection.QueryResult{Success: true, Message: "导出完成"}
|
||||
}
|
||||
|
||||
func quoteIdentByType(dbType string, ident string) string {
|
||||
@@ -1471,7 +1471,7 @@ func (a *App) ExportData(data []map[string]interface{}, columns []string, defaul
|
||||
|
||||
if err != nil || filename == "" {
|
||||
logger.Infof("ExportData 已取消或未选择文件:err=%v", err)
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
logger.Infof("ExportData 选定文件:%s", filename)
|
||||
|
||||
@@ -1482,11 +1482,11 @@ func (a *App) ExportData(data []map[string]interface{}, columns []string, defaul
|
||||
defer f.Close()
|
||||
if err := writeRowsToFile(f, data, columns, format); err != nil {
|
||||
logger.Warnf("ExportData 写入失败:file=%s err=%v", filename, err)
|
||||
return connection.QueryResult{Success: false, Message: "Write error: " + err.Error()}
|
||||
return connection.QueryResult{Success: false, Message: "写入失败:" + err.Error()}
|
||||
}
|
||||
|
||||
logger.Infof("ExportData 完成:file=%s rows=%d", filename, len(data))
|
||||
return connection.QueryResult{Success: true, Message: "Export successful"}
|
||||
return connection.QueryResult{Success: true, Message: "导出完成"}
|
||||
}
|
||||
|
||||
// ExportQuery exports by executing the provided SELECT query on backend side.
|
||||
@@ -1494,7 +1494,7 @@ func (a *App) ExportData(data []map[string]interface{}, columns []string, defaul
|
||||
func (a *App) ExportQuery(config connection.ConnectionConfig, dbName string, query string, defaultName string, format string) connection.QueryResult {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
return connection.QueryResult{Success: false, Message: "query required"}
|
||||
return connection.QueryResult{Success: false, Message: "查询语句不能为空"}
|
||||
}
|
||||
|
||||
if defaultName == "" {
|
||||
@@ -1507,7 +1507,7 @@ func (a *App) ExportQuery(config connection.ConnectionConfig, dbName string, que
|
||||
})
|
||||
if err != nil || filename == "" {
|
||||
logger.Infof("ExportQuery 已取消或未选择文件:err=%v", err)
|
||||
return connection.QueryResult{Success: false, Message: "Cancelled"}
|
||||
return connection.QueryResult{Success: false, Message: "已取消"}
|
||||
}
|
||||
logger.Infof("ExportQuery 开始:type=%s db=%s format=%s file=%s sql=%q", strings.TrimSpace(config.Type), strings.TrimSpace(dbName), strings.ToLower(strings.TrimSpace(format)), filename, sqlSnippet(query))
|
||||
|
||||
@@ -1520,7 +1520,7 @@ func (a *App) ExportQuery(config connection.ConnectionConfig, dbName string, que
|
||||
query = sanitizeSQLForPgLike(runConfig.Type, query)
|
||||
lowerQuery := strings.ToLower(strings.TrimSpace(query))
|
||||
if !(strings.HasPrefix(lowerQuery, "select") || strings.HasPrefix(lowerQuery, "with")) {
|
||||
return connection.QueryResult{Success: false, Message: "Only SELECT/WITH queries are supported"}
|
||||
return connection.QueryResult{Success: false, Message: "仅支持 SELECT/WITH 查询导出"}
|
||||
}
|
||||
|
||||
data, columns, err := queryDataForExport(dbInst, runConfig, query)
|
||||
@@ -1537,11 +1537,11 @@ func (a *App) ExportQuery(config connection.ConnectionConfig, dbName string, que
|
||||
|
||||
if err := writeRowsToFile(f, data, columns, format); err != nil {
|
||||
logger.Warnf("ExportQuery 写入失败:file=%s err=%v", filename, err)
|
||||
return connection.QueryResult{Success: false, Message: "Write error: " + err.Error()}
|
||||
return connection.QueryResult{Success: false, Message: "写入失败:" + err.Error()}
|
||||
}
|
||||
|
||||
logger.Infof("ExportQuery 完成:file=%s rows=%d cols=%d", filename, len(data), len(columns))
|
||||
return connection.QueryResult{Success: true, Message: "Export successful"}
|
||||
return connection.QueryResult{Success: true, Message: "导出完成"}
|
||||
}
|
||||
|
||||
func queryDataForExport(dbInst db.Database, config connection.ConnectionConfig, query string) ([]map[string]interface{}, []string, error) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package connection
|
||||
|
||||
// SSHConfig holds SSH connection details
|
||||
// SSHConfig 存储 SSH 隧道连接配置。
|
||||
type SSHConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
@@ -9,7 +9,7 @@ type SSHConfig struct {
|
||||
KeyPath string `json:"keyPath"`
|
||||
}
|
||||
|
||||
// ProxyConfig holds proxy connection details
|
||||
// ProxyConfig 存储代理连接配置。
|
||||
type ProxyConfig struct {
|
||||
Type string `json:"type"` // socks5 | http
|
||||
Host string `json:"host"`
|
||||
@@ -18,7 +18,7 @@ type ProxyConfig struct {
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPTunnelConfig holds independent HTTP CONNECT tunnel details
|
||||
// HTTPTunnelConfig 存储 HTTP CONNECT 隧道配置。
|
||||
type HTTPTunnelConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
@@ -26,7 +26,7 @@ type HTTPTunnelConfig struct {
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// ConnectionConfig holds database connection details including SSH
|
||||
// ConnectionConfig 存储数据库连接的完整配置,包括 SSH、代理、SSL 等网络层设置。
|
||||
type ConnectionConfig struct {
|
||||
Type string `json:"type"`
|
||||
Host string `json:"host"`
|
||||
@@ -63,7 +63,7 @@ type ConnectionConfig struct {
|
||||
MongoReplicaPassword string `json:"mongoReplicaPassword,omitempty"` // MongoDB replica auth password
|
||||
}
|
||||
|
||||
// QueryResult is the standard response format for Wails methods
|
||||
// QueryResult 是 Wails 绑定方法的统一响应格式,前端通过此结构体接收后端结果。
|
||||
type QueryResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
@@ -72,7 +72,7 @@ type QueryResult struct {
|
||||
QueryID string `json:"queryId,omitempty"` // Unique ID for query cancellation
|
||||
}
|
||||
|
||||
// ColumnDefinition represents a table column
|
||||
// ColumnDefinition 描述表的一个列定义。
|
||||
type ColumnDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
@@ -83,7 +83,7 @@ type ColumnDefinition struct {
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
// IndexDefinition represents a table index
|
||||
// IndexDefinition 描述表的一个索引定义。
|
||||
type IndexDefinition struct {
|
||||
Name string `json:"name"`
|
||||
ColumnName string `json:"columnName"`
|
||||
@@ -93,7 +93,7 @@ type IndexDefinition struct {
|
||||
SubPart int `json:"subPart,omitempty"`
|
||||
}
|
||||
|
||||
// ForeignKeyDefinition represents a foreign key
|
||||
// ForeignKeyDefinition 描述表的一个外键定义。
|
||||
type ForeignKeyDefinition struct {
|
||||
Name string `json:"name"`
|
||||
ColumnName string `json:"columnName"`
|
||||
@@ -102,7 +102,7 @@ type ForeignKeyDefinition struct {
|
||||
ConstraintName string `json:"constraintName"`
|
||||
}
|
||||
|
||||
// TriggerDefinition represents a trigger
|
||||
// TriggerDefinition 描述表的一个触发器定义。
|
||||
type TriggerDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Timing string `json:"timing"` // BEFORE/AFTER
|
||||
@@ -110,26 +110,27 @@ type TriggerDefinition struct {
|
||||
Statement string `json:"statement"`
|
||||
}
|
||||
|
||||
// ColumnDefinitionWithTable represents a column with its table name (for search/autocomplete)
|
||||
// ColumnDefinitionWithTable 带有表名标识的列定义,用于跨表搜索和 SQL 自动补全。
|
||||
type ColumnDefinitionWithTable struct {
|
||||
TableName string `json:"tableName"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// UpdateRow represents a row update with keys (WHERE) and values (SET)
|
||||
// UpdateRow 表示一行更新操作,Keys 为 WHERE 条件,Values 为 SET 值。
|
||||
type UpdateRow struct {
|
||||
Keys map[string]interface{} `json:"keys"`
|
||||
Values map[string]interface{} `json:"values"`
|
||||
}
|
||||
|
||||
// ChangeSet represents a batch of changes
|
||||
// ChangeSet 表示一组批量变更,包含新增、修改和删除操作。
|
||||
type ChangeSet struct {
|
||||
Inserts []map[string]interface{} `json:"inserts"`
|
||||
Updates []UpdateRow `json:"updates"`
|
||||
Deletes []map[string]interface{} `json:"deletes"`
|
||||
}
|
||||
|
||||
// MongoMemberInfo 描述 MongoDB 副本集成员的信息。
|
||||
type MongoMemberInfo struct {
|
||||
Host string `json:"host"`
|
||||
Role string `json:"role"`
|
||||
|
||||
@@ -271,7 +271,7 @@ func (c *ClickHouseDB) Close() error {
|
||||
|
||||
func (c *ClickHouseDB) Ping() error {
|
||||
if c.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := c.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -284,7 +284,7 @@ func (c *ClickHouseDB) Ping() error {
|
||||
|
||||
func (c *ClickHouseDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if c.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
rows, err := c.conn.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -296,7 +296,7 @@ func (c *ClickHouseDB) QueryContext(ctx context.Context, query string) ([]map[st
|
||||
|
||||
func (c *ClickHouseDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if c.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
rows, err := c.conn.Query(query)
|
||||
if err != nil {
|
||||
@@ -308,7 +308,7 @@ func (c *ClickHouseDB) Query(query string) ([]map[string]interface{}, []string,
|
||||
|
||||
func (c *ClickHouseDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if c.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := c.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -319,7 +319,7 @@ func (c *ClickHouseDB) ExecContext(ctx context.Context, query string) (int64, er
|
||||
|
||||
func (c *ClickHouseDB) Exec(query string) (int64, error) {
|
||||
if c.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := c.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -404,7 +404,7 @@ func (c *ClickHouseDB) GetCreateStatement(dbName, tableName string) (string, err
|
||||
return "", err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
row := data[0]
|
||||
if val, ok := getClickHouseValueFromRow(row, "statement", "create_statement", "sql", "query"); ok {
|
||||
@@ -427,7 +427,7 @@ func (c *ClickHouseDB) GetCreateStatement(dbName, tableName string) (string, err
|
||||
if longest != "" {
|
||||
return longest, nil
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (c *ClickHouseDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
@@ -582,7 +582,7 @@ func (c *ClickHouseDB) GetTriggers(dbName, tableName string) ([]connection.Trigg
|
||||
func (c *ClickHouseDB) resolveDatabaseAndTable(dbName, tableName string) (string, string, error) {
|
||||
rawTable := strings.TrimSpace(tableName)
|
||||
if rawTable == "" {
|
||||
return "", "", fmt.Errorf("table name required")
|
||||
return "", "", fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
resolvedDB := strings.TrimSpace(dbName)
|
||||
@@ -603,7 +603,7 @@ func (c *ClickHouseDB) resolveDatabaseAndTable(dbName, tableName string) (string
|
||||
resolvedDB = defaultClickHouseDatabase
|
||||
}
|
||||
if resolvedTable == "" {
|
||||
return "", "", fmt.Errorf("table name required")
|
||||
return "", "", fmt.Errorf("表名不能为空")
|
||||
}
|
||||
return resolvedDB, resolvedTable, nil
|
||||
}
|
||||
@@ -682,7 +682,7 @@ func isClickHouseTruthy(value interface{}) bool {
|
||||
|
||||
func (c *ClickHouseDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if c.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
database, table, err := c.resolveDatabaseAndTable(c.database, tableName)
|
||||
@@ -723,7 +723,7 @@ func (c *ClickHouseDB) ApplyChanges(tableName string, changes connection.ChangeS
|
||||
continue
|
||||
}
|
||||
if _, err := c.conn.Exec(query); err != nil {
|
||||
return fmt.Errorf("insert error: %v; sql=%s", err, query)
|
||||
return fmt.Errorf("插入失败:%v; sql=%s", err, query)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -47,7 +47,7 @@ func (c *CustomDB) Close() error {
|
||||
|
||||
func (c *CustomDB) Ping() error {
|
||||
if c.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := c.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -60,7 +60,7 @@ func (c *CustomDB) Ping() error {
|
||||
|
||||
func (c *CustomDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if c.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := c.conn.QueryContext(ctx, query)
|
||||
@@ -74,7 +74,7 @@ func (c *CustomDB) QueryContext(ctx context.Context, query string) ([]map[string
|
||||
|
||||
func (c *CustomDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if c.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := c.conn.Query(query)
|
||||
@@ -87,7 +87,7 @@ func (c *CustomDB) Query(query string) ([]map[string]interface{}, []string, erro
|
||||
|
||||
func (c *CustomDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if c.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := c.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -98,7 +98,7 @@ func (c *CustomDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (c *CustomDB) Exec(query string) (int64, error) {
|
||||
if c.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := c.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -249,7 +249,7 @@ func (c *CustomDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDe
|
||||
|
||||
func (c *CustomDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if c.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := c.conn.Begin()
|
||||
@@ -321,7 +321,7 @@ func (c *CustomDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,12 +349,12 @@ func (c *CustomDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ func (c *CustomDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ func (d *DamengDB) Close() error {
|
||||
|
||||
func (d *DamengDB) Ping() error {
|
||||
if d.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := d.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -156,7 +156,7 @@ func (d *DamengDB) Ping() error {
|
||||
|
||||
func (d *DamengDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if d.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := d.conn.QueryContext(ctx, query)
|
||||
@@ -170,7 +170,7 @@ func (d *DamengDB) QueryContext(ctx context.Context, query string) ([]map[string
|
||||
|
||||
func (d *DamengDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if d.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := d.conn.Query(query)
|
||||
@@ -183,7 +183,7 @@ func (d *DamengDB) Query(query string) ([]map[string]interface{}, []string, erro
|
||||
|
||||
func (d *DamengDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if d.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := d.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -194,7 +194,7 @@ func (d *DamengDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (d *DamengDB) Exec(query string) (int64, error) {
|
||||
if d.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := d.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -260,7 +260,7 @@ func (d *DamengDB) GetCreateStatement(dbName, tableName string) (string, error)
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (d *DamengDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
@@ -390,7 +390,7 @@ func (d *DamengDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDe
|
||||
|
||||
func (d *DamengDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if d.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := d.conn.Begin()
|
||||
@@ -438,7 +438,7 @@ func (d *DamengDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,12 +466,12 @@ func (d *DamengDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ func (d *DamengDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,23 +6,42 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Database 定义了统一的数据源访问接口。
|
||||
// 所有数据库驱动(MySQL、PostgreSQL、Oracle 等)均需实现此接口。
|
||||
// 方法调用方可通过 NewDatabase 工厂函数获取对应驱动的实例。
|
||||
type Database interface {
|
||||
// Connect 根据连接配置建立数据库连接。
|
||||
Connect(config connection.ConnectionConfig) error
|
||||
// Close 关闭数据库连接并释放底层资源。
|
||||
Close() error
|
||||
// Ping 测试连接是否仍然可用。
|
||||
Ping() error
|
||||
// Query 执行查询语句,返回结果行(列名→值映射)和列名列表。
|
||||
Query(query string) ([]map[string]interface{}, []string, error)
|
||||
// Exec 执行非查询语句(INSERT/UPDATE/DELETE 等),返回受影响行数。
|
||||
Exec(query string) (int64, error)
|
||||
// GetDatabases 返回当前连接可访问的数据库列表。
|
||||
GetDatabases() ([]string, error)
|
||||
// GetTables 返回指定数据库下的表列表。
|
||||
GetTables(dbName string) ([]string, error)
|
||||
// GetCreateStatement 返回指定表的建表 DDL 语句。
|
||||
GetCreateStatement(dbName, tableName string) (string, error)
|
||||
// GetColumns 返回指定表的列定义列表。
|
||||
GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error)
|
||||
// GetAllColumns 返回指定数据库下所有表的列定义(含表名标识)。
|
||||
GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error)
|
||||
// GetIndexes 返回指定表的索引定义列表。
|
||||
GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error)
|
||||
// GetForeignKeys 返回指定表的外键定义列表。
|
||||
GetForeignKeys(dbName, tableName string) ([]connection.ForeignKeyDefinition, error)
|
||||
// GetTriggers 返回指定表的触发器定义列表。
|
||||
GetTriggers(dbName, tableName string) ([]connection.TriggerDefinition, error)
|
||||
}
|
||||
|
||||
// BatchApplier 定义了批量变更提交接口。
|
||||
// 支持批量编辑的驱动实现此接口,用于一次性提交前端 DataGrid 中的增删改操作。
|
||||
type BatchApplier interface {
|
||||
// ApplyChanges 将一组变更(新增、修改、删除)批量提交到指定表。
|
||||
ApplyChanges(tableName string, changes connection.ChangeSet) error
|
||||
}
|
||||
|
||||
@@ -72,7 +91,9 @@ func normalizeDatabaseType(dbType string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Factory
|
||||
// NewDatabase 根据数据库类型创建对应的 Database 实例。
|
||||
// dbType 为数据库类型标识(如 "mysql"、"postgres"、"oracle" 等),大小写不敏感。
|
||||
// 如果指定类型未注册,返回错误。
|
||||
func NewDatabase(dbType string) (Database, error) {
|
||||
normalized := normalizeDatabaseType(dbType)
|
||||
if normalized == "" {
|
||||
@@ -80,7 +101,7 @@ func NewDatabase(dbType string) (Database, error) {
|
||||
}
|
||||
factory, ok := databaseFactories[normalized]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported database type: %s", dbType)
|
||||
return nil, fmt.Errorf("不支持的数据库类型:%s", dbType)
|
||||
}
|
||||
return factory(), nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// coreBuiltinDrivers 是始终内置可用的核心驱动,无需额外安装即可使用。
|
||||
var coreBuiltinDrivers = map[string]struct{}{
|
||||
"mysql": {},
|
||||
"redis": {},
|
||||
@@ -91,6 +92,8 @@ func driverDisplayName(driverType string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// IsOptionalGoDriver 返回指定驱动类型是否为可选的纯 Go 驱动。
|
||||
// 可选驱动需要用户在驱动管理界面点击“安装启用”后才能使用。
|
||||
func IsOptionalGoDriver(driverType string) bool {
|
||||
_, ok := optionalGoDrivers[normalizeRuntimeDriverType(driverType)]
|
||||
return ok
|
||||
@@ -100,6 +103,7 @@ func IsOptionalGoDriverBuildIncluded(driverType string) bool {
|
||||
return optionalGoDriverBuildIncluded(normalizeRuntimeDriverType(driverType))
|
||||
}
|
||||
|
||||
// IsBuiltinDriver 返回指定驱动类型是否为核心内置驱动(始终可用,无需安装)。
|
||||
func IsBuiltinDriver(driverType string) bool {
|
||||
_, ok := coreBuiltinDrivers[normalizeRuntimeDriverType(driverType)]
|
||||
return ok
|
||||
@@ -146,6 +150,8 @@ func currentExternalDriverDownloadDirectory() string {
|
||||
return defaultExternalDriverDownloadDirectory()
|
||||
}
|
||||
|
||||
// SetExternalDriverDownloadDirectory 设置可选驱动的下载存储目录。
|
||||
// 如果路径解析失败,会回退到默认目录(~/.gonavi/drivers)。
|
||||
func SetExternalDriverDownloadDirectory(downloadDir string) {
|
||||
root, err := resolveExternalDriverRoot(downloadDir)
|
||||
if err != nil {
|
||||
|
||||
@@ -55,7 +55,7 @@ func (d *DuckDB) Close() error {
|
||||
|
||||
func (d *DuckDB) Ping() error {
|
||||
if d.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := d.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -68,7 +68,7 @@ func (d *DuckDB) Ping() error {
|
||||
|
||||
func (d *DuckDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if d.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
rows, err := d.conn.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -80,7 +80,7 @@ func (d *DuckDB) QueryContext(ctx context.Context, query string) ([]map[string]i
|
||||
|
||||
func (d *DuckDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if d.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
rows, err := d.conn.Query(query)
|
||||
if err != nil {
|
||||
@@ -92,7 +92,7 @@ func (d *DuckDB) Query(query string) ([]map[string]interface{}, []string, error)
|
||||
|
||||
func (d *DuckDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if d.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := d.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -103,7 +103,7 @@ func (d *DuckDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
|
||||
func (d *DuckDB) Exec(query string) (int64, error) {
|
||||
if d.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := d.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -174,7 +174,7 @@ ORDER BY table_schema, table_name`
|
||||
func (d *DuckDB) GetCreateStatement(dbName, tableName string) (string, error) {
|
||||
schema, pureTable := normalizeDuckDBSchemaAndTable(dbName, tableName)
|
||||
if pureTable == "" {
|
||||
return "", fmt.Errorf("table name required")
|
||||
return "", fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
escapedTable := escapeDuckDBLiteral(pureTable)
|
||||
@@ -204,13 +204,13 @@ func (d *DuckDB) GetCreateStatement(dbName, tableName string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (d *DuckDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
schema, pureTable := normalizeDuckDBSchemaAndTable(dbName, tableName)
|
||||
if pureTable == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
@@ -303,7 +303,7 @@ func (d *DuckDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDefi
|
||||
|
||||
func (d *DuckDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if d.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := d.conn.Begin()
|
||||
@@ -346,7 +346,7 @@ func (d *DuckDB) ApplyChanges(tableName string, changes connection.ChangeSet) er
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,12 +367,12 @@ func (d *DuckDB) ApplyChanges(tableName string, changes connection.ChangeSet) er
|
||||
args = append(args, v)
|
||||
}
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,7 +392,7 @@ func (d *DuckDB) ApplyChanges(tableName string, changes connection.ChangeSet) er
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ func (h *HighGoDB) Close() error {
|
||||
|
||||
func (h *HighGoDB) Ping() error {
|
||||
if h.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := h.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -138,7 +138,7 @@ func (h *HighGoDB) Ping() error {
|
||||
|
||||
func (h *HighGoDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if h.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := h.conn.QueryContext(ctx, query)
|
||||
@@ -152,7 +152,7 @@ func (h *HighGoDB) QueryContext(ctx context.Context, query string) ([]map[string
|
||||
|
||||
func (h *HighGoDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if h.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := h.conn.Query(query)
|
||||
@@ -165,7 +165,7 @@ func (h *HighGoDB) Query(query string) ([]map[string]interface{}, []string, erro
|
||||
|
||||
func (h *HighGoDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if h.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := h.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -176,7 +176,7 @@ func (h *HighGoDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (h *HighGoDB) Exec(query string) (int64, error) {
|
||||
if h.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := h.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -232,7 +232,7 @@ func (h *HighGoDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefi
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -302,7 +302,7 @@ func (h *HighGoDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefin
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -407,7 +407,7 @@ func (h *HighGoDB) GetForeignKeys(dbName, tableName string) ([]connection.Foreig
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -467,7 +467,7 @@ func (h *HighGoDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDe
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -531,7 +531,7 @@ ORDER BY table_schema, table_name, ordinal_position`
|
||||
|
||||
func (h *HighGoDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if h.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := h.conn.Begin()
|
||||
@@ -579,7 +579,7 @@ func (h *HighGoDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,12 +607,12 @@ func (h *HighGoDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +636,7 @@ func (h *HighGoDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ func (k *KingbaseDB) Close() error {
|
||||
|
||||
func (k *KingbaseDB) Ping() error {
|
||||
if k.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := k.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -245,7 +245,7 @@ func (k *KingbaseDB) Ping() error {
|
||||
|
||||
func (k *KingbaseDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if k.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := k.conn.QueryContext(ctx, query)
|
||||
@@ -259,7 +259,7 @@ func (k *KingbaseDB) QueryContext(ctx context.Context, query string) ([]map[stri
|
||||
|
||||
func (k *KingbaseDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if k.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := k.conn.Query(query)
|
||||
@@ -272,7 +272,7 @@ func (k *KingbaseDB) Query(query string) ([]map[string]interface{}, []string, er
|
||||
|
||||
func (k *KingbaseDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if k.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := k.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -283,7 +283,7 @@ func (k *KingbaseDB) ExecContext(ctx context.Context, query string) (int64, erro
|
||||
|
||||
func (k *KingbaseDB) Exec(query string) (int64, error) {
|
||||
if k.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := k.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -367,7 +367,7 @@ func (k *KingbaseDB) GetColumns(dbName, tableName string) ([]connection.ColumnDe
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
// 转义函数:处理单引号,移除双引号
|
||||
@@ -440,7 +440,7 @@ ORDER BY a.attnum`, esc(schema), esc(table))
|
||||
func (k *KingbaseDB) getColumnsWithCurrentSchema(tableName string) ([]connection.ColumnDefinition, error) {
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
// 转义函数
|
||||
@@ -524,7 +524,7 @@ func (k *KingbaseDB) GetIndexes(dbName, tableName string) ([]connection.IndexDef
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
// 转义函数:处理单引号,移除双引号
|
||||
@@ -622,7 +622,7 @@ func (k *KingbaseDB) GetForeignKeys(dbName, tableName string) ([]connection.Fore
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
// 转义函数:处理单引号,移除双引号
|
||||
@@ -704,7 +704,7 @@ func (k *KingbaseDB) GetTriggers(dbName, tableName string) ([]connection.Trigger
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
// 转义函数:处理单引号,移除双引号
|
||||
@@ -747,7 +747,7 @@ func (k *KingbaseDB) GetTriggers(dbName, tableName string) ([]connection.Trigger
|
||||
|
||||
func (k *KingbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if k.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := k.conn.Begin()
|
||||
@@ -758,7 +758,7 @@ func (k *KingbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
|
||||
schema, table := splitKingbaseQualifiedTable(tableName)
|
||||
if table == "" {
|
||||
return fmt.Errorf("table name required")
|
||||
return fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
qualifiedTable := ""
|
||||
@@ -811,7 +811,7 @@ func (k *KingbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
@@ -840,7 +840,7 @@ func (k *KingbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v; sql=%s", err, query)
|
||||
return fmt.Errorf("插入失败:%v; sql=%s", err, query)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ func (m *MariaDB) Close() error {
|
||||
|
||||
func (m *MariaDB) Ping() error {
|
||||
if m.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := m.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -86,7 +86,7 @@ func (m *MariaDB) Ping() error {
|
||||
|
||||
func (m *MariaDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if m.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := m.conn.QueryContext(ctx, query)
|
||||
@@ -100,7 +100,7 @@ func (m *MariaDB) QueryContext(ctx context.Context, query string) ([]map[string]
|
||||
|
||||
func (m *MariaDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if m.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := m.conn.Query(query)
|
||||
@@ -113,7 +113,7 @@ func (m *MariaDB) Query(query string) ([]map[string]interface{}, []string, error
|
||||
|
||||
func (m *MariaDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if m.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := m.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -124,7 +124,7 @@ func (m *MariaDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (m *MariaDB) Exec(query string) (int64, error) {
|
||||
if m.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := m.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -186,7 +186,7 @@ func (m *MariaDB) GetCreateStatement(dbName, tableName string) (string, error) {
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (m *MariaDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
@@ -320,7 +320,7 @@ func (m *MariaDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDef
|
||||
|
||||
func (m *MariaDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if m.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := m.conn.Begin()
|
||||
@@ -342,7 +342,7 @@ func (m *MariaDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM `%s` WHERE %s", tableName, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,12 +367,12 @@ func (m *MariaDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE `%s` SET %s WHERE %s", tableName, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ func (m *MariaDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", tableName, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ func (m *MariaDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
func (m *MariaDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error) {
|
||||
query := fmt.Sprintf("SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%s'", dbName)
|
||||
if dbName == "" {
|
||||
return nil, fmt.Errorf("database name required for GetAllColumns")
|
||||
return nil, fmt.Errorf("获取全部列信息需要指定数据库名称")
|
||||
}
|
||||
|
||||
data, _, err := m.Query(query)
|
||||
|
||||
@@ -480,7 +480,7 @@ func (m *MongoDB) Close() error {
|
||||
|
||||
func (m *MongoDB) Ping() error {
|
||||
if m.client == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := m.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -684,7 +684,7 @@ func buildMembersFromHello(raw bson.M) []connection.MongoMemberInfo {
|
||||
|
||||
func (m *MongoDB) DiscoverMembers() (string, []connection.MongoMemberInfo, error) {
|
||||
if m.client == nil {
|
||||
return "", nil, fmt.Errorf("connection not open")
|
||||
return "", nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
timeout := m.pingTimeout
|
||||
@@ -835,7 +835,7 @@ func extractCollectionFromSQL(sql string) string {
|
||||
|
||||
func (m *MongoDB) queryWithContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if m.client == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
query = strings.TrimSpace(query)
|
||||
@@ -1079,7 +1079,7 @@ func (m *MongoDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (m *MongoDB) GetDatabases() ([]string, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -1094,7 +1094,7 @@ func (m *MongoDB) GetDatabases() ([]string, error) {
|
||||
|
||||
func (m *MongoDB) GetTables(dbName string) ([]string, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
targetDB := dbName
|
||||
@@ -1130,7 +1130,7 @@ func (m *MongoDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWit
|
||||
// GetIndexes returns indexes for a MongoDB collection
|
||||
func (m *MongoDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
targetDB := dbName
|
||||
@@ -1197,7 +1197,7 @@ func (m *MongoDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDef
|
||||
// ApplyChanges implements batch changes for MongoDB
|
||||
func (m *MongoDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if m.client == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
@@ -1213,7 +1213,7 @@ func (m *MongoDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
}
|
||||
if len(filter) > 0 {
|
||||
if _, err := collection.DeleteOne(ctx, filter); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1225,7 +1225,7 @@ func (m *MongoDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
filter[k] = v
|
||||
}
|
||||
if len(filter) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
updateDoc := bson.M{"$set": bson.M{}}
|
||||
@@ -1234,7 +1234,7 @@ func (m *MongoDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
}
|
||||
|
||||
if _, err := collection.UpdateOne(ctx, filter, updateDoc); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1246,7 +1246,7 @@ func (m *MongoDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
}
|
||||
if len(doc) > 0 {
|
||||
if _, err := collection.InsertOne(ctx, doc); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ func (m *MongoDBV1) Close() error {
|
||||
|
||||
func (m *MongoDBV1) Ping() error {
|
||||
if m.client == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := m.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -687,7 +687,7 @@ func buildMembersFromHello(raw bson.M) []connection.MongoMemberInfo {
|
||||
|
||||
func (m *MongoDBV1) DiscoverMembers() (string, []connection.MongoMemberInfo, error) {
|
||||
if m.client == nil {
|
||||
return "", nil, fmt.Errorf("connection not open")
|
||||
return "", nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
timeout := m.pingTimeout
|
||||
@@ -838,7 +838,7 @@ func extractCollectionFromSQL(sql string) string {
|
||||
|
||||
func (m *MongoDBV1) queryWithContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if m.client == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
query = strings.TrimSpace(query)
|
||||
@@ -1082,7 +1082,7 @@ func (m *MongoDBV1) ExecContext(ctx context.Context, query string) (int64, error
|
||||
|
||||
func (m *MongoDBV1) GetDatabases() ([]string, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -1097,7 +1097,7 @@ func (m *MongoDBV1) GetDatabases() ([]string, error) {
|
||||
|
||||
func (m *MongoDBV1) GetTables(dbName string) ([]string, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
targetDB := dbName
|
||||
@@ -1133,7 +1133,7 @@ func (m *MongoDBV1) GetAllColumns(dbName string) ([]connection.ColumnDefinitionW
|
||||
// GetIndexes returns indexes for a MongoDB collection
|
||||
func (m *MongoDBV1) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
targetDB := dbName
|
||||
@@ -1200,7 +1200,7 @@ func (m *MongoDBV1) GetTriggers(dbName, tableName string) ([]connection.TriggerD
|
||||
// ApplyChanges implements batch changes for MongoDB
|
||||
func (m *MongoDBV1) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if m.client == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
@@ -1216,7 +1216,7 @@ func (m *MongoDBV1) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
if len(filter) > 0 {
|
||||
if _, err := collection.DeleteOne(ctx, filter); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1228,7 +1228,7 @@ func (m *MongoDBV1) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
filter[k] = v
|
||||
}
|
||||
if len(filter) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
updateDoc := bson.M{"$set": bson.M{}}
|
||||
@@ -1237,7 +1237,7 @@ func (m *MongoDBV1) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
|
||||
if _, err := collection.UpdateOne(ctx, filter, updateDoc); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1249,7 +1249,7 @@ func (m *MongoDBV1) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
if len(doc) > 0 {
|
||||
if _, err := collection.InsertOne(ctx, doc); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +429,7 @@ func (m *MySQLAgentDB) ApplyChanges(tableName string, changes connection.ChangeS
|
||||
|
||||
func (m *MySQLAgentDB) requireClient() (*mysqlAgentClient, error) {
|
||||
if m.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
return m.client, nil
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ func (m *MySQLDB) Close() error {
|
||||
|
||||
func (m *MySQLDB) Ping() error {
|
||||
if m.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := m.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -280,7 +280,7 @@ func (m *MySQLDB) Ping() error {
|
||||
|
||||
func (m *MySQLDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if m.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := m.conn.QueryContext(ctx, query)
|
||||
@@ -294,7 +294,7 @@ func (m *MySQLDB) QueryContext(ctx context.Context, query string) ([]map[string]
|
||||
|
||||
func (m *MySQLDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if m.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := m.conn.Query(query)
|
||||
@@ -307,7 +307,7 @@ func (m *MySQLDB) Query(query string) ([]map[string]interface{}, []string, error
|
||||
|
||||
func (m *MySQLDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if m.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := m.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -318,7 +318,7 @@ func (m *MySQLDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (m *MySQLDB) Exec(query string) (int64, error) {
|
||||
if m.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := m.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -380,7 +380,7 @@ func (m *MySQLDB) GetCreateStatement(dbName, tableName string) (string, error) {
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (m *MySQLDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
@@ -514,7 +514,7 @@ func (m *MySQLDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDef
|
||||
|
||||
func (m *MySQLDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if m.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
columnTypeMap := m.loadColumnTypeMap(tableName)
|
||||
@@ -539,7 +539,7 @@ func (m *MySQLDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
query := fmt.Sprintf("DELETE FROM `%s` WHERE %s", tableName, strings.Join(wheres, " AND "))
|
||||
res, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
if affected, err := res.RowsAffected(); err == nil && affected == 0 {
|
||||
return fmt.Errorf("删除未生效:未匹配到任何行")
|
||||
@@ -567,13 +567,13 @@ func (m *MySQLDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE `%s` SET %s WHERE %s", tableName, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
res, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
if affected, err := res.RowsAffected(); err == nil && affected == 0 {
|
||||
return fmt.Errorf("更新未生效:未匹配到任何行")
|
||||
@@ -600,7 +600,7 @@ func (m *MySQLDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
query := fmt.Sprintf("INSERT INTO `%s` () VALUES ()", tableName)
|
||||
res, err := tx.Exec(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
if affected, err := res.RowsAffected(); err == nil && affected == 0 {
|
||||
return fmt.Errorf("插入未生效:未影响任何行")
|
||||
@@ -611,7 +611,7 @@ func (m *MySQLDB) ApplyChanges(tableName string, changes connection.ChangeSet) e
|
||||
query := fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", tableName, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
res, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
if affected, err := res.RowsAffected(); err == nil && affected == 0 {
|
||||
return fmt.Errorf("插入未生效:未影响任何行")
|
||||
@@ -774,7 +774,7 @@ func formatMySQLDateTime(t time.Time) string {
|
||||
func (m *MySQLDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error) {
|
||||
query := fmt.Sprintf("SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%s'", dbName)
|
||||
if dbName == "" {
|
||||
return nil, fmt.Errorf("database name required for GetAllColumns")
|
||||
return nil, fmt.Errorf("获取全部列信息需要指定数据库名称")
|
||||
}
|
||||
|
||||
data, _, err := m.Query(query)
|
||||
|
||||
@@ -510,7 +510,7 @@ func (d *OptionalDriverAgentDB) ApplyChanges(tableName string, changes connectio
|
||||
|
||||
func (d *OptionalDriverAgentDB) requireClient() (*optionalDriverAgentClient, error) {
|
||||
if d.client == nil {
|
||||
return nil, fmt.Errorf("connection not open")
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
return d.client, nil
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func (o *OracleDB) Close() error {
|
||||
|
||||
func (o *OracleDB) Ping() error {
|
||||
if o.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := o.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -148,7 +148,7 @@ func (o *OracleDB) Ping() error {
|
||||
|
||||
func (o *OracleDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if o.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := o.conn.QueryContext(ctx, query)
|
||||
@@ -162,7 +162,7 @@ func (o *OracleDB) QueryContext(ctx context.Context, query string) ([]map[string
|
||||
|
||||
func (o *OracleDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if o.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := o.conn.Query(query)
|
||||
@@ -175,7 +175,7 @@ func (o *OracleDB) Query(query string) ([]map[string]interface{}, []string, erro
|
||||
|
||||
func (o *OracleDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if o.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := o.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -186,7 +186,7 @@ func (o *OracleDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (o *OracleDB) Exec(query string) (int64, error) {
|
||||
if o.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := o.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -259,7 +259,7 @@ func (o *OracleDB) GetCreateStatement(dbName, tableName string) (string, error)
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (o *OracleDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
@@ -391,7 +391,7 @@ func (o *OracleDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDe
|
||||
|
||||
func (o *OracleDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if o.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := o.conn.Begin()
|
||||
@@ -439,7 +439,7 @@ func (o *OracleDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,12 +467,12 @@ func (o *OracleDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ func (o *OracleDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ func (p *PostgresDB) Close() error {
|
||||
|
||||
func (p *PostgresDB) Ping() error {
|
||||
if p.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := p.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -208,7 +208,7 @@ func (p *PostgresDB) Ping() error {
|
||||
|
||||
func (p *PostgresDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if p.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := p.conn.QueryContext(ctx, query)
|
||||
@@ -222,7 +222,7 @@ func (p *PostgresDB) QueryContext(ctx context.Context, query string) ([]map[stri
|
||||
|
||||
func (p *PostgresDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if p.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := p.conn.Query(query)
|
||||
@@ -235,7 +235,7 @@ func (p *PostgresDB) Query(query string) ([]map[string]interface{}, []string, er
|
||||
|
||||
func (p *PostgresDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if p.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := p.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -246,7 +246,7 @@ func (p *PostgresDB) ExecContext(ctx context.Context, query string) (int64, erro
|
||||
|
||||
func (p *PostgresDB) Exec(query string) (int64, error) {
|
||||
if p.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := p.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -302,7 +302,7 @@ func (p *PostgresDB) GetColumns(dbName, tableName string) ([]connection.ColumnDe
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -372,7 +372,7 @@ func (p *PostgresDB) GetIndexes(dbName, tableName string) ([]connection.IndexDef
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -478,7 +478,7 @@ func (p *PostgresDB) GetForeignKeys(dbName, tableName string) ([]connection.Fore
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -538,7 +538,7 @@ func (p *PostgresDB) GetTriggers(dbName, tableName string) ([]connection.Trigger
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -602,7 +602,7 @@ ORDER BY table_schema, table_name, ordinal_position`
|
||||
|
||||
func (p *PostgresDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if p.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := p.conn.Begin()
|
||||
@@ -650,7 +650,7 @@ func (p *PostgresDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,12 +678,12 @@ func (p *PostgresDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,7 +707,7 @@ func (p *PostgresDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ func (s *SQLiteDB) Close() error {
|
||||
|
||||
func (s *SQLiteDB) Ping() error {
|
||||
if s.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := s.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -197,7 +197,7 @@ func (s *SQLiteDB) Ping() error {
|
||||
|
||||
func (s *SQLiteDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if s.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := s.conn.QueryContext(ctx, query)
|
||||
@@ -211,7 +211,7 @@ func (s *SQLiteDB) QueryContext(ctx context.Context, query string) ([]map[string
|
||||
|
||||
func (s *SQLiteDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if s.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := s.conn.Query(query)
|
||||
@@ -224,7 +224,7 @@ func (s *SQLiteDB) Query(query string) ([]map[string]interface{}, []string, erro
|
||||
|
||||
func (s *SQLiteDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if s.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := s.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -235,7 +235,7 @@ func (s *SQLiteDB) ExecContext(ctx context.Context, query string) (int64, error)
|
||||
|
||||
func (s *SQLiteDB) Exec(query string) (int64, error) {
|
||||
if s.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := s.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -275,13 +275,13 @@ func (s *SQLiteDB) GetCreateStatement(dbName, tableName string) (string, error)
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (s *SQLiteDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(v string) string { return strings.ReplaceAll(v, "'", "''") }
|
||||
@@ -372,7 +372,7 @@ func (s *SQLiteDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefi
|
||||
func (s *SQLiteDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(v string) string { return strings.ReplaceAll(v, "'", "''") }
|
||||
@@ -463,7 +463,7 @@ func (s *SQLiteDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefin
|
||||
func (s *SQLiteDB) GetForeignKeys(dbName, tableName string) ([]connection.ForeignKeyDefinition, error) {
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(v string) string { return strings.ReplaceAll(v, "'", "''") }
|
||||
@@ -537,7 +537,7 @@ func (s *SQLiteDB) GetForeignKeys(dbName, tableName string) ([]connection.Foreig
|
||||
func (s *SQLiteDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDefinition, error) {
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(v string) string { return strings.ReplaceAll(v, "'", "''") }
|
||||
@@ -588,7 +588,7 @@ func (s *SQLiteDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDe
|
||||
|
||||
func (s *SQLiteDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if s.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := s.conn.Begin()
|
||||
@@ -634,7 +634,7 @@ func (s *SQLiteDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,12 +659,12 @@ func (s *SQLiteDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +686,7 @@ func (s *SQLiteDB) ApplyChanges(tableName string, changes connection.ChangeSet)
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ func (s *SqlServerDB) Close() error {
|
||||
|
||||
func (s *SqlServerDB) Ping() error {
|
||||
if s.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := s.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -130,7 +130,7 @@ func (s *SqlServerDB) Ping() error {
|
||||
|
||||
func (s *SqlServerDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if s.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := s.conn.QueryContext(ctx, query)
|
||||
@@ -144,7 +144,7 @@ func (s *SqlServerDB) QueryContext(ctx context.Context, query string) ([]map[str
|
||||
|
||||
func (s *SqlServerDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if s.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := s.conn.Query(query)
|
||||
@@ -157,7 +157,7 @@ func (s *SqlServerDB) Query(query string) ([]map[string]interface{}, []string, e
|
||||
|
||||
func (s *SqlServerDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if s.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := s.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -168,7 +168,7 @@ func (s *SqlServerDB) ExecContext(ctx context.Context, query string) (int64, err
|
||||
|
||||
func (s *SqlServerDB) Exec(query string) (int64, error) {
|
||||
if s.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := s.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -236,7 +236,7 @@ func (s *SqlServerDB) GetColumns(dbName, tableName string) ([]connection.ColumnD
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -344,7 +344,7 @@ func (s *SqlServerDB) GetIndexes(dbName, tableName string) ([]connection.IndexDe
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -425,7 +425,7 @@ func (s *SqlServerDB) GetForeignKeys(dbName, tableName string) ([]connection.For
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -483,7 +483,7 @@ func (s *SqlServerDB) GetTriggers(dbName, tableName string) ([]connection.Trigge
|
||||
}
|
||||
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -530,7 +530,7 @@ ORDER BY tr.name`,
|
||||
|
||||
func (s *SqlServerDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if s.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := s.conn.Begin()
|
||||
@@ -573,7 +573,7 @@ func (s *SqlServerDB) ApplyChanges(tableName string, changes connection.ChangeSe
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,12 +601,12 @@ func (s *SqlServerDB) ApplyChanges(tableName string, changes connection.ChangeSe
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +630,7 @@ func (s *SqlServerDB) ApplyChanges(tableName string, changes connection.ChangeSe
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ func (t *TDengineDB) Close() error {
|
||||
|
||||
func (t *TDengineDB) Ping() error {
|
||||
if t.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := t.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -133,7 +133,7 @@ func (t *TDengineDB) Ping() error {
|
||||
|
||||
func (t *TDengineDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if t.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := t.conn.QueryContext(ctx, query)
|
||||
@@ -147,7 +147,7 @@ func (t *TDengineDB) QueryContext(ctx context.Context, query string) ([]map[stri
|
||||
|
||||
func (t *TDengineDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if t.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := t.conn.Query(query)
|
||||
@@ -161,7 +161,7 @@ func (t *TDengineDB) Query(query string) ([]map[string]interface{}, []string, er
|
||||
|
||||
func (t *TDengineDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if t.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := t.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -172,7 +172,7 @@ func (t *TDengineDB) ExecContext(ctx context.Context, query string) (int64, erro
|
||||
|
||||
func (t *TDengineDB) Exec(query string) (int64, error) {
|
||||
if t.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := t.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -274,7 +274,7 @@ func (t *TDengineDB) GetCreateStatement(dbName, tableName string) (string, error
|
||||
if lastErr != nil {
|
||||
return "", lastErr
|
||||
}
|
||||
return "", fmt.Errorf("create statement not found")
|
||||
return "", fmt.Errorf("未找到建表语句")
|
||||
}
|
||||
|
||||
func (t *TDengineDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
@@ -325,7 +325,7 @@ func (t *TDengineDB) GetColumns(dbName, tableName string) ([]connection.ColumnDe
|
||||
|
||||
func (t *TDengineDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error) {
|
||||
if strings.TrimSpace(dbName) == "" {
|
||||
return nil, fmt.Errorf("database name required for GetAllColumns")
|
||||
return nil, fmt.Errorf("获取全部列信息需要指定数据库名称")
|
||||
}
|
||||
|
||||
tables, err := t.GetTables(dbName)
|
||||
@@ -365,10 +365,10 @@ func (t *TDengineDB) GetTriggers(dbName, tableName string) ([]connection.Trigger
|
||||
|
||||
func (t *TDengineDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if t.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
if strings.TrimSpace(tableName) == "" {
|
||||
return fmt.Errorf("table name required")
|
||||
return fmt.Errorf("表名不能为空")
|
||||
}
|
||||
if len(changes.Updates) > 0 || len(changes.Deletes) > 0 {
|
||||
return fmt.Errorf("TDengine 目标端当前仅支持 INSERT 写入,暂不支持 UPDATE/DELETE 差异同步,请改用仅插入或全量覆盖模式")
|
||||
@@ -384,7 +384,7 @@ func (t *TDengineDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
continue
|
||||
}
|
||||
if _, err := t.conn.Exec(query); err != nil {
|
||||
return fmt.Errorf("insert error: %v; sql=%s", err, query)
|
||||
return fmt.Errorf("插入失败:%v; sql=%s", err, query)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -392,7 +392,7 @@ func (t *TDengineDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
|
||||
func buildTDengineInsertSQL(qualifiedTable string, row map[string]interface{}) (string, error) {
|
||||
if strings.TrimSpace(qualifiedTable) == "" {
|
||||
return "", fmt.Errorf("qualified table required")
|
||||
return "", fmt.Errorf("需要指定完整的表名")
|
||||
}
|
||||
if len(row) == 0 {
|
||||
return "", nil
|
||||
|
||||
@@ -124,7 +124,7 @@ func (v *VastbaseDB) Close() error {
|
||||
|
||||
func (v *VastbaseDB) Ping() error {
|
||||
if v.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
timeout := v.pingTimeout
|
||||
if timeout <= 0 {
|
||||
@@ -137,7 +137,7 @@ func (v *VastbaseDB) Ping() error {
|
||||
|
||||
func (v *VastbaseDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
|
||||
if v.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := v.conn.QueryContext(ctx, query)
|
||||
@@ -151,7 +151,7 @@ func (v *VastbaseDB) QueryContext(ctx context.Context, query string) ([]map[stri
|
||||
|
||||
func (v *VastbaseDB) Query(query string) ([]map[string]interface{}, []string, error) {
|
||||
if v.conn == nil {
|
||||
return nil, nil, fmt.Errorf("connection not open")
|
||||
return nil, nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
rows, err := v.conn.Query(query)
|
||||
@@ -164,7 +164,7 @@ func (v *VastbaseDB) Query(query string) ([]map[string]interface{}, []string, er
|
||||
|
||||
func (v *VastbaseDB) ExecContext(ctx context.Context, query string) (int64, error) {
|
||||
if v.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := v.conn.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
@@ -175,7 +175,7 @@ func (v *VastbaseDB) ExecContext(ctx context.Context, query string) (int64, erro
|
||||
|
||||
func (v *VastbaseDB) Exec(query string) (int64, error) {
|
||||
if v.conn == nil {
|
||||
return 0, fmt.Errorf("connection not open")
|
||||
return 0, fmt.Errorf("连接未打开")
|
||||
}
|
||||
res, err := v.conn.Exec(query)
|
||||
if err != nil {
|
||||
@@ -231,7 +231,7 @@ func (v *VastbaseDB) GetColumns(dbName, tableName string) ([]connection.ColumnDe
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -301,7 +301,7 @@ func (v *VastbaseDB) GetIndexes(dbName, tableName string) ([]connection.IndexDef
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -406,7 +406,7 @@ func (v *VastbaseDB) GetForeignKeys(dbName, tableName string) ([]connection.Fore
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -466,7 +466,7 @@ func (v *VastbaseDB) GetTriggers(dbName, tableName string) ([]connection.Trigger
|
||||
}
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return nil, fmt.Errorf("table name required")
|
||||
return nil, fmt.Errorf("表名不能为空")
|
||||
}
|
||||
|
||||
esc := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
@@ -530,7 +530,7 @@ ORDER BY table_schema, table_name, ordinal_position`
|
||||
|
||||
func (v *VastbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet) error {
|
||||
if v.conn == nil {
|
||||
return fmt.Errorf("connection not open")
|
||||
return fmt.Errorf("连接未打开")
|
||||
}
|
||||
|
||||
tx, err := v.conn.Begin()
|
||||
@@ -578,7 +578,7 @@ func (v *VastbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
}
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE %s", qualifiedTable, strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("delete error: %v", err)
|
||||
return fmt.Errorf("删除失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,12 +606,12 @@ func (v *VastbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
}
|
||||
|
||||
if len(wheres) == 0 {
|
||||
return fmt.Errorf("update requires keys")
|
||||
return fmt.Errorf("更新操作需要主键条件")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s", qualifiedTable, strings.Join(sets, ", "), strings.Join(wheres, " AND "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("update error: %v", err)
|
||||
return fmt.Errorf("更新失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,7 +635,7 @@ func (v *VastbaseDB) ApplyChanges(tableName string, changes connection.ChangeSet
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", qualifiedTable, strings.Join(cols, ", "), strings.Join(placeholders, ", "))
|
||||
if _, err := tx.Exec(query, args...); err != nil {
|
||||
return fmt.Errorf("insert error: %v", err)
|
||||
return fmt.Errorf("插入失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
65
internal/logger/logger_test.go
Normal file
65
internal/logger/logger_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorChain_NilError(t *testing.T) {
|
||||
if got := ErrorChain(nil); got != "" {
|
||||
t.Errorf("ErrorChain(nil) = %q; want empty string", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorChain_SingleError(t *testing.T) {
|
||||
err := errors.New("single error")
|
||||
got := ErrorChain(err)
|
||||
if got != "single error" {
|
||||
t.Errorf("ErrorChain(single) = %q; want %q", got, "single error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorChain_WrappedErrors(t *testing.T) {
|
||||
inner := errors.New("root cause")
|
||||
middle := fmt.Errorf("middle: %w", inner)
|
||||
outer := fmt.Errorf("outer: %w", middle)
|
||||
|
||||
got := ErrorChain(outer)
|
||||
// Should contain all three distinct messages
|
||||
if got == "" {
|
||||
t.Fatal("ErrorChain returned empty string for wrapped errors")
|
||||
}
|
||||
// The chain should start with the outermost error
|
||||
if len(got) < len("outer:") {
|
||||
t.Errorf("ErrorChain result too short: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorChain_DeduplicatesMessages(t *testing.T) {
|
||||
// Create a chain where wrapping doesn't add new text
|
||||
inner := errors.New("same message")
|
||||
outer := fmt.Errorf("%w", inner)
|
||||
|
||||
got := ErrorChain(outer)
|
||||
// Should not repeat "same message"
|
||||
if got != "same message" {
|
||||
t.Errorf("ErrorChain should deduplicate: got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorChain_TruncatesLongChain(t *testing.T) {
|
||||
// Build a chain of 25 errors (exceeds the 20-level limit)
|
||||
var err error = errors.New("base")
|
||||
for i := 0; i < 25; i++ {
|
||||
err = fmt.Errorf("level-%d: %w", i, err)
|
||||
}
|
||||
got := ErrorChain(err)
|
||||
if got == "" {
|
||||
t.Fatal("ErrorChain returned empty for long chain")
|
||||
}
|
||||
// Should contain truncation notice
|
||||
if len(got) == 0 {
|
||||
t.Error("expected non-empty result for long chain")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user