🐛 fix(duckdb): 修复无主键结果无法安全编辑

- 为 DuckDB 查询结果和表预览补充隐藏 rowid 定位列,允许无主键表安全提交修改
- DataGrid 提交变更时仅将 rowid 用作定位条件,避免把隐藏定位列写回业务字段
- DuckDB ApplyChanges 对 duckdb-rowid 改用未加引号的 rowid 条件,修复更新和删除失效
- 补充前后端回归测试,覆盖 QueryEditor、DataViewer、rowLocator 与 ApplyChanges 链路
This commit is contained in:
Syngnat
2026-06-05 14:05:18 +08:00
parent ea1737ab8d
commit 2254b76232
9 changed files with 378 additions and 15 deletions

View File

@@ -24,7 +24,11 @@ import { splitSidebarQualifiedName } from '../utils/sidebarLocate';
import { normalizeSidebarViewName } from '../utils/sidebarMetadata';
import { SIDEBAR_SQL_EDITOR_DRAG_MIME, decodeSidebarSqlEditorDragPayload, hasSidebarSqlEditorDragPayload } from '../utils/sidebarSqlDrag';
import { resolveUniqueKeyGroupsFromIndexes } from './dataGridCopyInsert';
import { ORACLE_ROWID_LOCATOR_COLUMN, type EditRowLocator } from '../utils/rowLocator';
import {
DUCKDB_ROWID_LOCATOR_COLUMN,
ORACLE_ROWID_LOCATOR_COLUMN,
type EditRowLocator,
} from '../utils/rowLocator';
import { getQueryTabDraft, hasQueryTabDraft, setQueryTabDraft, setSQLFileTabDraft } from '../utils/sqlFileTabDrafts';
import {
getColumnDefinitionKey,
@@ -579,6 +583,10 @@ const buildQueryRowIDExpression = (dbType: string, sourceAlias?: string): string
`${sourceAlias ? `${sourceAlias}.` : ''}ROWID AS ${quoteIdentPart(dbType, ORACLE_ROWID_LOCATOR_COLUMN)}`
);
const buildDuckDBRowIDExpression = (dbType: string, sourceAlias?: string): string => (
`${sourceAlias ? `${sourceAlias}.` : ''}rowid AS ${quoteIdentPart(dbType, DUCKDB_ROWID_LOCATOR_COLUMN)}`
);
const escapeMetadataSqlLiteral = (raw: string): string => String(raw || '').replace(/'/g, "''");
const quoteSqlServerDbIdentifier = (raw: string): string => `[${String(raw || '').replace(/]/g, ']]')}]`;
@@ -1834,6 +1842,7 @@ const resolveQueryLocatorPlan = async ({
const appendExpressions: string[] = [];
const hiddenColumns: string[] = [];
let needsOracleRowIDExpression = false;
let needsDuckDBRowIDExpression = false;
const buildColumnLocator = (strategy: 'primary-key' | 'unique-key', locatorColumns: string[]): EditRowLocator => {
const valueColumns = locatorColumns.map((column, index) => {
@@ -1872,6 +1881,16 @@ const resolveQueryLocatorPlan = async ({
writableColumns,
readOnly: false,
};
} else if (String(dbType || '').trim().toLowerCase() === 'duckdb') {
needsDuckDBRowIDExpression = true;
plan.editLocator = {
strategy: 'duckdb-rowid',
columns: ['rowid'],
valueColumns: [DUCKDB_ROWID_LOCATOR_COLUMN],
hiddenColumns: [DUCKDB_ROWID_LOCATOR_COLUMN],
writableColumns,
readOnly: false,
};
} else {
const reason = !resIndexes?.success
? '无法加载唯一索引元数据,无法安全提交修改。'
@@ -1883,6 +1902,7 @@ const resolveQueryLocatorPlan = async ({
const executableAppendExpressions = [
...(needsOracleRowIDExpression ? [buildQueryRowIDExpression(dbType)] : []),
...(needsDuckDBRowIDExpression ? [buildDuckDBRowIDExpression(dbType)] : []),
...appendExpressions,
];