diff --git a/frontend/src/components/DataGrid.tsx b/frontend/src/components/DataGrid.tsx index 49458a5..427a49f 100644 --- a/frontend/src/components/DataGrid.tsx +++ b/frontend/src/components/DataGrid.tsx @@ -8,14 +8,19 @@ import { useStore } from '../store'; import { v4 as uuidv4 } from 'uuid'; import 'react-resizable/css/styles.css'; +// Normalize RFC3339-like datetime strings to `YYYY-MM-DD HH:mm:ss` for display/editing. +const normalizeDateTimeString = (val: string) => { + const match = val.match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})/); + if (!match) return val; + return `${match[1]} ${match[2]}`; +}; + // --- Helper: Format Value --- const formatCellValue = (val: any) => { if (val === null) return NULL; if (typeof val === 'object') return JSON.stringify(val); if (typeof val === 'string') { - if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(val)) { - return val.replace('T', ' ').replace(/\+.*$/, '').replace(/Z$/, ''); - } + return normalizeDateTimeString(val); } return String(val); }; @@ -103,13 +108,15 @@ const EditableCell: React.FC = React.memo(({ const toggleEdit = () => { setEditing(!editing); - form.setFieldsValue({ [dataIndex]: record[dataIndex] }); + const raw = record[dataIndex]; + const initialValue = typeof raw === 'string' ? normalizeDateTimeString(raw) : raw; + form.setFieldsValue({ [dataIndex]: initialValue }); }; const save = async () => { try { if (!form) return; - const values = await form.validateFields(); + const values = await form.validateFields([dataIndex]); toggleEdit(); handleSave({ ...record, ...values }); } catch (errInfo) { @@ -278,6 +285,7 @@ const DataGrid: React.FC = ({ setModifiedRows({}); setDeletedRowKeys(new Set()); setSelectedRowKeys([]); + form.resetFields(); }, [tableName, dbName, connectionId]); // Reset on context change const displayData = useMemo(() => { diff --git a/frontend/src/components/DataViewer.tsx b/frontend/src/components/DataViewer.tsx index fc4f0fd..db332a4 100644 --- a/frontend/src/components/DataViewer.tsx +++ b/frontend/src/components/DataViewer.tsx @@ -41,11 +41,18 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => { ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" } }; - const quoteIdent = (ident: string) => { + const quoteIdentPart = (ident: string) => { if (!ident) return ident; if (config.type === 'mysql') return `\`${ident.replace(/`/g, '``')}\``; return `"${ident.replace(/"/g, '""')}"`; }; + const quoteQualifiedIdent = (ident: string) => { + const raw = (ident || '').trim(); + if (!raw) return raw; + const parts = raw.split('.').filter(Boolean); + if (parts.length <= 1) return quoteIdentPart(raw); + return parts.map(quoteIdentPart).join('.'); + }; const escapeLiteral = (val: string) => val.replace(/'/g, "''"); const dbName = tab.dbName || ''; @@ -55,19 +62,19 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => { filterConditions.forEach(cond => { if (cond.column && cond.value) { if (cond.op === 'LIKE') { - whereParts.push(`${quoteIdent(cond.column)} LIKE '%${escapeLiteral(cond.value)}%'`); + whereParts.push(`${quoteIdentPart(cond.column)} LIKE '%${escapeLiteral(cond.value)}%'`); } else { - whereParts.push(`${quoteIdent(cond.column)} ${cond.op} '${escapeLiteral(cond.value)}'`); + whereParts.push(`${quoteIdentPart(cond.column)} ${cond.op} '${escapeLiteral(cond.value)}'`); } } }); const whereSQL = whereParts.length > 0 ? `WHERE ${whereParts.join(' AND ')}` : ""; - const countSql = `SELECT COUNT(*) as total FROM ${quoteIdent(tableName)} ${whereSQL}`; + const countSql = `SELECT COUNT(*) as total FROM ${quoteQualifiedIdent(tableName)} ${whereSQL}`; - let sql = `SELECT * FROM ${quoteIdent(tableName)} ${whereSQL}`; + let sql = `SELECT * FROM ${quoteQualifiedIdent(tableName)} ${whereSQL}`; if (sortInfo && sortInfo.order) { - sql += ` ORDER BY ${quoteIdent(sortInfo.columnKey)} ${sortInfo.order === 'ascend' ? 'ASC' : 'DESC'}`; + sql += ` ORDER BY ${quoteIdentPart(sortInfo.columnKey)} ${sortInfo.order === 'ascend' ? 'ASC' : 'DESC'}`; } const offset = (page - 1) * size; sql += ` LIMIT ${size} OFFSET ${offset}`; diff --git a/internal/app/db_context.go b/internal/app/db_context.go new file mode 100644 index 0000000..684c7a3 --- /dev/null +++ b/internal/app/db_context.go @@ -0,0 +1,56 @@ +package app + +import ( + "strings" + + "GoNavi-Wails/internal/connection" +) + +func normalizeRunConfig(config connection.ConnectionConfig, dbName string) connection.ConnectionConfig { + runConfig := config + name := strings.TrimSpace(dbName) + if name == "" { + return runConfig + } + + switch strings.ToLower(strings.TrimSpace(config.Type)) { + case "mysql", "postgres", "kingbase": + // 这些类型的 dbName 表示“数据库”,需要写入连接配置以选择目标库。 + runConfig.Database = name + case "dameng": + // 达梦使用 schema 参数,沿用现有行为:dbName 表示 schema。 + runConfig.Database = name + default: + // oracle: dbName 表示 schema/owner,不能覆盖 config.Database(服务名) + // sqlite: 无需设置 Database + // custom: 语义不明确,避免污染缓存 key + } + + return runConfig +} + +func normalizeSchemaAndTable(config connection.ConnectionConfig, dbName string, tableName string) (string, string) { + rawTable := strings.TrimSpace(tableName) + rawDB := strings.TrimSpace(dbName) + if rawTable == "" { + return rawDB, rawTable + } + + if parts := strings.SplitN(rawTable, ".", 2); len(parts) == 2 { + schema := strings.TrimSpace(parts[0]) + table := strings.TrimSpace(parts[1]) + if schema != "" && table != "" { + return schema, table + } + } + + switch strings.ToLower(strings.TrimSpace(config.Type)) { + case "postgres", "kingbase": + // PG/金仓:dbName 在 UI 里是“数据库”,schema 需从 tableName 或使用默认 public。 + return "public", rawTable + default: + // MySQL:dbName 表示数据库;Oracle/达梦:dbName 表示 schema/owner。 + return rawDB, rawTable + } +} + diff --git a/internal/app/methods_db.go b/internal/app/methods_db.go index 1e31820..fa010be 100644 --- a/internal/app/methods_db.go +++ b/internal/app/methods_db.go @@ -83,10 +83,7 @@ func (a *App) MySQLShowCreateTable(config connection.ConnectionConfig, dbName st } func (a *App) DBQuery(config connection.ConnectionConfig, dbName string, query string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { @@ -143,10 +140,7 @@ func (a *App) DBGetDatabases(config connection.ConnectionConfig) connection.Quer } func (a *App) DBGetTables(config connection.ConnectionConfig, dbName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { @@ -169,10 +163,7 @@ func (a *App) DBGetTables(config connection.ConnectionConfig, dbName string) con } func (a *App) DBShowCreateTable(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { @@ -180,7 +171,8 @@ func (a *App) DBShowCreateTable(config connection.ConnectionConfig, dbName strin return connection.QueryResult{Success: false, Message: err.Error()} } - sqlStr, err := dbInst.GetCreateStatement(dbName, tableName) + schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName) + sqlStr, err := dbInst.GetCreateStatement(schemaName, pureTableName) if err != nil { logger.Error(err, "DBShowCreateTable 获取建表语句失败:%s 表=%s", formatConnSummary(runConfig), tableName) return connection.QueryResult{Success: false, Message: err.Error()} @@ -190,17 +182,15 @@ func (a *App) DBShowCreateTable(config connection.ConnectionConfig, dbName strin } func (a *App) DBGetColumns(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } - columns, err := dbInst.GetColumns(dbName, tableName) + schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName) + columns, err := dbInst.GetColumns(schemaName, pureTableName) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } @@ -209,17 +199,15 @@ func (a *App) DBGetColumns(config connection.ConnectionConfig, dbName string, ta } func (a *App) DBGetIndexes(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } - indexes, err := dbInst.GetIndexes(dbName, tableName) + schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName) + indexes, err := dbInst.GetIndexes(schemaName, pureTableName) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } @@ -228,17 +216,15 @@ func (a *App) DBGetIndexes(config connection.ConnectionConfig, dbName string, ta } func (a *App) DBGetForeignKeys(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } - fks, err := dbInst.GetForeignKeys(dbName, tableName) + schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName) + fks, err := dbInst.GetForeignKeys(schemaName, pureTableName) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } @@ -247,17 +233,15 @@ func (a *App) DBGetForeignKeys(config connection.ConnectionConfig, dbName string } func (a *App) DBGetTriggers(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } - triggers, err := dbInst.GetTriggers(dbName, tableName) + schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName) + triggers, err := dbInst.GetTriggers(schemaName, pureTableName) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } @@ -266,10 +250,7 @@ func (a *App) DBGetTriggers(config connection.ConnectionConfig, dbName string, t } func (a *App) DBGetAllColumns(config connection.ConnectionConfig, dbName string) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { diff --git a/internal/app/methods_file.go b/internal/app/methods_file.go index d28b83b..e717fe6 100644 --- a/internal/app/methods_file.go +++ b/internal/app/methods_file.go @@ -135,10 +135,7 @@ func (a *App) ImportData(config connection.ConnectionConfig, dbName, tableName s return connection.QueryResult{Success: true, Message: "No data to import"} } - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} @@ -164,21 +161,16 @@ func (a *App) ImportData(config connection.ConnectionConfig, dbName, tableName s values = append(values, fmt.Sprintf("'%s'", vStr)) } } - - query := fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", - tableName, - strings.Join(cols, ", "), - strings.Join(values, ", ")) - - if runConfig.Type == "postgres" { - pgCols := make([]string, len(cols)) - for i, c := range cols { pgCols[i] = fmt.Sprintf("\"%s\"", c) } - query = fmt.Sprintf("INSERT INTO \"%s\" (%s) VALUES (%s)", - tableName, - strings.Join(pgCols, ", "), - strings.Join(values, ", ")) + quotedCols := make([]string, len(cols)) + for i, c := range cols { + quotedCols[i] = quoteIdentByType(runConfig.Type, c) } + query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", + quoteQualifiedIdentByType(runConfig.Type, tableName), + strings.Join(quotedCols, ", "), + strings.Join(values, ", ")) + _, err := dbInst.Exec(query) if err != nil { errCount++ @@ -192,10 +184,7 @@ func (a *App) ImportData(config connection.ConnectionConfig, dbName, tableName s } func (a *App) ApplyChanges(config connection.ConnectionConfig, dbName, tableName string, changes connection.ChangeSet) connection.QueryResult { - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { @@ -223,20 +212,14 @@ func (a *App) ExportTable(config connection.ConnectionConfig, dbName string, tab return connection.QueryResult{Success: false, Message: "Cancelled"} } - runConfig := config - if dbName != "" { - runConfig.Database = dbName - } + runConfig := normalizeRunConfig(config, dbName) dbInst, err := a.getDatabase(runConfig) if err != nil { return connection.QueryResult{Success: false, Message: err.Error()} } - query := fmt.Sprintf("SELECT * FROM `%s`", tableName) - if runConfig.Type == "postgres" { - query = fmt.Sprintf("SELECT * FROM \"%s\"", tableName) - } + query := fmt.Sprintf("SELECT * FROM %s", quoteQualifiedIdentByType(runConfig.Type, tableName)) data, columns, err := dbInst.Query(query) if err != nil { @@ -318,6 +301,45 @@ data, columns, err := dbInst.Query(query) return connection.QueryResult{Success: true, Message: "Export successful"} } +func quoteIdentByType(dbType string, ident string) string { + if ident == "" { + return ident + } + + switch dbType { + case "mysql": + return "`" + strings.ReplaceAll(ident, "`", "``") + "`" + default: + return `"` + strings.ReplaceAll(ident, `"`, `""`) + `"` + } +} + +func quoteQualifiedIdentByType(dbType string, ident string) string { + raw := strings.TrimSpace(ident) + if raw == "" { + return raw + } + + parts := strings.Split(raw, ".") + if len(parts) <= 1 { + return quoteIdentByType(dbType, raw) + } + + quotedParts := make([]string, 0, len(parts)) + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "" { + continue + } + quotedParts = append(quotedParts, quoteIdentByType(dbType, part)) + } + + if len(quotedParts) == 0 { + return quoteIdentByType(dbType, raw) + } + return strings.Join(quotedParts, ".") +} + // ExportData exports provided data to a file func (a *App) ExportData(data []map[string]interface{}, columns []string, defaultName string, format string) connection.QueryResult { if defaultName == "" { diff --git a/internal/db/custom_impl.go b/internal/db/custom_impl.go index 4fcfdcc..3d21faa 100644 --- a/internal/db/custom_impl.go +++ b/internal/db/custom_impl.go @@ -92,7 +92,11 @@ func (c *CustomDB) Query(query string) ([]map[string]interface{}, []string, erro val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val } @@ -136,13 +140,22 @@ func (c *CustomDB) GetTables(dbName string) ([]string, error) { query = fmt.Sprintf("SHOW TABLES FROM `%s`", dbName) } } else if c.driver == "postgres" || c.driver == "kingbase" { - if dbName != "" && dbName != "public" { - query = fmt.Sprintf("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", dbName) + query = ` + SELECT table_schema AS schemaname, table_name AS tablename + FROM information_schema.tables + WHERE table_type = 'BASE TABLE' + AND table_schema NOT IN ('pg_catalog', 'information_schema')` + if dbName != "" { + query += fmt.Sprintf(" AND table_schema = '%s'", dbName) } + query += " ORDER BY table_schema, table_name" } else if c.driver == "sqlite" { query = "SELECT name FROM sqlite_master WHERE type='table'" } else if c.driver == "oracle" || c.driver == "dm" { query = "SELECT table_name FROM user_tables" + if dbName != "" { + query = fmt.Sprintf("SELECT owner, table_name FROM all_tables WHERE owner = '%s' ORDER BY table_name", strings.ToUpper(dbName)) + } } // Fallback generic execution @@ -153,6 +166,18 @@ func (c *CustomDB) GetTables(dbName string) ([]string, error) { var tables []string for _, row := range data { + if schema, okSchema := row["schemaname"]; okSchema { + if name, okName := row["tablename"]; okName { + tables = append(tables, fmt.Sprintf("%v.%v", schema, name)) + continue + } + } + if owner, okOwner := row["OWNER"]; okOwner { + if name, okName := row["TABLE_NAME"]; okName { + tables = append(tables, fmt.Sprintf("%v.%v", owner, name)) + continue + } + } // iterate keys to find likely column for k, v := range row { if strings.Contains(strings.ToLower(k), "name") || strings.Contains(strings.ToLower(k), "table") { diff --git a/internal/db/dameng_impl.go b/internal/db/dameng_impl.go index 2fe1ba3..e8b7b31 100644 --- a/internal/db/dameng_impl.go +++ b/internal/db/dameng_impl.go @@ -123,7 +123,11 @@ func (d *DamengDB) Query(query string) ([]map[string]interface{}, []string, erro val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val } @@ -166,7 +170,7 @@ func (d *DamengDB) GetDatabases() ([]string, error) { } func (d *DamengDB) GetTables(dbName string) ([]string, error) { - query := fmt.Sprintf("SELECT table_name FROM all_tables WHERE owner = '%s'", strings.ToUpper(dbName)) + query := fmt.Sprintf("SELECT owner, table_name FROM all_tables WHERE owner = '%s' ORDER BY table_name", strings.ToUpper(dbName)) if dbName == "" { query = "SELECT table_name FROM user_tables" } @@ -178,6 +182,14 @@ func (d *DamengDB) GetTables(dbName string) ([]string, error) { var tables []string for _, row := range data { + if dbName != "" { + if owner, okOwner := row["OWNER"]; okOwner { + if name, okName := row["TABLE_NAME"]; okName { + tables = append(tables, fmt.Sprintf("%v.%v", owner, name)) + continue + } + } + } if val, ok := row["TABLE_NAME"]; ok { tables = append(tables, fmt.Sprintf("%v", val)) } diff --git a/internal/db/kingbase_impl.go b/internal/db/kingbase_impl.go index 7d62c22..a85210f 100644 --- a/internal/db/kingbase_impl.go +++ b/internal/db/kingbase_impl.go @@ -154,7 +154,11 @@ func (k *KingbaseDB) Query(query string) ([]map[string]interface{}, []string, er val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val } @@ -193,15 +197,14 @@ func (k *KingbaseDB) GetDatabases() ([]string, error) { } func (k *KingbaseDB) GetTables(dbName string) ([]string, error) { - // Usually restricted to current database connection in PG/Kingbase - // dbName param is often Schema in PG context, or ignored if we are connected to a specific DB. - // But in PG, cross-database queries are not standard without dblink. - // We assume dbName here might mean Schema (public, etc.) - - query := "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'" - if dbName != "" && dbName != "public" { - query = fmt.Sprintf("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", dbName) - } + // Kingbase: tables are scoped by the current DB connection; include schema to avoid search_path issues. + query := ` + SELECT table_schema AS schemaname, table_name AS tablename + FROM information_schema.tables + WHERE table_type = 'BASE TABLE' + AND table_schema NOT IN ('pg_catalog', 'information_schema') + AND table_schema NOT LIKE 'pg_%' + ORDER BY table_schema, table_name` data, _, err := k.Query(query) if err != nil { @@ -210,6 +213,12 @@ func (k *KingbaseDB) GetTables(dbName string) ([]string, error) { var tables []string for _, row := range data { + schema, okSchema := row["schemaname"] + name, okName := row["tablename"] + if okSchema && okName { + tables = append(tables, fmt.Sprintf("%v.%v", schema, name)) + continue + } if val, ok := row["table_name"]; ok { tables = append(tables, fmt.Sprintf("%v", val)) } diff --git a/internal/db/mysql_impl.go b/internal/db/mysql_impl.go index 7c8c041..fdd11d2 100644 --- a/internal/db/mysql_impl.go +++ b/internal/db/mysql_impl.go @@ -111,7 +111,11 @@ func (m *MySQLDB) Query(query string) ([]map[string]interface{}, []string, error val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val } diff --git a/internal/db/oracle_impl.go b/internal/db/oracle_impl.go index ef162fe..95598b8 100644 --- a/internal/db/oracle_impl.go +++ b/internal/db/oracle_impl.go @@ -129,7 +129,11 @@ func (o *OracleDB) Query(query string) ([]map[string]interface{}, []string, erro val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val } @@ -171,7 +175,7 @@ func (o *OracleDB) GetTables(dbName string) ([]string, error) { // dbName is Schema/Owner query := "SELECT table_name FROM user_tables" if dbName != "" { - query = fmt.Sprintf("SELECT table_name FROM all_tables WHERE owner = '%s'", strings.ToUpper(dbName)) + query = fmt.Sprintf("SELECT owner, table_name FROM all_tables WHERE owner = '%s' ORDER BY table_name", strings.ToUpper(dbName)) } data, _, err := o.Query(query) @@ -181,6 +185,14 @@ func (o *OracleDB) GetTables(dbName string) ([]string, error) { var tables []string for _, row := range data { + if dbName != "" { + if owner, okOwner := row["OWNER"]; okOwner { + if name, okName := row["TABLE_NAME"]; okName { + tables = append(tables, fmt.Sprintf("%v.%v", owner, name)) + continue + } + } + } if val, ok := row["TABLE_NAME"]; ok { tables = append(tables, fmt.Sprintf("%v", val)) } diff --git a/internal/db/postgres_impl.go b/internal/db/postgres_impl.go index 42602f1..fba01da 100644 --- a/internal/db/postgres_impl.go +++ b/internal/db/postgres_impl.go @@ -112,7 +112,11 @@ rows, err := p.conn.Query(query) val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val } @@ -150,7 +154,7 @@ func (p *PostgresDB) GetDatabases() ([]string, error) { } func (p *PostgresDB) GetTables(dbName string) ([]string, error) { - query := "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'" + query := "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname != 'information_schema' AND schemaname NOT LIKE 'pg_%' ORDER BY schemaname, tablename" data, _, err := p.Query(query) if err != nil { return nil, err @@ -158,8 +162,14 @@ func (p *PostgresDB) GetTables(dbName string) ([]string, error) { var tables []string for _, row := range data { - if val, ok := row["tablename"]; ok { - tables = append(tables, fmt.Sprintf("%v", val)) + schema, okSchema := row["schemaname"] + name, okName := row["tablename"] + if okSchema && okName { + tables = append(tables, fmt.Sprintf("%v.%v", schema, name)) + continue + } + if okName { + tables = append(tables, fmt.Sprintf("%v", name)) } } return tables, nil diff --git a/internal/db/sqlite_impl.go b/internal/db/sqlite_impl.go index 681dea4..01daa1e 100644 --- a/internal/db/sqlite_impl.go +++ b/internal/db/sqlite_impl.go @@ -87,7 +87,11 @@ func (s *SQLiteDB) Query(query string) ([]map[string]interface{}, []string, erro val := values[i] b, ok := val.([]byte) if ok { - v = string(b) + if b == nil { + v = nil + } else { + v = string(b) + } } else { v = val }