mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-18 12:54:08 +08:00
🐛 fix(oracle): 修复存储包定义截断与斜杠误执行
- 对象树支持序列和存储包双击及右键查看定义 - Oracle package 同时读取 PACKAGE 与 PACKAGE BODY 并生成可编辑 SQL - SQL 拆分器跳过 SQL*Plus 独立斜杠和注释分隔符 - 补充前后端 PL/SQL 拆分与定义查看回归测试
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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() !== '';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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 [
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)))))),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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;';
|
||||
|
||||
|
||||
@@ -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--;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "エクスポート",
|
||||
|
||||
@@ -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": "Экспорт",
|
||||
|
||||
@@ -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": "导出",
|
||||
|
||||
@@ -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": "匯出",
|
||||
|
||||
Reference in New Issue
Block a user