mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-21 16:34:21 +08:00
✨ feat(data-grid): 支持无主键表安全编辑
- 定位策略:新增主键、唯一索引和 Oracle ROWID 三类安全行定位能力 - 查询编辑器:简单单表 SELECT 自动补充隐藏定位列,复杂结果保持只读 - 表预览:无主键表可通过唯一索引或 Oracle ROWID 安全编辑 - 提交流程:移除无主键整行 WHERE fallback,隐藏定位列不参与展示和写入 - 后端保护:Oracle、MySQL、PostgreSQL 更新删除必须恰好影响 1 行 - 测试覆盖:补充 QueryEditor、DataViewer、DataGrid 和 ApplyChanges 相关用例 Refs #419
This commit is contained in:
64
frontend/src/utils/queryResultTable.ts
Normal file
64
frontend/src/utils/queryResultTable.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
export type QueryResultTableRef = {
|
||||
tableName: string;
|
||||
metadataDbName: string;
|
||||
metadataTableName: string;
|
||||
};
|
||||
|
||||
const stripIdentifierQuotes = (part: string): string => {
|
||||
const text = String(part || '').trim();
|
||||
if (!text) return '';
|
||||
if ((text.startsWith('`') && text.endsWith('`')) || (text.startsWith('"') && text.endsWith('"'))) {
|
||||
return text.slice(1, -1).trim();
|
||||
}
|
||||
if (text.startsWith('[') && text.endsWith(']')) {
|
||||
return text.slice(1, -1).trim();
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
const normalizeQualifiedName = (raw: string): string => (
|
||||
String(raw || '')
|
||||
.split('.')
|
||||
.map((part) => stripIdentifierQuotes(part.trim()))
|
||||
.filter(Boolean)
|
||||
.join('.')
|
||||
);
|
||||
|
||||
const isOracleLikeDialect = (dialect: string): boolean => {
|
||||
const normalized = String(dialect || '').trim().toLowerCase();
|
||||
return normalized === 'oracle' || normalized === 'dameng' || normalized === 'dm' || normalized === 'dm8';
|
||||
};
|
||||
|
||||
export const extractQueryResultTableRef = (
|
||||
sql: string,
|
||||
dialect: string,
|
||||
currentDb: string,
|
||||
): QueryResultTableRef | undefined => {
|
||||
const text = String(sql || '').trim();
|
||||
if (!text) return undefined;
|
||||
if (/\b(JOIN|UNION|INTERSECT|EXCEPT|MINUS)\b/i.test(text)) return undefined;
|
||||
if (/^\s*SELECT\s+DISTINCT\b/i.test(text)) return undefined;
|
||||
if (/\bGROUP\s+BY\b|\bHAVING\b/i.test(text)) return undefined;
|
||||
|
||||
const tableMatch = text.match(/^\s*SELECT\s+.+?\s+FROM\s+((?:[`"\[]?\w+[`"\]]?)(?:\s*\.\s*(?:[`"\[]?\w+[`"\]]?)){0,2})\s*(?:$|[\s;])/im);
|
||||
if (!tableMatch) return undefined;
|
||||
|
||||
const qualifiedName = normalizeQualifiedName(tableMatch[1]);
|
||||
if (!qualifiedName) return undefined;
|
||||
|
||||
const parts = qualifiedName.split('.').filter(Boolean);
|
||||
const metadataTableName = parts[parts.length - 1] || '';
|
||||
if (!metadataTableName) return undefined;
|
||||
|
||||
const owner = parts.length >= 2 ? parts[parts.length - 2] : '';
|
||||
const metadataDbName = owner || currentDb || '';
|
||||
const tableName = isOracleLikeDialect(dialect) && owner
|
||||
? `${owner}.${metadataTableName}`
|
||||
: metadataTableName;
|
||||
|
||||
return {
|
||||
tableName,
|
||||
metadataDbName,
|
||||
metadataTableName,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user