🐛 fix(db): 适配 schema/owner 限定名,修复 PG/金仓表不存在

- 表列表返回 schema.table/owner.table,避免 search_path 不一致导致 relation does not exist
  - 元数据/导入导出/提交变更统一解析限定名并正确引用
  - 前端查询与数据浏览支持限定名 quote
  - 单元格编辑态时间字段统一显示为 YYYY-MM-DD HH:mm:ss
  close #36
This commit is contained in:
杨国锋
2026-02-03 13:44:57 +08:00
parent 99c21f4fd4
commit aa7651d95c
10 changed files with 213 additions and 92 deletions

View File

@@ -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}`;