diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx
index 23f8a43a..67d747b8 100644
--- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx
+++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx
@@ -9509,6 +9509,87 @@ describe('QueryEditor external SQL save', () => {
expect(messageApi.warning).not.toHaveBeenCalled();
});
+ it('keeps Dameng USER_COL_COMMENTS queries read-only without injecting ROWID', async () => {
+ storeState.connections[0].config.type = 'dameng';
+ storeState.connections[0].config.database = 'APP';
+ const sql = `SELECT T.TABLE_NAME, T.COLUMN_NAME, T.COMMENTS
+FROM USER_COL_COMMENTS T
+WHERE T.TABLE_NAME = 'MEITUAN_COMMENT_INFO';`;
+ backendApp.DBGetColumns.mockResolvedValueOnce({ success: true, data: [] });
+ backendApp.DBGetIndexes.mockResolvedValueOnce({ success: true, data: [] });
+ backendApp.DBQueryMulti.mockResolvedValueOnce({
+ success: true,
+ data: [{
+ columns: ['TABLE_NAME', 'COLUMN_NAME', 'COMMENTS'],
+ rows: [{
+ TABLE_NAME: 'MEITUAN_COMMENT_INFO',
+ COLUMN_NAME: 'CONTENT',
+ COMMENTS: '评论内容',
+ }],
+ }],
+ });
+
+ let renderer: ReactTestRenderer;
+ await act(async () => {
+ renderer = create();
+ });
+
+ await act(async () => {
+ await findButton(renderer!, '运行').props.onClick();
+ });
+ await act(async () => {
+ await Promise.resolve();
+ await Promise.resolve();
+ });
+
+ const executedSql = String(backendApp.DBQueryMulti.mock.calls[0][2]);
+ expect(executedSql).toContain('FROM USER_COL_COMMENTS T');
+ expect(executedSql).not.toMatch(/\bROWID\b/i);
+ expect(dataGridState.latestProps?.editLocator).toMatchObject({ readOnly: true });
+ expect(dataGridState.latestProps?.readOnly).toBe(true);
+ });
+
+ it('keeps Dameng DBA_TAB_PRIVS queries read-only without injecting ROWID', async () => {
+ storeState.connections[0].config.type = 'dameng';
+ storeState.connections[0].config.database = 'APP';
+ const sql = `SELECT *
+FROM DBA_TAB_PRIVS
+WHERE GRANTEE = 'APPUSER';`;
+ backendApp.DBGetColumns.mockResolvedValueOnce({ success: true, data: [] });
+ backendApp.DBGetIndexes.mockResolvedValueOnce({ success: true, data: [] });
+ backendApp.DBQueryMulti.mockResolvedValueOnce({
+ success: true,
+ data: [{
+ columns: ['GRANTEE', 'OWNER', 'TABLE_NAME', 'PRIVILEGE'],
+ rows: [{
+ GRANTEE: 'APPUSER',
+ OWNER: 'APPUSER',
+ TABLE_NAME: 'MEITUAN_COMMENT_INFO',
+ PRIVILEGE: 'SELECT',
+ }],
+ }],
+ });
+
+ let renderer: ReactTestRenderer;
+ await act(async () => {
+ renderer = create();
+ });
+
+ await act(async () => {
+ await findButton(renderer!, '运行').props.onClick();
+ });
+ await act(async () => {
+ await Promise.resolve();
+ await Promise.resolve();
+ });
+
+ const executedSql = String(backendApp.DBQueryMulti.mock.calls[0][2]);
+ expect(executedSql).toContain('FROM DBA_TAB_PRIVS');
+ expect(executedSql).not.toMatch(/\bROWID\b/i);
+ expect(dataGridState.latestProps?.editLocator).toMatchObject({ readOnly: true });
+ expect(dataGridState.latestProps?.readOnly).toBe(true);
+ });
+
it('uses Oracle login user as default schema for unqualified query result metadata', async () => {
storeState.connections[0].config.type = 'oracle';
storeState.connections[0].config.user = 'dev';
diff --git a/frontend/src/components/queryEditor/QueryEditorHelpers.ts b/frontend/src/components/queryEditor/QueryEditorHelpers.ts
index 4935019f..d4a834b5 100644
--- a/frontend/src/components/queryEditor/QueryEditorHelpers.ts
+++ b/frontend/src/components/queryEditor/QueryEditorHelpers.ts
@@ -2771,6 +2771,10 @@ export const resolveQueryLocatorPlan = async ({
const tableColumns = resCols.data as ColumnDefinition[];
const tableColumnNames = tableColumns.map(getColumnDefinitionName).filter(Boolean);
+ if (tableColumnNames.length === 0) {
+ plan.editLocator = buildQueryReadOnlyLocator(translate('query_editor.message.read_only_system_metadata'));
+ return plan;
+ }
let executableStatement = statement;
if (isOracleLikeDialect(dbType) && selectInfo.selectsAll) {
const rewritten = rewriteOracleDuplicateSelectColumns(executableStatement, tableColumnNames);