🐛 fix(oracle): 修复存储包定义截断与斜杠误执行

- 对象树支持序列和存储包双击及右键查看定义
- Oracle package 同时读取 PACKAGE 与 PACKAGE BODY 并生成可编辑 SQL
- SQL 拆分器跳过 SQL*Plus 独立斜杠和注释分隔符
- 补充前后端 PL/SQL 拆分与定义查看回归测试
This commit is contained in:
Syngnat
2026-06-25 10:12:28 +08:00
parent 06a984f39d
commit 37ccaf7743
22 changed files with 905 additions and 19 deletions

View File

@@ -295,6 +295,130 @@ describe('DefinitionViewer object edit entry', () => {
expect(query).toContain('SELECT COUNT(*) INTO v_count FROM dual;');
});
it('opens an editable query tab for Oracle sequence definitions', async () => {
storeState.connections[0].config.type = 'oracle';
backendApp.DBQuery.mockResolvedValue({
success: true,
data: [{
SEQUENCE_OWNER: 'H2',
SEQUENCE_NAME: 'HWMS_PACK_SEQNO',
MIN_VALUE: 1,
MAX_VALUE: 999999999999,
INCREMENT_BY: 1,
CYCLE_FLAG: 'N',
ORDER_FLAG: 'N',
CACHE_SIZE: 20,
}],
});
let renderer: any;
await act(async () => {
renderer = create(renderWithI18n(createTab({
id: 'sequence-def-conn-1-H2-H2.HWMS_PACK_SEQNO',
title: 'Sequence: H2.HWMS_PACK_SEQNO',
type: 'sequence-def',
sequenceName: 'H2.HWMS_PACK_SEQNO',
viewName: undefined,
viewKind: undefined,
})));
await flushPromises();
});
expect(String(backendApp.DBQuery.mock.calls[0][2] || '')).toContain('FROM ALL_SEQUENCES');
const editorText = String(renderer.root.findAll((node: any) => node.props['data-editor'] === 'true')[0].children.join(''));
expect(editorText).toContain('CREATE SEQUENCE H2.HWMS_PACK_SEQNO');
expect(editorText).toContain('CACHE 20');
const button = renderer.root.findAll((node: any) => node.type === 'button' && findButtonText(node).includes('Edit object'))[0];
await act(async () => {
await button.props.onClick();
await flushPromises();
});
expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
type: 'query',
queryMode: 'object-edit',
query: expect.stringContaining('CREATE SEQUENCE H2.HWMS_PACK_SEQNO'),
}));
});
it('loads Oracle package specification and body definitions without dropping lines', async () => {
storeState.connections[0].config.type = 'oracle';
backendApp.DBQuery
.mockResolvedValueOnce({
success: true,
data: [
{ TEXT: 'PACKAGE pkg_order AS\n' },
{ TEXT: ' PROCEDURE sync_order(p_id IN NUMBER);\n' },
{ TEXT: 'END pkg_order;\n' },
],
})
.mockResolvedValueOnce({
success: true,
data: [
{ TEXT: 'PACKAGE BODY pkg_order AS\n' },
{ TEXT: ' PROCEDURE sync_order(p_id IN NUMBER) IS\n' },
{ TEXT: ' BEGIN\n' },
{ TEXT: ' NULL;\n' },
{ TEXT: ' END;\n' },
{ TEXT: 'END pkg_order;\n' },
],
});
let renderer: any;
await act(async () => {
renderer = create(renderWithI18n(createTab({
id: 'package-def-conn-1-H2-H2.PKG_ORDER',
title: 'Package: H2.PKG_ORDER',
type: 'package-def',
packageName: 'H2.PKG_ORDER',
viewName: undefined,
viewKind: undefined,
})));
await flushPromises();
});
expect(String(backendApp.DBQuery.mock.calls[0][2] || '')).toContain("TYPE = 'PACKAGE'");
expect(String(backendApp.DBQuery.mock.calls[1][2] || '')).toContain("TYPE = 'PACKAGE BODY'");
const editorText = String(renderer.root.findAll((node: any) => node.props['data-editor'] === 'true')[0].children.join(''));
expect(editorText).toContain('PACKAGE pkg_order AS');
expect(editorText).toContain('PROCEDURE sync_order(p_id IN NUMBER);');
expect(editorText).toContain('PACKAGE BODY pkg_order AS');
expect(editorText).toContain('NULL;');
backendApp.DBQuery
.mockResolvedValueOnce({
success: true,
data: [
{ TEXT: 'PACKAGE pkg_order AS\n' },
{ TEXT: ' PROCEDURE sync_order(p_id IN NUMBER);\n' },
{ TEXT: 'END pkg_order;\n' },
],
})
.mockResolvedValueOnce({
success: true,
data: [
{ TEXT: 'PACKAGE BODY pkg_order AS\n' },
{ TEXT: ' PROCEDURE sync_order(p_id IN NUMBER) IS\n' },
{ TEXT: ' BEGIN\n' },
{ TEXT: ' NULL;\n' },
{ TEXT: ' END sync_order;\n' },
{ TEXT: 'END pkg_order;\n' },
],
});
const button = renderer.root.findAll((node: any) => node.type === 'button' && findButtonText(node).includes('Edit object'))[0];
await act(async () => {
await button.props.onClick();
await flushPromises();
});
const editQuery = String(storeState.addTab.mock.calls[0][0].query || '');
expect(editQuery).toContain('CREATE OR REPLACE PACKAGE pkg_order AS');
expect(editQuery).toContain('/\nCREATE OR REPLACE PACKAGE BODY pkg_order AS');
expect(editQuery).toContain('END sync_order;');
});
it('reloads the latest object definition before opening object edit', async () => {
backendApp.DBQuery
.mockResolvedValueOnce({

View File

@@ -48,6 +48,17 @@ const isCommentOnlyDefinition = (definition: string): boolean => {
return lines.length > 0 && lines.every((line) => line.startsWith('--'));
};
const withCreateOrReplacePackageHeaders = (definition: string): string => {
const normalized = String(definition || '').replace(/\r\n/g, '\n').trim();
if (!normalized) return '';
return normalized
.split(/(?=^\s*PACKAGE(?:\s+BODY)?\b)/gim)
.map((part) => part.trim())
.filter(Boolean)
.map((part) => (/^\s*CREATE\b/i.test(part) ? part : `CREATE OR REPLACE ${part}`))
.join('\n/\n');
};
const buildEditableDefinitionSql = (
tab: TabData,
definition: string,
@@ -75,6 +86,18 @@ const buildEditableDefinitionSql = (
return `${header}CREATE OR REPLACE VIEW ${objectName} AS\n${ensureSqlStatementTerminator(normalizedDefinition)}`;
}
if (tab.type === 'sequence-def' && !/^\s*create\b/i.test(normalizedDefinition)) {
return `${header}${ensureSqlStatementTerminator(`CREATE SEQUENCE ${objectName}\n${normalizedDefinition}`)}`;
}
if (
tab.type === 'package-def'
&& !/^\s*create\b/i.test(normalizedDefinition)
&& /^\s*package\b/i.test(normalizedDefinition)
) {
return `${header}${withCreateOrReplacePackageHeaders(normalizedDefinition)}`;
}
if (
tab.type === 'routine-def'
&& !/^\s*create\b/i.test(normalizedDefinition)
@@ -109,6 +132,8 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
tab.eventName,
tab.routineName,
tab.routineType,
tab.sequenceName,
tab.packageName,
].map((item) => String(item || '')).join('||');
const escapeSQLLiteral = (raw: string): string => String(raw || '').replace(/'/g, "''");
@@ -320,6 +345,58 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
}
};
const buildShowSequenceQueries = (dialect: string, sequenceName: string, dbName: string): string[] => {
const { schema, name } = parseSchemaAndName(sequenceName);
const safeName = escapeSQLLiteral(name);
const safeDbName = escapeSQLLiteral(dbName);
const owner = schema ? escapeSQLLiteral(schema).toUpperCase() : (safeDbName ? safeDbName.toUpperCase() : '');
switch (dialect) {
case 'oracle':
case 'dm':
if (owner) {
return [`SELECT SEQUENCE_OWNER, SEQUENCE_NAME, MIN_VALUE, MAX_VALUE, INCREMENT_BY, CYCLE_FLAG, ORDER_FLAG, CACHE_SIZE, LAST_NUMBER FROM ALL_SEQUENCES WHERE SEQUENCE_OWNER = '${owner}' AND SEQUENCE_NAME = '${safeName.toUpperCase()}'`];
}
return [`SELECT SEQUENCE_NAME, MIN_VALUE, MAX_VALUE, INCREMENT_BY, CYCLE_FLAG, ORDER_FLAG, CACHE_SIZE, LAST_NUMBER FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '${safeName.toUpperCase()}'`];
case 'postgres':
case 'kingbase':
case 'highgo':
case 'vastbase':
case 'opengauss':
case 'gaussdb': {
const schemaRef = schema || 'public';
return [`SELECT sequence_schema, sequence_name, data_type, start_value, minimum_value, maximum_value, increment FROM information_schema.sequences WHERE sequence_schema = '${escapeSQLLiteral(schemaRef)}' AND sequence_name = '${safeName}' LIMIT 1`];
}
default:
return [`-- ${t('definition_viewer.editor.unsupported_sequence_definition')}`];
}
};
const buildShowPackageQueries = (dialect: string, packageName: string, dbName: string): string[] => {
const { schema, name } = parseSchemaAndName(packageName);
const safeName = escapeSQLLiteral(name);
const safeDbName = escapeSQLLiteral(dbName);
switch (dialect) {
case 'oracle':
case 'dm': {
const owner = schema ? escapeSQLLiteral(schema).toUpperCase() : (safeDbName ? safeDbName.toUpperCase() : '');
if (owner) {
return [
`SELECT TEXT FROM ALL_SOURCE WHERE OWNER = '${owner}' AND NAME = '${safeName.toUpperCase()}' AND TYPE = 'PACKAGE' ORDER BY LINE`,
`SELECT TEXT FROM ALL_SOURCE WHERE OWNER = '${owner}' AND NAME = '${safeName.toUpperCase()}' AND TYPE = 'PACKAGE BODY' ORDER BY LINE`,
];
}
return [
`SELECT TEXT FROM USER_SOURCE WHERE NAME = '${safeName.toUpperCase()}' AND TYPE = 'PACKAGE' ORDER BY LINE`,
`SELECT TEXT FROM USER_SOURCE WHERE NAME = '${safeName.toUpperCase()}' AND TYPE = 'PACKAGE BODY' ORDER BY LINE`,
];
}
default:
return [`-- ${t('definition_viewer.editor.unsupported_package_definition')}`];
}
};
const runQueryCandidates = async (
config: Record<string, any>,
dbName: string,
@@ -350,6 +427,35 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
return { success: false, data: [], message: lastMessage };
};
const runQueryCandidatesCollectAll = async (
config: Record<string, any>,
dbName: string,
queries: string[]
): Promise<{ success: boolean; data: any[]; message?: string }> => {
let lastMessage = '';
let hasSuccessfulQuery = false;
const data: any[] = [];
for (const query of queries) {
const sql = String(query || '').trim();
if (!sql) continue;
try {
const result = await DBQuery(buildRpcConnectionConfig(config) as any, dbName, sql);
if (!result.success || !Array.isArray(result.data)) {
lastMessage = result.message || lastMessage;
continue;
}
hasSuccessfulQuery = true;
data.push(...result.data);
} catch (error: any) {
lastMessage = error?.message || String(error);
}
}
if (hasSuccessfulQuery) {
return { success: true, data };
}
return { success: false, data: [], message: lastMessage };
};
const getVersionHint = async (config: Record<string, any>, dbName: string): Promise<string> => {
const candidates = [
`SELECT VERSION() AS version`,
@@ -511,6 +617,66 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
}
};
const buildSequenceDefinitionFromRow = (row: Record<string, any>): string => {
const sequenceName = String(getCaseInsensitiveRawValue(row, ['sequence_name']) || '').trim();
const owner = String(getCaseInsensitiveRawValue(row, ['sequence_owner', 'owner', 'sequence_schema']) || '').trim();
const name = owner && sequenceName ? `${owner}.${sequenceName}` : sequenceName;
if (!name) return JSON.stringify(row, null, 2);
const clauses: string[] = [];
const increment = getCaseInsensitiveRawValue(row, ['increment_by', 'increment']);
const minValue = getCaseInsensitiveRawValue(row, ['min_value', 'minimum_value']);
const maxValue = getCaseInsensitiveRawValue(row, ['max_value', 'maximum_value']);
const cacheSize = Number(getCaseInsensitiveRawValue(row, ['cache_size']));
const cycleFlag = String(getCaseInsensitiveRawValue(row, ['cycle_flag']) || '').trim().toUpperCase();
const orderFlag = String(getCaseInsensitiveRawValue(row, ['order_flag']) || '').trim().toUpperCase();
if (increment !== undefined && increment !== null && String(increment).trim() !== '') {
clauses.push(`INCREMENT BY ${increment}`);
}
if (minValue !== undefined && minValue !== null && String(minValue).trim() !== '') {
clauses.push(`MINVALUE ${minValue}`);
}
if (maxValue !== undefined && maxValue !== null && String(maxValue).trim() !== '') {
clauses.push(`MAXVALUE ${maxValue}`);
}
if (Number.isFinite(cacheSize)) {
clauses.push(cacheSize > 0 ? `CACHE ${cacheSize}` : 'NOCACHE');
}
if (cycleFlag) clauses.push(cycleFlag === 'Y' ? 'CYCLE' : 'NOCYCLE');
if (orderFlag) clauses.push(orderFlag === 'Y' ? 'ORDER' : 'NOORDER');
return [`CREATE SEQUENCE ${name}`, ...clauses.map((clause) => ` ${clause}`)].join('\n') + ';';
};
const extractSequenceDefinition = (dialect: string, data: any[]): string => {
if (!data || data.length === 0) return `-- ${t('definition_viewer.editor.sequence_definition_not_found')}`;
switch (dialect) {
case 'oracle':
case 'dm':
case 'postgres':
case 'kingbase':
case 'highgo':
case 'vastbase':
case 'opengauss':
case 'gaussdb':
return buildSequenceDefinitionFromRow(data[0] as Record<string, any>);
default:
return JSON.stringify(data[0], null, 2);
}
};
const extractPackageDefinition = (dialect: string, data: any[]): string => {
if (!data || data.length === 0) return `-- ${t('definition_viewer.editor.package_definition_not_found')}`;
switch (dialect) {
case 'oracle':
case 'dm':
return data.map(row => row.text || row.TEXT || Object.values(row)[0] || '').join('');
default:
return JSON.stringify(data[0], null, 2);
}
};
const loadDefinition = async (): Promise<{ success: boolean; definition?: string; error?: string }> => {
const conn = connections.find(c => c.id === tab.connectionId);
if (!conn) {
@@ -543,6 +709,22 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
queries = buildShowEventQueries(dialect, eventName, dbName);
extractFn = extractEventDefinition;
resolvedObjectLabel = t('definition_viewer.object.event');
} else if (tab.type === 'sequence-def') {
const sequenceName = tab.sequenceName || '';
if (!sequenceName) {
return { success: false, error: t('definition_viewer.error.sequence_name_empty') };
}
queries = buildShowSequenceQueries(dialect, sequenceName, dbName);
extractFn = extractSequenceDefinition;
resolvedObjectLabel = t('definition_viewer.object.sequence');
} else if (tab.type === 'package-def') {
const packageName = tab.packageName || '';
if (!packageName) {
return { success: false, error: t('definition_viewer.error.package_name_empty') };
}
queries = buildShowPackageQueries(dialect, packageName, dbName);
extractFn = extractPackageDefinition;
resolvedObjectLabel = t('definition_viewer.object.package');
} else {
const routineName = tab.routineName || '';
const routineType = tab.routineType || 'FUNCTION';
@@ -573,7 +755,9 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
ssh: conn.config.ssh || { host: '', port: 22, user: '', password: '', keyPath: '' }
};
const result = await runQueryCandidates(config, dbName, queries);
const result = tab.type === 'package-def'
? await runQueryCandidatesCollectAll(config, dbName, queries)
: await runQueryCandidates(config, dbName, queries);
if (result.success && Array.isArray(result.data) && result.data.length > 0) {
return { success: true, definition: extractFn(dialect, result.data) };
@@ -655,7 +839,7 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
return () => {
cancelled = true;
};
}, [tab.connectionId, tab.dbName, tab.viewName, tab.viewKind, tab.eventName, tab.routineName, tab.routineType, tab.type, connections, objectIdentityKey, t]);
}, [tab.connectionId, tab.dbName, tab.viewName, tab.viewKind, tab.eventName, tab.routineName, tab.routineType, tab.sequenceName, tab.packageName, tab.type, connections, objectIdentityKey, t]);
useEffect(() => () => {
isMountedRef.current = false;
@@ -663,13 +847,29 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
const objectLabel = tab.type === 'view-def'
? (tab.viewKind === 'materialized' ? t('definition_viewer.object.materialized_view') : t('definition_viewer.object.view'))
: (tab.type === 'event-def' ? t('definition_viewer.object.event') : t('definition_viewer.object.routine'));
: (tab.type === 'event-def'
? t('definition_viewer.object.event')
: (tab.type === 'sequence-def'
? t('definition_viewer.object.sequence')
: (tab.type === 'package-def'
? t('definition_viewer.object.package')
: t('definition_viewer.object.routine'))));
const objectName = tab.type === 'view-def'
? tab.viewName
: (tab.type === 'event-def' ? tab.eventName : tab.routineName);
: (tab.type === 'event-def'
? tab.eventName
: (tab.type === 'sequence-def'
? tab.sequenceName
: (tab.type === 'package-def' ? tab.packageName : tab.routineName)));
const loadingTip = tab.type === 'view-def'
? t('definition_viewer.loading.view_definition')
: (tab.type === 'event-def' ? t('definition_viewer.loading.event_definition') : t('definition_viewer.loading.routine_definition'));
: (tab.type === 'event-def'
? t('definition_viewer.loading.event_definition')
: (tab.type === 'sequence-def'
? t('definition_viewer.loading.sequence_definition')
: (tab.type === 'package-def'
? t('definition_viewer.loading.package_definition')
: t('definition_viewer.loading.routine_definition'))));
const normalizedObjectName = String(objectName || '').trim();
const displayedDefinition = loadedDefinitionKeyRef.current === objectIdentityKey ? definition : '';
const hasDefinition = String(displayedDefinition || '').trim() !== '';

View File

@@ -1636,7 +1636,11 @@ const Sidebar: React.FC<{
routineType
});
return;
} else if (node.type === 'sequence' || node.type === 'package') {
} else if (node.type === 'sequence') {
openSequenceDefinition(node);
return;
} else if (node.type === 'package') {
openPackageDefinition(node);
return;
} else if (node.type === 'jvm-mode') {
const { providerMode, id } = node.dataRef;
@@ -1844,6 +1848,8 @@ const Sidebar: React.FC<{
handleRebindSavedQuery,
openRoutineDefinition,
openEventDefinition,
openSequenceDefinition,
openPackageDefinition,
openEditRoutine,
openCreateRoutine,
handleDropRoutine,
@@ -2188,6 +2194,8 @@ const Sidebar: React.FC<{
openEditRoutine,
handleDropRoutine,
openEventDefinition,
openSequenceDefinition,
openPackageDefinition,
resolveMessagePublishTarget,
openMessagePublishModal,
openDesign,

View File

@@ -61,6 +61,8 @@ const getTabKindLabel = (tab: TabData): string => {
}
if (tab.type === 'event-def') return t('tab_manager.kind_badge.event');
if (tab.type === 'routine-def') return t('tab_manager.kind_badge.routine');
if (tab.type === 'sequence-def') return t('tab_manager.kind_badge.sequence');
if (tab.type === 'package-def') return t('tab_manager.kind_badge.package');
return t('tab_manager.kind_badge.fallback');
};
@@ -89,6 +91,8 @@ const getTabKindTooltipLabel = (tab: TabData): string => {
}
if (tab.type === 'event-def') return t('tab_manager.hover.kind.event');
if (tab.type === 'routine-def') return t('tab_manager.hover.kind.routine');
if (tab.type === 'sequence-def') return t('tab_manager.hover.kind.sequence');
if (tab.type === 'package-def') return t('tab_manager.hover.kind.package');
return t('tab_manager.hover.kind.fallback');
};
@@ -97,6 +101,8 @@ const getTabObjectLabel = (tab: TabData): string => {
if (tab.viewName) return tab.viewName;
if (tab.eventName) return tab.eventName;
if (tab.routineName) return tab.routineName;
if (tab.sequenceName) return tab.sequenceName;
if (tab.packageName) return tab.packageName;
if (tab.triggerName) return tab.triggerName;
if (tab.resourcePath) return tab.resourcePath;
if (tab.filePath) return tab.filePath;
@@ -409,7 +415,7 @@ const TabContent: React.FC<{ tab: TabData; isActive: boolean }> = React.memo(({
if (tab.type === 'trigger') {
return <TriggerViewer tab={tab} />;
}
if (tab.type === 'view-def' || tab.type === 'event-def' || tab.type === 'routine-def') {
if (tab.type === 'view-def' || tab.type === 'event-def' || tab.type === 'routine-def' || tab.type === 'sequence-def' || tab.type === 'package-def') {
return <DefinitionViewer tab={tab} />;
}
if (tab.type === 'table-overview') {

View File

@@ -165,6 +165,8 @@ export const buildSidebarLegacyNodeMenuItems = (
openEditRoutine,
handleDropRoutine,
openEventDefinition,
openSequenceDefinition,
openPackageDefinition,
resolveMessagePublishTarget,
openMessagePublishModal,
openDesign,
@@ -834,6 +836,36 @@ export const buildSidebarLegacyNodeMenuItems = (
]
},
];
} else if (node.type === 'sequence') {
return [
{
key: 'view-sequence-def',
label: t('sidebar.menu.view_object_definition'),
icon: <CodeOutlined />,
onClick: () => openSequenceDefinition(node)
},
{
key: 'copy-sequence-name',
label: t('sidebar.menu.copy_object_name'),
icon: <CopyOutlined />,
onClick: () => handleCopyTableName(node)
},
];
} else if (node.type === 'package') {
return [
{
key: 'view-package-def',
label: t('sidebar.menu.view_object_definition'),
icon: <CodeOutlined />,
onClick: () => openPackageDefinition(node)
},
{
key: 'copy-package-name',
label: t('sidebar.menu.copy_object_name'),
icon: <CopyOutlined />,
onClick: () => handleCopyTableName(node)
},
];
} else if (node.type === 'db-event') {
return [
{

View File

@@ -986,6 +986,30 @@ export const useSidebarObjectActions = ({
});
};
const openSequenceDefinition = (node: any) => {
const { sequenceName, dbName, id } = node.dataRef;
addTab({
id: `sequence-def-${id}-${dbName}-${sequenceName}`,
title: t('sidebar.tab.sequence_definition', { name: sequenceName }),
type: 'sequence-def',
connectionId: id,
dbName,
sequenceName,
});
};
const openPackageDefinition = (node: any) => {
const { packageName, dbName, id } = node.dataRef;
addTab({
id: `package-def-${id}-${dbName}-${packageName}`,
title: t('sidebar.tab.package_definition', { name: packageName }),
type: 'package-def',
connectionId: id,
dbName,
packageName,
});
};
const openEditRoutine = async (node: any) => {
const conn = node.dataRef;
const { routineName, routineType, dbName, id } = conn;
@@ -1222,6 +1246,8 @@ export const useSidebarObjectActions = ({
handleRebindSavedQuery,
openRoutineDefinition,
openEventDefinition,
openSequenceDefinition,
openPackageDefinition,
openEditRoutine,
openCreateRoutine,
handleDropRoutine,

View File

@@ -453,6 +453,8 @@ export interface TabData {
| "view-def"
| "event-def"
| "routine-def"
| "sequence-def"
| "package-def"
| "table-overview"
| "table-export"
| "jvm-overview"
@@ -480,6 +482,8 @@ export interface TabData {
eventName?: string; // Event name for MySQL event definition tabs
routineName?: string; // Routine name for function/procedure definition tabs
routineType?: string; // 'FUNCTION' or 'PROCEDURE'
sequenceName?: string; // Sequence name for sequence definition tabs
packageName?: string; // Package name for package definition tabs
schemaName?: string; // Schema / owner name for schema-grouped objects
sidebarLocateKey?: string; // Precise sidebar tree key for locating an object node
savedQueryId?: string; // Saved query identity for quick-save behavior

View File

@@ -1,6 +1,6 @@
import { splitQualifiedNameLast } from './qualifiedName';
export type SidebarLocateObjectGroup = 'tables' | 'views' | 'materializedViews' | 'triggers' | 'routines' | 'externalSqlFiles';
export type SidebarLocateObjectGroup = 'tables' | 'views' | 'materializedViews' | 'triggers' | 'routines' | 'sequences' | 'packages' | 'externalSqlFiles';
export type SidebarLocateDatabaseObjectGroup = Exclude<SidebarLocateObjectGroup, 'externalSqlFiles'>;
export interface SidebarLocateDatabaseObjectRequest {
@@ -57,6 +57,8 @@ export interface SidebarLocateTabLike {
triggerName?: string;
triggerTableName?: string;
routineName?: string;
sequenceName?: string;
packageName?: string;
schemaName?: string;
sidebarLocateKey?: string;
filePath?: string;
@@ -84,12 +86,16 @@ const inferObjectGroup = (detail: Record<string, unknown>, connectionId: string,
if (explicitGroup === 'materializedViews' || explicitGroup === 'materialized-view') return 'materializedViews';
if (explicitGroup === 'triggers' || explicitGroup === 'trigger') return 'triggers';
if (explicitGroup === 'routines' || explicitGroup === 'routine') return 'routines';
if (explicitGroup === 'sequences' || explicitGroup === 'sequence') return 'sequences';
if (explicitGroup === 'packages' || explicitGroup === 'package') return 'packages';
const explicitType = toTrimmedString(detail.objectType);
if (explicitType === 'view' || explicitType === 'views') return 'views';
if (explicitType === 'materialized' || explicitType === 'materialized-view') return 'materializedViews';
if (explicitType === 'trigger' || explicitType === 'triggers') return 'triggers';
if (explicitType === 'routine' || explicitType === 'routines') return 'routines';
if (explicitType === 'sequence' || explicitType === 'sequences') return 'sequences';
if (explicitType === 'package' || explicitType === 'packages') return 'packages';
const tabId = toTrimmedString(detail.tabId);
const dbNodeKey = `${connectionId}-${dbName}`;
@@ -97,6 +103,8 @@ const inferObjectGroup = (detail: Record<string, unknown>, connectionId: string,
if (tabId.startsWith(`${dbNodeKey}-view-`)) return 'views';
if (tabId.startsWith(`${dbNodeKey}-trigger-`)) return 'triggers';
if (tabId.startsWith(`${dbNodeKey}-routine-`) || tabId.startsWith(`routine-def-${connectionId}-${dbName}-`)) return 'routines';
if (tabId.startsWith(`${dbNodeKey}-sequence-`) || tabId.startsWith(`sequence-def-${connectionId}-${dbName}-`)) return 'sequences';
if (tabId.startsWith(`${dbNodeKey}-package-`) || tabId.startsWith(`package-def-${connectionId}-${dbName}-`)) return 'packages';
return 'tables';
};
@@ -117,7 +125,7 @@ export const normalizeSidebarLocateObjectRequest = (detail: unknown): SidebarLoc
const connectionId = toTrimmedString(raw.connectionId);
const dbName = toTrimmedString(raw.dbName);
const tableName = toTrimmedString(raw.tableName || raw.objectName || raw.viewName || raw.triggerName || raw.routineName);
const tableName = toTrimmedString(raw.tableName || raw.objectName || raw.viewName || raw.triggerName || raw.routineName || raw.sequenceName || raw.packageName);
if (!connectionId || !dbName || !tableName) {
return null;
@@ -155,8 +163,12 @@ export const normalizeSidebarLocateObjectRequestFromTab = (tab: SidebarLocateTab
? toTrimmedString(tab.triggerName || tab.tableName)
: tab.type === 'routine-def'
? toTrimmedString(tab.routineName || tab.tableName)
: toTrimmedString(tab.tableName || tab.viewName);
if (tab.type !== 'table' && tab.type !== 'view-def' && tab.type !== 'trigger' && tab.type !== 'routine-def') {
: tab.type === 'sequence-def'
? toTrimmedString(tab.sequenceName || tab.tableName)
: tab.type === 'package-def'
? toTrimmedString(tab.packageName || tab.tableName)
: toTrimmedString(tab.tableName || tab.viewName);
if (tab.type !== 'table' && tab.type !== 'view-def' && tab.type !== 'trigger' && tab.type !== 'routine-def' && tab.type !== 'sequence-def' && tab.type !== 'package-def') {
return null;
}
@@ -172,7 +184,11 @@ export const normalizeSidebarLocateObjectRequestFromTab = (tab: SidebarLocateTab
? 'triggers'
: (tab.type === 'routine-def'
? 'routines'
: (tab.objectType === 'materialized-view' ? 'materializedViews' : (tab.objectType === 'view' ? 'views' : undefined)))),
: (tab.type === 'sequence-def'
? 'sequences'
: (tab.type === 'package-def'
? 'packages'
: (tab.objectType === 'materialized-view' ? 'materializedViews' : (tab.objectType === 'view' ? 'views' : undefined)))))),
});
};

View File

@@ -175,6 +175,103 @@ describe('sqlStatementSelection', () => {
});
});
it('skips SQL*Plus slash delimiter comments after named Oracle procedure endings', () => {
const sql = [
'-- 修改函数/存储过程H2.cproc_tzhssr_order2sale_A1',
'-- 请确认语法兼容当前数据库后执行',
'CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(',
' p_sourceid IN VARCHAR2,',
' p_msg_out OUT NVARCHAR2',
') AS',
' v_saleno VARCHAR2(40);',
' v_ecnt NUMBER;',
'BEGIN',
' SELECT COUNT(*) INTO v_ecnt FROM dual;',
" p_msg_out := 'OK';",
'EXCEPTION',
' WHEN OTHERS THEN',
' p_msg_out := SQLERRM;',
'END cproc_tzhssr_order2sale_A1;',
'/ -- SQLPlus delimiter from PL/SQL tools',
'SELECT 1 FROM dual;',
].join('\n');
const ranges = findSqlStatementRanges(sql).map((range) => range.text);
expect(ranges).toEqual([
[
'-- 修改函数/存储过程H2.cproc_tzhssr_order2sale_A1',
'-- 请确认语法兼容当前数据库后执行',
'CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(',
' p_sourceid IN VARCHAR2,',
' p_msg_out OUT NVARCHAR2',
') AS',
' v_saleno VARCHAR2(40);',
' v_ecnt NUMBER;',
'BEGIN',
' SELECT COUNT(*) INTO v_ecnt FROM dual;',
" p_msg_out := 'OK';",
'EXCEPTION',
' WHEN OTHERS THEN',
' p_msg_out := SQLERRM;',
'END cproc_tzhssr_order2sale_A1;',
].join('\n'),
'SELECT 1 FROM dual',
]);
expect(resolveExecutableSql(sql, sql.indexOf('CREATE OR REPLACE'))).toEqual({
sql: ranges[0],
source: 'statement',
});
expect(resolveExecutableSql(sql, sql.indexOf('p_msg_out := SQLERRM'))).toEqual({
sql: ranges[0],
source: 'statement',
});
});
it('keeps Oracle PACKAGE specification and body definitions as complete executable statements', () => {
const sql = [
'CREATE OR REPLACE PACKAGE pkg_order AS',
' PROCEDURE sync_order(p_id IN NUMBER);',
'END pkg_order;',
'/',
'CREATE OR REPLACE PACKAGE BODY pkg_order AS',
' PROCEDURE sync_order(p_id IN NUMBER) IS',
' BEGIN',
' NULL;',
' END sync_order;',
'END pkg_order;',
'/ -- SQLPlus delimiter from PL/SQL tools',
'SELECT 1 FROM dual;',
].join('\n');
const ranges = findSqlStatementRanges(sql).map((range) => range.text);
expect(ranges).toEqual([
[
'CREATE OR REPLACE PACKAGE pkg_order AS',
' PROCEDURE sync_order(p_id IN NUMBER);',
'END pkg_order;',
].join('\n'),
[
'CREATE OR REPLACE PACKAGE BODY pkg_order AS',
' PROCEDURE sync_order(p_id IN NUMBER) IS',
' BEGIN',
' NULL;',
' END sync_order;',
'END pkg_order;',
].join('\n'),
'SELECT 1 FROM dual',
]);
expect(resolveExecutableSql(sql, sql.indexOf('PROCEDURE sync_order'))).toEqual({
sql: ranges[0],
source: 'statement',
});
expect(resolveExecutableSql(sql, sql.indexOf('NULL'))).toEqual({
sql: ranges[1],
source: 'statement',
});
});
it('does not treat a slash operator line as a SQL*Plus delimiter', () => {
const sql = 'SELECT 10\n/\n2 FROM dual;';

View File

@@ -75,6 +75,12 @@ const resolveStandaloneSqlSlashLineEnd = (text: string, index: number): number |
let lineEnd = index + 1;
while (lineEnd < text.length && text[lineEnd] !== '\n') {
if (text[lineEnd] === '-' && text[lineEnd + 1] === '-') {
while (lineEnd < text.length && text[lineEnd] !== '\n') {
lineEnd += 1;
}
return lineEnd;
}
if (!isHorizontalWhitespace(text[lineEnd])) {
return null;
}
@@ -116,7 +122,32 @@ const isCreateRoutineHeaderPrefix = (text: string): boolean => {
current = nextSqlSignificantTokenSpan(text, current.end);
}
return current.token === 'procedure' || current.token === 'function';
if (current.token === 'procedure' || current.token === 'function') {
return true;
}
if (current.token !== 'package') {
return false;
}
current = nextSqlSignificantTokenSpan(text, current.end);
return current.token === '' || current.token === 'body' || isSqlIdentifierStart(current.token[0] || '');
};
const isCreatePackageHeaderPrefix = (text: string): boolean => {
let current = nextSqlSignificantTokenSpan(text, 0);
if (current.token !== 'create') return false;
current = nextSqlSignificantTokenSpan(text, current.end);
if (current.token === 'or') {
current = nextSqlSignificantTokenSpan(text, current.end);
if (current.token !== 'replace') return false;
current = nextSqlSignificantTokenSpan(text, current.end);
}
while (['editionable', 'noneditionable'].includes(current.token)) {
current = nextSqlSignificantTokenSpan(text, current.end);
}
return current.token === 'package';
};
const shouldEnterPlsqlCreateRoutineBlock = (
@@ -290,7 +321,9 @@ export const findSqlStatementRanges = (sql: string): SqlStatementRange[] => {
justClosedPLSQLBlock = false;
} else if (plsqlDepth === 0 && shouldEnterPlsqlCreateRoutineBlock(text, statementStart, token, tokenEnd)) {
plsqlDepth++;
plsqlDeclareBeginSkips++;
if (!isCreatePackageHeaderPrefix(text.slice(statementStart, tokenEnd - token.length))) {
plsqlDeclareBeginSkips++;
}
justClosedPLSQLBlock = false;
} else if (token === 'end' && plsqlDepth > 0 && !isPlsqlControlEnd(text, tokenEnd)) {
plsqlDepth--;

View File

@@ -463,6 +463,12 @@ const buildCompactObjectTabTitle = (tab: TabData, translate: TabDisplayTranslate
if (tab.type === 'routine-def') {
return replaceTitleObjectLabel(tab.title, tab.routineName);
}
if (tab.type === 'sequence-def') {
return replaceTitleObjectLabel(tab.title, tab.sequenceName);
}
if (tab.type === 'package-def') {
return replaceTitleObjectLabel(tab.title, tab.packageName);
}
return tab.title;
};
@@ -479,6 +485,8 @@ export const getTabDisplayKindLabel = (tab: TabData): string => {
if (tab.type === 'view-def') return tab.viewKind === 'materialized' ? 'MV' : 'VIEW';
if (tab.type === 'event-def') return 'EVT';
if (tab.type === 'routine-def') return 'FUNC';
if (tab.type === 'sequence-def') return 'SEQ';
if (tab.type === 'package-def') return 'PKG';
return 'TAB';
};
@@ -488,6 +496,8 @@ const getTabRawObjectLabel = (tab: TabData, translate: TabDisplayTranslate = def
if (tab.viewName) return tab.viewName;
if (tab.eventName) return tab.eventName;
if (tab.routineName) return tab.routineName;
if (tab.sequenceName) return tab.sequenceName;
if (tab.packageName) return tab.packageName;
if (tab.triggerName) return tab.triggerName;
if (tab.resourcePath) return tab.resourcePath;
if (tab.filePath) return getFileNameFromPath(tab.filePath);

View File

@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"errors"
"reflect"
"strings"
"testing"
@@ -481,6 +482,59 @@ END;`
}
}
func TestDBQueryMultiKeepsOraclePackageSpecAndBodyTogether(t *testing.T) {
originalNewDatabaseFunc := newDatabaseFunc
t.Cleanup(func() {
newDatabaseFunc = originalNewDatabaseFunc
})
fakeDB := &fakeBatchWriteDB{}
newDatabaseFunc = func(dbType string) (db.Database, error) {
return fakeDB, nil
}
app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test"))
config := connection.ConnectionConfig{
Type: "oracle",
Host: "127.0.0.1",
Port: 1521,
User: "app",
}
query := `CREATE OR REPLACE PACKAGE pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER);
END pkg_order;
/
CREATE OR REPLACE PACKAGE BODY pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER) IS
BEGIN
NULL;
END sync_order;
END pkg_order;
/ -- SQLPlus delimiter from PL/SQL tools`
wantExecuted := []string{
`CREATE OR REPLACE PACKAGE pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER);
END pkg_order;`,
`CREATE OR REPLACE PACKAGE BODY pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER) IS
BEGIN
NULL;
END sync_order;
END pkg_order;`,
}
result := app.DBQueryMulti(config, "ORCLPDB1", query, "oracle-package-test")
if !result.Success {
t.Fatalf("expected DBQueryMulti success, got failure: %s", result.Message)
}
if fakeDB.execCalls != 2 || len(fakeDB.execQueries) != 2 {
t.Fatalf("expected two sequential exec calls, got execCalls=%d queries=%#v", fakeDB.execCalls, fakeDB.execQueries)
}
if !reflect.DeepEqual(fakeDB.execQueries, wantExecuted) {
t.Fatalf("expected package spec/body to stay intact, got %#v", fakeDB.execQueries)
}
}
var _ db.BatchWriteExecer = (*fakeBatchWriteDB)(nil)
var _ db.SessionExecerProvider = (*fakeBatchWriteDB)(nil)
var _ db.QueryMessageExecer = (*fakeBatchWriteDB)(nil)

View File

@@ -475,3 +475,52 @@ func TestStreamSQLFileSkipsOracleSqlPlusSlashDelimiter(t *testing.T) {
t.Fatalf("unexpected second statement: %q", statements[1])
}
}
func TestStreamSQLFileKeepsOraclePackageSpecAndBodyTogether(t *testing.T) {
input := strings.Join([]string{
"CREATE OR REPLACE PACKAGE pkg_order AS",
" PROCEDURE sync_order(p_id IN NUMBER);",
"END pkg_order;",
"/",
"CREATE OR REPLACE PACKAGE BODY pkg_order AS",
" PROCEDURE sync_order(p_id IN NUMBER) IS",
" BEGIN",
" NULL;",
" END sync_order;",
"END pkg_order;",
"/ -- SQLPlus delimiter from PL/SQL tools",
"SELECT 1 FROM dual;",
}, "\n")
var statements []string
count, err := streamSQLFile(&chunkedReader{data: []byte(input), step: 3}, func(index int, stmt string) error {
statements = append(statements, stmt)
return nil
})
if err != nil {
t.Fatalf("streamSQLFile returned error: %v", err)
}
if count != 3 || len(statements) != 3 {
t.Fatalf("expected 3 statements, got count=%d statements=%#v", count, statements)
}
if statements[0] != strings.Join([]string{
"CREATE OR REPLACE PACKAGE pkg_order AS",
" PROCEDURE sync_order(p_id IN NUMBER);",
"END pkg_order;",
}, "\n") {
t.Fatalf("unexpected package spec statement: %q", statements[0])
}
if statements[1] != strings.Join([]string{
"CREATE OR REPLACE PACKAGE BODY pkg_order AS",
" PROCEDURE sync_order(p_id IN NUMBER) IS",
" BEGIN",
" NULL;",
" END sync_order;",
"END pkg_order;",
}, "\n") {
t.Fatalf("unexpected package body statement: %q", statements[1])
}
if statements[2] != "SELECT 1 FROM dual" {
t.Fatalf("unexpected third statement: %q", statements[2])
}
}

View File

@@ -131,7 +131,9 @@ func splitSQLStatements(sql string) []string {
justClosedPLSQLBlock = false
} else if plsqlDepth == 0 && shouldEnterPLSQLCreateRoutineBlock(text, cur.String(), token, tokenEnd) {
plsqlDepth++
plsqlDeclareBeginSkips++
if !isCreatePackageHeaderPrefix(cur.String()) {
plsqlDeclareBeginSkips++
}
justClosedPLSQLBlock = false
} else if token == "end" && plsqlDepth > 0 && !isPLSQLControlEnd(text, tokenEnd) {
plsqlDepth--
@@ -262,6 +264,18 @@ func scanSQLStandaloneSlashLineSuffix(text string, pos int) (lineEnd int, standa
if text[i] == '\n' {
return i, true, true
}
if text[i] == '-' {
if i+1 >= len(text) {
return len(text), true, false
}
if text[i+1] == '-' {
lineEnd := scanSQLLineCommentEnd(text, i+2)
if lineEnd >= len(text) {
return lineEnd, true, false
}
return lineEnd, true, true
}
}
if !isSQLHorizontalWhitespace(text[i]) {
return 0, false, true
}
@@ -269,6 +283,15 @@ func scanSQLStandaloneSlashLineSuffix(text string, pos int) (lineEnd int, standa
return len(text), true, false
}
func scanSQLLineCommentEnd(text string, pos int) int {
for i := pos; i < len(text); i++ {
if text[i] == '\n' {
return i
}
}
return len(text)
}
func skipSQLWhitespaceAndComments(text string, pos int) int {
i := pos
for i < len(text) {
@@ -384,7 +407,36 @@ func isCreateRoutineHeaderPrefix(text string) bool {
currentToken, currentEnd = nextSQLSignificantTokenSpan(text, currentEnd)
}
return currentToken == "procedure" || currentToken == "function"
if currentToken == "procedure" || currentToken == "function" {
return true
}
if currentToken != "package" {
return false
}
currentToken, _ = nextSQLSignificantTokenSpan(text, currentEnd)
return currentToken == "" || currentToken == "body" || isSQLIdentifierStart(currentToken[0])
}
func isCreatePackageHeaderPrefix(text string) bool {
currentToken, currentEnd := nextSQLSignificantTokenSpan(text, 0)
if currentToken != "create" {
return false
}
currentToken, currentEnd = nextSQLSignificantTokenSpan(text, currentEnd)
if currentToken == "or" {
currentToken, currentEnd = nextSQLSignificantTokenSpan(text, currentEnd)
if currentToken != "replace" {
return false
}
currentToken, currentEnd = nextSQLSignificantTokenSpan(text, currentEnd)
}
for currentToken == "editionable" || currentToken == "noneditionable" {
currentToken, currentEnd = nextSQLSignificantTokenSpan(text, currentEnd)
}
return currentToken == "package"
}
func nextSQLSignificantTokenSpan(text string, pos int) (string, int) {

View File

@@ -148,7 +148,9 @@ func (s *sqlStreamSplitter) Feed(chunk []byte) []string {
s.closedPLSQL = false
} else if s.plsqlDepth == 0 && shouldEnterPLSQLCreateRoutineBlock(text, s.cur.String(), token, tokenEnd) {
s.plsqlDepth++
s.declareSkips++
if !isCreatePackageHeaderPrefix(s.cur.String()) {
s.declareSkips++
}
s.closedPLSQL = false
} else if token == "end" && s.plsqlDepth > 0 && !isPLSQLControlEnd(text, tokenEnd) {
s.plsqlDepth--
@@ -336,7 +338,7 @@ func isIncompleteSQLDollarTag(s string) bool {
func shouldDeferPLSQLKeywordInStream(text string, tokenStart int, tokenEnd int, token string) bool {
switch token {
case "begin", "declare", "end", "create", "or", "replace", "editionable", "noneditionable", "procedure", "function", "is", "as":
case "begin", "declare", "end", "create", "or", "replace", "editionable", "noneditionable", "procedure", "function", "package", "body", "is", "as":
default:
return false
}
@@ -361,7 +363,7 @@ func shouldDeferPLSQLKeywordPrefixInStream(text string, tokenStart int, tokenEnd
if tokenEnd < len(text) {
return false
}
for _, keyword := range []string{"begin", "declare", "end", "create", "or", "replace", "editionable", "noneditionable", "procedure", "function", "is", "as"} {
for _, keyword := range []string{"begin", "declare", "end", "create", "or", "replace", "editionable", "noneditionable", "procedure", "function", "package", "body", "is", "as"} {
if strings.HasPrefix(keyword, token) && token != keyword {
if tokenStart > 0 && isSQLIdentifierPart(text[tokenStart-1]) {
return false

View File

@@ -251,6 +251,83 @@ END;`,
}
}
func TestSplitSQLStatements_OracleCreateProcedureSkipsCommentedSqlPlusSlashDelimiter(t *testing.T) {
input := `-- 修改函数/存储过程H2.cproc_tzhssr_order2sale_A1
-- 请确认语法兼容当前数据库后执行
CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(
p_sourceid IN VARCHAR2,
p_saleno_out OUT VARCHAR2,
p_msg_out OUT NVARCHAR2
) AS
v_saleno VARCHAR2(40);
v_ecnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_ecnt FROM dual;
p_saleno_out := v_saleno;
p_msg_out := 'OK';
EXCEPTION
WHEN OTHERS THEN
p_msg_out := SQLERRM;
END cproc_tzhssr_order2sale_A1;
/ -- SQLPlus delimiter from PL/SQL tools
SELECT 1 FROM dual;`
got := splitSQLStatements(input)
want := []string{
`-- 修改函数/存储过程H2.cproc_tzhssr_order2sale_A1
-- 请确认语法兼容当前数据库后执行
CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(
p_sourceid IN VARCHAR2,
p_saleno_out OUT VARCHAR2,
p_msg_out OUT NVARCHAR2
) AS
v_saleno VARCHAR2(40);
v_ecnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_ecnt FROM dual;
p_saleno_out := v_saleno;
p_msg_out := 'OK';
EXCEPTION
WHEN OTHERS THEN
p_msg_out := SQLERRM;
END cproc_tzhssr_order2sale_A1;`,
"SELECT 1 FROM dual",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
}
}
func TestSplitSQLStatements_OraclePackageSpecAndBodyStayWhole(t *testing.T) {
input := `CREATE OR REPLACE PACKAGE pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER);
END pkg_order;
/
CREATE OR REPLACE PACKAGE BODY pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER) IS
BEGIN
NULL;
END sync_order;
END pkg_order;
/ -- SQLPlus delimiter from PL/SQL tools
SELECT 1 FROM dual;`
got := splitSQLStatements(input)
want := []string{
`CREATE OR REPLACE PACKAGE pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER);
END pkg_order;`,
`CREATE OR REPLACE PACKAGE BODY pkg_order AS
PROCEDURE sync_order(p_id IN NUMBER) IS
BEGIN
NULL;
END sync_order;
END pkg_order;`,
"SELECT 1 FROM dual",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
}
}
func TestSplitSQLStatements_DoesNotTreatSlashOperatorLineAsDelimiter(t *testing.T) {
input := "SELECT 10\n/\n2 FROM dual;"
got := splitSQLStatements(input)

View File

@@ -4677,7 +4677,9 @@
"definition_viewer.editor.metadata_fallback.name_label": "Name",
"definition_viewer.editor.metadata_fallback.type_label": "Typ",
"definition_viewer.editor.object_definition_not_found": "{{object}}definition nicht gefunden",
"definition_viewer.editor.package_definition_not_found": "Package definition not found",
"definition_viewer.editor.routine_definition_not_found": "Funktions-/Prozedurdefinition nicht gefunden",
"definition_viewer.editor.sequence_definition_not_found": "Sequence definition not found",
"definition_viewer.editor.sphinx.compat_queries_hint": "Mehrere Kompatibilitätsabfragen wurden ausgeführt. Ursache können Versionsbeschränkungen oder ein nicht unterstützter Objekttyp sein.",
"definition_viewer.editor.sphinx.empty_result": "Die aktuelle Sphinx-Instanz{{version}} hat keine {{object}}definition zurückgegeben.",
"definition_viewer.editor.sphinx.failed_message_label": "Zurückgegebene Fehlermeldung",
@@ -4686,7 +4688,9 @@
"definition_viewer.editor.sphinx.version_suffix": " (Version: {{version}})",
"definition_viewer.editor.unsupported_event_definition": "Dieser Datenbanktyp unterstützt das Anzeigen von Ereignisdefinitionen nicht",
"definition_viewer.editor.unsupported_object_definition": "Diese Objektdefinition wird nicht unterstützt",
"definition_viewer.editor.unsupported_package_definition": "This database type does not support viewing package definitions",
"definition_viewer.editor.unsupported_routine_definition": "Dieser Datenbanktyp unterstützt das Anzeigen von Funktions-/Prozedurdefinitionen nicht",
"definition_viewer.editor.unsupported_sequence_definition": "This database type does not support viewing sequence definitions",
"definition_viewer.editor.unsupported_sqlite_routine_definition": "SQLite unterstützt keine Verwaltung von Funktions-/Prozedurdefinitionen",
"definition_viewer.editor.unsupported_view_definition": "Dieser Datenbanktyp unterstützt das Anzeigen von Ansichtsdefinitionen nicht",
"definition_viewer.editor.view_definition_not_found": "Ansichtsdefinition nicht gefunden",
@@ -4695,16 +4699,22 @@
"definition_viewer.error.load_failed": "Laden fehlgeschlagen",
"definition_viewer.error.query_failed": "Definitionsabfrage fehlgeschlagen",
"definition_viewer.error.query_failed_detail": "Definitionsabfrage fehlgeschlagen: {{detail}}",
"definition_viewer.error.package_name_empty": "Package name is empty",
"definition_viewer.error.routine_name_empty": "Funktions-/Prozedurname ist leer",
"definition_viewer.error.sequence_name_empty": "Sequence name is empty",
"definition_viewer.error.view_name_empty": "Ansichtsname ist leer",
"definition_viewer.field.database": "Datenbank",
"definition_viewer.field.type": "Typ",
"definition_viewer.loading.event_definition": "Ereignisdefinition wird geladen...",
"definition_viewer.loading.package_definition": "Loading package definition...",
"definition_viewer.loading.routine_definition": "Funktions-/Prozedurdefinition wird geladen...",
"definition_viewer.loading.sequence_definition": "Loading sequence definition...",
"definition_viewer.loading.view_definition": "Ansichtsdefinition wird geladen...",
"definition_viewer.object.event": "Ereignis",
"definition_viewer.object.materialized_view": "Materialisierte Ansicht",
"definition_viewer.object.package": "Package",
"definition_viewer.object.routine": "Funktion/Prozedur",
"definition_viewer.object.sequence": "Sequence",
"definition_viewer.object.view": "Ansicht",
"definition_viewer.warning.refresh_latest_failed": "Neueste Definition konnte nicht aktualisiert werden",
"dev.perf_data_grid.columns": "Spalten",
@@ -7019,10 +7029,12 @@
"sidebar.tab.new_query": "Neue Abfrage",
"sidebar.tab.new_query_database": "Neue Abfrage ({{database}})",
"sidebar.tab.new_table": "Neue Tabelle - {{database}}",
"sidebar.tab.package_definition": "Package: {{name}}",
"sidebar.tab.recent_query": "Letzte Abfrage",
"sidebar.tab.redis_command": "Befehl - {{database}}",
"sidebar.tab.redis_monitor": "Monitoring - {{database}}",
"sidebar.tab.routine_definition": "{{type}}: {{name}}",
"sidebar.tab.sequence_definition": "Sequence: {{name}}",
"sidebar.tab.table_overview": "Tabellenübersicht - {{database}}{{schema}}",
"sidebar.tab.table_structure": "Tabellenstruktur ({{table}})",
"sidebar.tab.trigger": "Trigger: {{name}}",
@@ -7280,11 +7292,13 @@
"tab_manager.hover.kind.jvm_overview": "JVM-Übersicht",
"tab_manager.hover.kind.jvm_resource": "JVM-Ressource",
"tab_manager.hover.kind.materialized_view": "Materialisierte Ansicht",
"tab_manager.hover.kind.package": "Package",
"tab_manager.hover.kind.query": "SQL-Abfrage",
"tab_manager.hover.kind.redis_command": "Redis-Befehl",
"tab_manager.hover.kind.redis_keys": "Redis Key",
"tab_manager.hover.kind.redis_monitor": "Redis-Monitor",
"tab_manager.hover.kind.routine": "Funktion / Prozedur",
"tab_manager.hover.kind.sequence": "Sequence",
"tab_manager.hover.kind.sql_analysis": "SQL-Analyse-Workbench",
"tab_manager.hover.kind.table": "Tabellendaten",
"tab_manager.hover.kind.table_export": "Export-Workbench",
@@ -7300,9 +7314,11 @@
"tab_manager.kind_badge.fallback": "Tab",
"tab_manager.kind_badge.jvm": "JVM",
"tab_manager.kind_badge.materialized_view": "MV",
"tab_manager.kind_badge.package": "Pkg",
"tab_manager.kind_badge.query": "SQL",
"tab_manager.kind_badge.redis": "Redis",
"tab_manager.kind_badge.routine": "Funktion",
"tab_manager.kind_badge.sequence": "Seq",
"tab_manager.kind_badge.sql_analysis": "Analyse",
"tab_manager.kind_badge.table": "Tabelle",
"tab_manager.kind_badge.table_export": "Export",

View File

@@ -4677,7 +4677,9 @@
"definition_viewer.editor.metadata_fallback.name_label": "Name",
"definition_viewer.editor.metadata_fallback.type_label": "Type",
"definition_viewer.editor.object_definition_not_found": "{{object}} definition not found",
"definition_viewer.editor.package_definition_not_found": "Package definition not found",
"definition_viewer.editor.routine_definition_not_found": "Function/procedure definition not found",
"definition_viewer.editor.sequence_definition_not_found": "Sequence definition not found",
"definition_viewer.editor.sphinx.compat_queries_hint": "Multiple compatibility queries were executed. This may be due to version capability limits or an unsupported object type.",
"definition_viewer.editor.sphinx.empty_result": "Current Sphinx instance{{version}} returned no {{object}} definition.",
"definition_viewer.editor.sphinx.failed_message_label": "Returned failure message",
@@ -4686,7 +4688,9 @@
"definition_viewer.editor.sphinx.version_suffix": " (version: {{version}})",
"definition_viewer.editor.unsupported_event_definition": "This database type does not support viewing event definitions",
"definition_viewer.editor.unsupported_object_definition": "This object definition is not supported",
"definition_viewer.editor.unsupported_package_definition": "This database type does not support viewing package definitions",
"definition_viewer.editor.unsupported_routine_definition": "This database type does not support viewing function/procedure definitions",
"definition_viewer.editor.unsupported_sequence_definition": "This database type does not support viewing sequence definitions",
"definition_viewer.editor.unsupported_sqlite_routine_definition": "SQLite does not support function/procedure definition management",
"definition_viewer.editor.unsupported_view_definition": "This database type does not support viewing view definitions",
"definition_viewer.editor.view_definition_not_found": "View definition not found",
@@ -4695,16 +4699,22 @@
"definition_viewer.error.load_failed": "Load failed",
"definition_viewer.error.query_failed": "Failed to query definition",
"definition_viewer.error.query_failed_detail": "Failed to query definition: {{detail}}",
"definition_viewer.error.package_name_empty": "Package name is empty",
"definition_viewer.error.routine_name_empty": "Function/procedure name is empty",
"definition_viewer.error.sequence_name_empty": "Sequence name is empty",
"definition_viewer.error.view_name_empty": "View name is empty",
"definition_viewer.field.database": "Database",
"definition_viewer.field.type": "Type",
"definition_viewer.loading.event_definition": "Loading event definition...",
"definition_viewer.loading.package_definition": "Loading package definition...",
"definition_viewer.loading.routine_definition": "Loading function/procedure definition...",
"definition_viewer.loading.sequence_definition": "Loading sequence definition...",
"definition_viewer.loading.view_definition": "Loading view definition...",
"definition_viewer.object.event": "Event",
"definition_viewer.object.materialized_view": "Materialized view",
"definition_viewer.object.package": "Package",
"definition_viewer.object.routine": "Function/procedure",
"definition_viewer.object.sequence": "Sequence",
"definition_viewer.object.view": "View",
"definition_viewer.warning.refresh_latest_failed": "Failed to refresh the latest definition",
"dev.perf_data_grid.columns": "Columns",
@@ -7019,10 +7029,12 @@
"sidebar.tab.new_query": "New query",
"sidebar.tab.new_query_database": "New query ({{database}})",
"sidebar.tab.new_table": "New table - {{database}}",
"sidebar.tab.package_definition": "Package: {{name}}",
"sidebar.tab.recent_query": "Recent query",
"sidebar.tab.redis_command": "Command - {{database}}",
"sidebar.tab.redis_monitor": "Monitor - {{database}}",
"sidebar.tab.routine_definition": "{{type}}: {{name}}",
"sidebar.tab.sequence_definition": "Sequence: {{name}}",
"sidebar.tab.table_overview": "Table overview - {{database}}{{schema}}",
"sidebar.tab.table_structure": "Table structure ({{table}})",
"sidebar.tab.trigger": "Trigger: {{name}}",
@@ -7280,11 +7292,13 @@
"tab_manager.hover.kind.jvm_overview": "JVM overview",
"tab_manager.hover.kind.jvm_resource": "JVM resource",
"tab_manager.hover.kind.materialized_view": "Materialized view",
"tab_manager.hover.kind.package": "Package",
"tab_manager.hover.kind.query": "SQL query",
"tab_manager.hover.kind.redis_command": "Redis command",
"tab_manager.hover.kind.redis_keys": "Redis Key",
"tab_manager.hover.kind.redis_monitor": "Redis monitor",
"tab_manager.hover.kind.routine": "Function / procedure",
"tab_manager.hover.kind.sequence": "Sequence",
"tab_manager.hover.kind.sql_analysis": "SQL analysis workbench",
"tab_manager.hover.kind.table": "Table data",
"tab_manager.hover.kind.table_export": "Export workbench",
@@ -7300,9 +7314,11 @@
"tab_manager.kind_badge.fallback": "Tab",
"tab_manager.kind_badge.jvm": "JVM",
"tab_manager.kind_badge.materialized_view": "MV",
"tab_manager.kind_badge.package": "Pkg",
"tab_manager.kind_badge.query": "SQL",
"tab_manager.kind_badge.redis": "Redis",
"tab_manager.kind_badge.routine": "Func",
"tab_manager.kind_badge.sequence": "Seq",
"tab_manager.kind_badge.sql_analysis": "Analyze",
"tab_manager.kind_badge.table": "Table",
"tab_manager.kind_badge.table_export": "Export",

View File

@@ -4677,7 +4677,9 @@
"definition_viewer.editor.metadata_fallback.name_label": "名前",
"definition_viewer.editor.metadata_fallback.type_label": "種類",
"definition_viewer.editor.object_definition_not_found": "{{object}}定義が見つかりません",
"definition_viewer.editor.package_definition_not_found": "Package definition not found",
"definition_viewer.editor.routine_definition_not_found": "関数/ストアドプロシージャ定義が見つかりません",
"definition_viewer.editor.sequence_definition_not_found": "Sequence definition not found",
"definition_viewer.editor.sphinx.compat_queries_hint": "複数の互換クエリを実行しました。バージョン機能の制限、または未対応のオブジェクト種類が原因の可能性があります。",
"definition_viewer.editor.sphinx.empty_result": "現在の Sphinx インスタンス{{version}}は{{object}}定義を返しませんでした。",
"definition_viewer.editor.sphinx.failed_message_label": "返された失敗メッセージ",
@@ -4686,7 +4688,9 @@
"definition_viewer.editor.sphinx.version_suffix": "(バージョン: {{version}}",
"definition_viewer.editor.unsupported_event_definition": "このデータベース種類ではイベント定義の表示はサポートされていません",
"definition_viewer.editor.unsupported_object_definition": "このオブジェクト定義の表示はサポートされていません",
"definition_viewer.editor.unsupported_package_definition": "This database type does not support viewing package definitions",
"definition_viewer.editor.unsupported_routine_definition": "このデータベース種類では関数/ストアドプロシージャ定義の表示はサポートされていません",
"definition_viewer.editor.unsupported_sequence_definition": "This database type does not support viewing sequence definitions",
"definition_viewer.editor.unsupported_sqlite_routine_definition": "SQLite は関数/ストアドプロシージャ定義の管理をサポートしていません",
"definition_viewer.editor.unsupported_view_definition": "このデータベース種類ではビュー定義の表示はサポートされていません",
"definition_viewer.editor.view_definition_not_found": "ビュー定義が見つかりません",
@@ -4695,16 +4699,22 @@
"definition_viewer.error.load_failed": "読み込みに失敗しました",
"definition_viewer.error.query_failed": "定義の取得に失敗しました",
"definition_viewer.error.query_failed_detail": "定義の取得に失敗しました: {{detail}}",
"definition_viewer.error.package_name_empty": "Package name is empty",
"definition_viewer.error.routine_name_empty": "関数/ストアドプロシージャ名が空です",
"definition_viewer.error.sequence_name_empty": "Sequence name is empty",
"definition_viewer.error.view_name_empty": "ビュー名が空です",
"definition_viewer.field.database": "データベース",
"definition_viewer.field.type": "種類",
"definition_viewer.loading.event_definition": "イベント定義を読み込み中...",
"definition_viewer.loading.package_definition": "Loading package definition...",
"definition_viewer.loading.routine_definition": "関数/ストアドプロシージャ定義を読み込み中...",
"definition_viewer.loading.sequence_definition": "Loading sequence definition...",
"definition_viewer.loading.view_definition": "ビュー定義を読み込み中...",
"definition_viewer.object.event": "イベント",
"definition_viewer.object.materialized_view": "マテリアライズドビュー",
"definition_viewer.object.package": "Package",
"definition_viewer.object.routine": "関数/ストアドプロシージャ",
"definition_viewer.object.sequence": "Sequence",
"definition_viewer.object.view": "ビュー",
"definition_viewer.warning.refresh_latest_failed": "最新の定義を更新できませんでした",
"dev.perf_data_grid.columns": "列数",
@@ -7019,10 +7029,12 @@
"sidebar.tab.new_query": "新規クエリ",
"sidebar.tab.new_query_database": "新規クエリ({{database}}",
"sidebar.tab.new_table": "新規テーブル - {{database}}",
"sidebar.tab.package_definition": "Package: {{name}}",
"sidebar.tab.recent_query": "最近のクエリ",
"sidebar.tab.redis_command": "コマンド - {{database}}",
"sidebar.tab.redis_monitor": "監視 - {{database}}",
"sidebar.tab.routine_definition": "{{type}}: {{name}}",
"sidebar.tab.sequence_definition": "Sequence: {{name}}",
"sidebar.tab.table_overview": "テーブル概要 - {{database}}{{schema}}",
"sidebar.tab.table_structure": "テーブル構造({{table}}",
"sidebar.tab.trigger": "トリガー: {{name}}",
@@ -7280,11 +7292,13 @@
"tab_manager.hover.kind.jvm_overview": "JVM 概要",
"tab_manager.hover.kind.jvm_resource": "JVM リソース",
"tab_manager.hover.kind.materialized_view": "マテリアライズドビュー",
"tab_manager.hover.kind.package": "Package",
"tab_manager.hover.kind.query": "SQL クエリ",
"tab_manager.hover.kind.redis_command": "Redis コマンド",
"tab_manager.hover.kind.redis_keys": "Redis Key",
"tab_manager.hover.kind.redis_monitor": "Redis 監視",
"tab_manager.hover.kind.routine": "関数 / プロシージャ",
"tab_manager.hover.kind.sequence": "Sequence",
"tab_manager.hover.kind.sql_analysis": "SQL 分析ワークベンチ",
"tab_manager.hover.kind.table": "テーブルデータ",
"tab_manager.hover.kind.table_export": "エクスポートワークベンチ",
@@ -7300,9 +7314,11 @@
"tab_manager.kind_badge.fallback": "タブ",
"tab_manager.kind_badge.jvm": "JVM",
"tab_manager.kind_badge.materialized_view": "MV",
"tab_manager.kind_badge.package": "Pkg",
"tab_manager.kind_badge.query": "SQL",
"tab_manager.kind_badge.redis": "Redis",
"tab_manager.kind_badge.routine": "関数",
"tab_manager.kind_badge.sequence": "Seq",
"tab_manager.kind_badge.sql_analysis": "分析",
"tab_manager.kind_badge.table": "表",
"tab_manager.kind_badge.table_export": "エクスポート",

View File

@@ -4677,7 +4677,9 @@
"definition_viewer.editor.metadata_fallback.name_label": "Имя",
"definition_viewer.editor.metadata_fallback.type_label": "Тип",
"definition_viewer.editor.object_definition_not_found": "Определение {{object}} не найдено",
"definition_viewer.editor.package_definition_not_found": "Package definition not found",
"definition_viewer.editor.routine_definition_not_found": "Определение функции/процедуры не найдено",
"definition_viewer.editor.sequence_definition_not_found": "Sequence definition not found",
"definition_viewer.editor.sphinx.compat_queries_hint": "Было выполнено несколько совместимых запросов. Причиной могут быть ограничения версии или неподдерживаемый тип объекта.",
"definition_viewer.editor.sphinx.empty_result": "Текущий экземпляр Sphinx{{version}} не вернул определение {{object}}.",
"definition_viewer.editor.sphinx.failed_message_label": "Возвращенное сообщение об ошибке",
@@ -4686,7 +4688,9 @@
"definition_viewer.editor.sphinx.version_suffix": " (версия: {{version}})",
"definition_viewer.editor.unsupported_event_definition": "Этот тип базы данных не поддерживает просмотр определений событий",
"definition_viewer.editor.unsupported_object_definition": "Просмотр определения этого объекта не поддерживается",
"definition_viewer.editor.unsupported_package_definition": "This database type does not support viewing package definitions",
"definition_viewer.editor.unsupported_routine_definition": "Этот тип базы данных не поддерживает просмотр определений функций/процедур",
"definition_viewer.editor.unsupported_sequence_definition": "This database type does not support viewing sequence definitions",
"definition_viewer.editor.unsupported_sqlite_routine_definition": "SQLite не поддерживает управление определениями функций/процедур",
"definition_viewer.editor.unsupported_view_definition": "Этот тип базы данных не поддерживает просмотр определений представлений",
"definition_viewer.editor.view_definition_not_found": "Определение представления не найдено",
@@ -4695,16 +4699,22 @@
"definition_viewer.error.load_failed": "Не удалось загрузить",
"definition_viewer.error.query_failed": "Не удалось запросить определение",
"definition_viewer.error.query_failed_detail": "Не удалось запросить определение: {{detail}}",
"definition_viewer.error.package_name_empty": "Package name is empty",
"definition_viewer.error.routine_name_empty": "Имя функции/процедуры пустое",
"definition_viewer.error.sequence_name_empty": "Sequence name is empty",
"definition_viewer.error.view_name_empty": "Имя представления пустое",
"definition_viewer.field.database": "База данных",
"definition_viewer.field.type": "Тип",
"definition_viewer.loading.event_definition": "Загрузка определения события...",
"definition_viewer.loading.package_definition": "Loading package definition...",
"definition_viewer.loading.routine_definition": "Загрузка определения функции/процедуры...",
"definition_viewer.loading.sequence_definition": "Loading sequence definition...",
"definition_viewer.loading.view_definition": "Загрузка определения представления...",
"definition_viewer.object.event": "Событие",
"definition_viewer.object.materialized_view": "Материализованное представление",
"definition_viewer.object.package": "Package",
"definition_viewer.object.routine": "Функция/процедура",
"definition_viewer.object.sequence": "Sequence",
"definition_viewer.object.view": "Представление",
"definition_viewer.warning.refresh_latest_failed": "Не удалось обновить последнее определение",
"dev.perf_data_grid.columns": "Столбцы",
@@ -7019,10 +7029,12 @@
"sidebar.tab.new_query": "Новый запрос",
"sidebar.tab.new_query_database": "Новый запрос ({{database}})",
"sidebar.tab.new_table": "Новая таблица - {{database}}",
"sidebar.tab.package_definition": "Package: {{name}}",
"sidebar.tab.recent_query": "Недавний запрос",
"sidebar.tab.redis_command": "Команда - {{database}}",
"sidebar.tab.redis_monitor": "Мониторинг - {{database}}",
"sidebar.tab.routine_definition": "{{type}}: {{name}}",
"sidebar.tab.sequence_definition": "Sequence: {{name}}",
"sidebar.tab.table_overview": "Обзор таблицы - {{database}}{{schema}}",
"sidebar.tab.table_structure": "Структура таблицы ({{table}})",
"sidebar.tab.trigger": "Триггер: {{name}}",
@@ -7280,11 +7292,13 @@
"tab_manager.hover.kind.jvm_overview": "Обзор JVM",
"tab_manager.hover.kind.jvm_resource": "Ресурс JVM",
"tab_manager.hover.kind.materialized_view": "Материализованное представление",
"tab_manager.hover.kind.package": "Package",
"tab_manager.hover.kind.query": "SQL-запрос",
"tab_manager.hover.kind.redis_command": "Команда Redis",
"tab_manager.hover.kind.redis_keys": "Redis Key",
"tab_manager.hover.kind.redis_monitor": "Монитор Redis",
"tab_manager.hover.kind.routine": "Функция / процедура",
"tab_manager.hover.kind.sequence": "Sequence",
"tab_manager.hover.kind.sql_analysis": "Рабочая область анализа SQL",
"tab_manager.hover.kind.table": "Данные таблицы",
"tab_manager.hover.kind.table_export": "Рабочая область экспорта",
@@ -7300,9 +7314,11 @@
"tab_manager.kind_badge.fallback": "Вкладка",
"tab_manager.kind_badge.jvm": "JVM",
"tab_manager.kind_badge.materialized_view": "MV",
"tab_manager.kind_badge.package": "Pkg",
"tab_manager.kind_badge.query": "SQL",
"tab_manager.kind_badge.redis": "Redis",
"tab_manager.kind_badge.routine": "Функция",
"tab_manager.kind_badge.sequence": "Seq",
"tab_manager.kind_badge.sql_analysis": "Анализ",
"tab_manager.kind_badge.table": "Таблица",
"tab_manager.kind_badge.table_export": "Экспорт",

View File

@@ -4677,7 +4677,9 @@
"definition_viewer.editor.metadata_fallback.name_label": "名称",
"definition_viewer.editor.metadata_fallback.type_label": "类型",
"definition_viewer.editor.object_definition_not_found": "未找到{{object}}定义",
"definition_viewer.editor.package_definition_not_found": "未找到存储包定义",
"definition_viewer.editor.routine_definition_not_found": "未找到函数/存储过程定义",
"definition_viewer.editor.sequence_definition_not_found": "未找到序列定义",
"definition_viewer.editor.sphinx.compat_queries_hint": "已执行多套兼容查询,可能是版本能力限制或对象类型不支持。",
"definition_viewer.editor.sphinx.empty_result": "当前 Sphinx 实例{{version}}未返回{{object}}定义。",
"definition_viewer.editor.sphinx.failed_message_label": "返回失败信息",
@@ -4686,7 +4688,9 @@
"definition_viewer.editor.sphinx.version_suffix": "(版本: {{version}}",
"definition_viewer.editor.unsupported_event_definition": "暂不支持该数据库类型的事件定义查看",
"definition_viewer.editor.unsupported_object_definition": "暂不支持该对象定义查看",
"definition_viewer.editor.unsupported_package_definition": "暂不支持该数据库类型的存储包定义查看",
"definition_viewer.editor.unsupported_routine_definition": "暂不支持该数据库类型的函数/存储过程定义查看",
"definition_viewer.editor.unsupported_sequence_definition": "暂不支持该数据库类型的序列定义查看",
"definition_viewer.editor.unsupported_sqlite_routine_definition": "SQLite 不支持函数/存储过程定义管理",
"definition_viewer.editor.unsupported_view_definition": "暂不支持该数据库类型的视图定义查看",
"definition_viewer.editor.view_definition_not_found": "未找到视图定义",
@@ -4695,16 +4699,22 @@
"definition_viewer.error.load_failed": "加载失败",
"definition_viewer.error.query_failed": "查询定义失败",
"definition_viewer.error.query_failed_detail": "查询定义失败:{{detail}}",
"definition_viewer.error.package_name_empty": "存储包名称为空",
"definition_viewer.error.routine_name_empty": "函数/存储过程名称为空",
"definition_viewer.error.sequence_name_empty": "序列名称为空",
"definition_viewer.error.view_name_empty": "视图名称为空",
"definition_viewer.field.database": "数据库",
"definition_viewer.field.type": "类型",
"definition_viewer.loading.event_definition": "加载事件定义...",
"definition_viewer.loading.package_definition": "加载存储包定义...",
"definition_viewer.loading.routine_definition": "加载函数/存储过程定义...",
"definition_viewer.loading.sequence_definition": "加载序列定义...",
"definition_viewer.loading.view_definition": "加载视图定义...",
"definition_viewer.object.event": "事件",
"definition_viewer.object.materialized_view": "物化视图",
"definition_viewer.object.package": "存储包",
"definition_viewer.object.routine": "函数/存储过程",
"definition_viewer.object.sequence": "序列",
"definition_viewer.object.view": "视图",
"definition_viewer.warning.refresh_latest_failed": "刷新最新定义失败",
"dev.perf_data_grid.columns": "列数",
@@ -7019,10 +7029,12 @@
"sidebar.tab.new_query": "新建查询",
"sidebar.tab.new_query_database": "新建查询({{database}}",
"sidebar.tab.new_table": "新建表 - {{database}}",
"sidebar.tab.package_definition": "存储包:{{name}}",
"sidebar.tab.recent_query": "最近查询",
"sidebar.tab.redis_command": "命令 - {{database}}",
"sidebar.tab.redis_monitor": "监控 - {{database}}",
"sidebar.tab.routine_definition": "{{type}}{{name}}",
"sidebar.tab.sequence_definition": "序列:{{name}}",
"sidebar.tab.table_overview": "表概览 - {{database}}{{schema}}",
"sidebar.tab.table_structure": "表结构({{table}}",
"sidebar.tab.trigger": "触发器:{{name}}",
@@ -7280,11 +7292,13 @@
"tab_manager.hover.kind.jvm_overview": "JVM 概览",
"tab_manager.hover.kind.jvm_resource": "JVM 资源",
"tab_manager.hover.kind.materialized_view": "物化视图",
"tab_manager.hover.kind.package": "存储包",
"tab_manager.hover.kind.query": "SQL 查询",
"tab_manager.hover.kind.redis_command": "Redis 命令",
"tab_manager.hover.kind.redis_keys": "Redis Key",
"tab_manager.hover.kind.redis_monitor": "Redis 监控",
"tab_manager.hover.kind.routine": "函数 / 存储过程",
"tab_manager.hover.kind.sequence": "序列",
"tab_manager.hover.kind.sql_analysis": "SQL 分析工作台",
"tab_manager.hover.kind.table": "表数据",
"tab_manager.hover.kind.table_export": "导出工作台",
@@ -7300,9 +7314,11 @@
"tab_manager.kind_badge.fallback": "标签",
"tab_manager.kind_badge.jvm": "JVM",
"tab_manager.kind_badge.materialized_view": "物化视图",
"tab_manager.kind_badge.package": "包",
"tab_manager.kind_badge.query": "SQL",
"tab_manager.kind_badge.redis": "Redis",
"tab_manager.kind_badge.routine": "函数",
"tab_manager.kind_badge.sequence": "序列",
"tab_manager.kind_badge.sql_analysis": "分析",
"tab_manager.kind_badge.table": "表",
"tab_manager.kind_badge.table_export": "导出",

View File

@@ -4677,7 +4677,9 @@
"definition_viewer.editor.metadata_fallback.name_label": "名稱",
"definition_viewer.editor.metadata_fallback.type_label": "類型",
"definition_viewer.editor.object_definition_not_found": "找不到{{object}}定義",
"definition_viewer.editor.package_definition_not_found": "Package definition not found",
"definition_viewer.editor.routine_definition_not_found": "找不到函式/預存程序定義",
"definition_viewer.editor.sequence_definition_not_found": "Sequence definition not found",
"definition_viewer.editor.sphinx.compat_queries_hint": "已執行多組相容查詢,可能是版本能力限制或物件類型不支援。",
"definition_viewer.editor.sphinx.empty_result": "目前 Sphinx 實例{{version}}未返回{{object}}定義。",
"definition_viewer.editor.sphinx.failed_message_label": "返回失敗訊息",
@@ -4686,7 +4688,9 @@
"definition_viewer.editor.sphinx.version_suffix": "(版本: {{version}}",
"definition_viewer.editor.unsupported_event_definition": "暫不支援此資料庫類型的事件定義查看",
"definition_viewer.editor.unsupported_object_definition": "暫不支援此物件定義查看",
"definition_viewer.editor.unsupported_package_definition": "This database type does not support viewing package definitions",
"definition_viewer.editor.unsupported_routine_definition": "暫不支援此資料庫類型的函式/預存程序定義查看",
"definition_viewer.editor.unsupported_sequence_definition": "This database type does not support viewing sequence definitions",
"definition_viewer.editor.unsupported_sqlite_routine_definition": "SQLite 不支援函式/預存程序定義管理",
"definition_viewer.editor.unsupported_view_definition": "暫不支援此資料庫類型的檢視定義查看",
"definition_viewer.editor.view_definition_not_found": "找不到檢視定義",
@@ -4695,16 +4699,22 @@
"definition_viewer.error.load_failed": "載入失敗",
"definition_viewer.error.query_failed": "查詢定義失敗",
"definition_viewer.error.query_failed_detail": "查詢定義失敗:{{detail}}",
"definition_viewer.error.package_name_empty": "Package name is empty",
"definition_viewer.error.routine_name_empty": "函式/預存程序名稱為空",
"definition_viewer.error.sequence_name_empty": "Sequence name is empty",
"definition_viewer.error.view_name_empty": "檢視名稱為空",
"definition_viewer.field.database": "資料庫",
"definition_viewer.field.type": "類型",
"definition_viewer.loading.event_definition": "載入事件定義...",
"definition_viewer.loading.package_definition": "Loading package definition...",
"definition_viewer.loading.routine_definition": "載入函式/預存程序定義...",
"definition_viewer.loading.sequence_definition": "Loading sequence definition...",
"definition_viewer.loading.view_definition": "載入檢視定義...",
"definition_viewer.object.event": "事件",
"definition_viewer.object.materialized_view": "物化檢視",
"definition_viewer.object.package": "Package",
"definition_viewer.object.routine": "函式/預存程序",
"definition_viewer.object.sequence": "Sequence",
"definition_viewer.object.view": "檢視",
"definition_viewer.warning.refresh_latest_failed": "重新整理最新定義失敗",
"dev.perf_data_grid.columns": "欄數",
@@ -7019,10 +7029,12 @@
"sidebar.tab.new_query": "新增查詢",
"sidebar.tab.new_query_database": "新增查詢({{database}}",
"sidebar.tab.new_table": "新增資料表 - {{database}}",
"sidebar.tab.package_definition": "Package: {{name}}",
"sidebar.tab.recent_query": "最近查詢",
"sidebar.tab.redis_command": "命令 - {{database}}",
"sidebar.tab.redis_monitor": "監控 - {{database}}",
"sidebar.tab.routine_definition": "{{type}}{{name}}",
"sidebar.tab.sequence_definition": "Sequence: {{name}}",
"sidebar.tab.table_overview": "資料表概覽 - {{database}}{{schema}}",
"sidebar.tab.table_structure": "資料表結構({{table}}",
"sidebar.tab.trigger": "觸發器:{{name}}",
@@ -7280,11 +7292,13 @@
"tab_manager.hover.kind.jvm_overview": "JVM 概覽",
"tab_manager.hover.kind.jvm_resource": "JVM 資源",
"tab_manager.hover.kind.materialized_view": "物化視圖",
"tab_manager.hover.kind.package": "Package",
"tab_manager.hover.kind.query": "SQL 查詢",
"tab_manager.hover.kind.redis_command": "Redis 命令",
"tab_manager.hover.kind.redis_keys": "Redis Key",
"tab_manager.hover.kind.redis_monitor": "Redis 監控",
"tab_manager.hover.kind.routine": "函數 / 程序",
"tab_manager.hover.kind.sequence": "Sequence",
"tab_manager.hover.kind.sql_analysis": "SQL 分析工作台",
"tab_manager.hover.kind.table": "表資料",
"tab_manager.hover.kind.table_export": "匯出工作台",
@@ -7300,9 +7314,11 @@
"tab_manager.kind_badge.fallback": "標籤",
"tab_manager.kind_badge.jvm": "JVM",
"tab_manager.kind_badge.materialized_view": "物化視圖",
"tab_manager.kind_badge.package": "Pkg",
"tab_manager.kind_badge.query": "SQL",
"tab_manager.kind_badge.redis": "Redis",
"tab_manager.kind_badge.routine": "函式",
"tab_manager.kind_badge.sequence": "Seq",
"tab_manager.kind_badge.sql_analysis": "分析",
"tab_manager.kind_badge.table": "表",
"tab_manager.kind_badge.table_export": "匯出",