mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-22 13:17:10 +08:00
⚡️ perf(database): 优化查询元数据加载和连接释放
- 查询编辑器仅预取当前库及 SQL 显式引用库的元数据 - 断开侧边栏连接时主动释放同实例后端缓存连接 - 完善 Redis 连接释放和 Wails 前端绑定 - 修复 SQL Server 存储过程消息结果显示 - 调整查询工具栏布局并补充回归测试 Close #541
This commit is contained in:
@@ -505,8 +505,10 @@ describe('QueryEditor external SQL save', () => {
|
||||
}
|
||||
storeState.sqlEditorPendingTransactions[tabId] = transaction;
|
||||
});
|
||||
Object.values(backendApp).forEach((fn) => fn.mockReset());
|
||||
messageApi.success.mockReset();
|
||||
messageApi.error.mockReset();
|
||||
messageApi.info.mockReset();
|
||||
messageApi.warning.mockReset();
|
||||
backendApp.DBQuery.mockResolvedValue({ success: true, data: [] });
|
||||
backendApp.WriteSQLFile.mockResolvedValue({ success: true });
|
||||
@@ -915,8 +917,6 @@ describe('QueryEditor external SQL save', () => {
|
||||
autoFetchState.visible = true;
|
||||
storeState.connections[0].config.database = '';
|
||||
backendApp.DBGetDatabases.mockResolvedValueOnce({ success: true, data: [{ Database: 'information_schema' }, { Database: 'main' }] });
|
||||
backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBGetAllColumns.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [{ Tables_in_main: 'organization' }] });
|
||||
backendApp.DBGetAllColumns.mockResolvedValueOnce({ success: true, data: [] });
|
||||
|
||||
@@ -950,8 +950,6 @@ describe('QueryEditor external SQL save', () => {
|
||||
autoFetchState.visible = true;
|
||||
storeState.connections[0].config.database = '';
|
||||
backendApp.DBGetDatabases.mockResolvedValueOnce({ success: true, data: [{ Database: 'information_schema' }, { Database: 'main' }] });
|
||||
backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBGetAllColumns.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [{ Tables_in_main: 'fs_org_auth_application' }] });
|
||||
backendApp.DBGetAllColumns.mockResolvedValueOnce({
|
||||
success: true,
|
||||
@@ -1111,6 +1109,47 @@ describe('QueryEditor external SQL save', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('preloads metadata only for the current database when many databases are visible', async () => {
|
||||
let renderer!: ReactTestRenderer;
|
||||
autoFetchState.visible = true;
|
||||
storeState.connections[0].config.type = 'mysql';
|
||||
storeState.connections[0].config.database = '';
|
||||
const databaseRows = [
|
||||
{ Database: 'main' },
|
||||
...Array.from({ length: 40 }, (_, index) => ({ Database: `tenant_${String(index + 1).padStart(3, '0')}` })),
|
||||
];
|
||||
backendApp.DBGetDatabases.mockResolvedValueOnce({ success: true, data: databaseRows });
|
||||
backendApp.DBGetTables.mockImplementation(async (_config: any, dbName: string) => ({
|
||||
success: true,
|
||||
data: dbName === 'main' ? [{ Tables_in_main: 'users' }] : [{ [`Tables_in_${dbName}`]: 'unexpected_table' }],
|
||||
}));
|
||||
backendApp.DBGetAllColumns.mockImplementation(async (_config: any, dbName: string) => ({
|
||||
success: true,
|
||||
data: dbName === 'main' ? [{ tableName: 'users', name: 'id', type: 'bigint' }] : [],
|
||||
}));
|
||||
backendApp.DBQuery.mockResolvedValue({ success: true, data: [] });
|
||||
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ query: 'SELECT * FROM users', dbName: 'main' })} />);
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(backendApp.DBGetDatabases).toHaveBeenCalledTimes(1);
|
||||
expect(backendApp.DBGetTables.mock.calls.map((call: any[]) => call[1])).toEqual(['main']);
|
||||
expect(backendApp.DBGetAllColumns.mock.calls.map((call: any[]) => call[1])).toEqual(['main']);
|
||||
const metadataQueryDbs = new Set(backendApp.DBQuery.mock.calls.map((call: any[]) => call[1]));
|
||||
expect([...metadataQueryDbs]).toEqual(['main']);
|
||||
|
||||
await act(async () => {
|
||||
renderer.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
it('suggests columns in WHERE for cross-database MySQL tables with quoted hyphenated database names', async () => {
|
||||
let renderer!: ReactTestRenderer;
|
||||
autoFetchState.visible = true;
|
||||
@@ -3253,6 +3292,154 @@ describe('QueryEditor external SQL save', () => {
|
||||
expect(dataGridState.latestProps?.columnNames).toEqual(['name']);
|
||||
});
|
||||
|
||||
it('prefers the first displayable sqlserver procedure result when empty result sets are returned', async () => {
|
||||
storeState.connections[0].config.type = 'sqlserver';
|
||||
storeState.connections[0].config.database = 'hydee';
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [
|
||||
{ statementIndex: 1, columns: [], rows: [] },
|
||||
{
|
||||
statementIndex: 1,
|
||||
columns: ['insert_sql'],
|
||||
rows: [
|
||||
{ insert_sql: "insert into c_user(userid) values('168')" },
|
||||
{ insert_sql: "insert into c_user(userid) values('169')" },
|
||||
],
|
||||
},
|
||||
{ statementIndex: 1, columns: [], rows: [] },
|
||||
{ statementIndex: 1, columns: [], rows: [] },
|
||||
],
|
||||
});
|
||||
|
||||
let renderer!: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'hydee', query: "p_get_select 'c_user','userid = ''168''',1" })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(textContent(renderer!.toJSON())).toContain('结果 4');
|
||||
expect(dataGridState.latestProps?.columnNames).toEqual(['insert_sql']);
|
||||
expect(dataGridState.latestProps?.data?.[0]).toMatchObject({
|
||||
insert_sql: "insert into c_user(userid) values('168')",
|
||||
});
|
||||
});
|
||||
|
||||
it('prefers concrete sqlserver procedure rows over affected-row status results', async () => {
|
||||
storeState.connections[0].config.type = 'sqlserver';
|
||||
storeState.connections[0].config.database = 'hydee';
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [
|
||||
{ statementIndex: 1, columns: ['affectedRows'], rows: [{ affectedRows: 0 }] },
|
||||
{ statementIndex: 1, columns: [], rows: [] },
|
||||
{
|
||||
statementIndex: 1,
|
||||
columns: ['insert_sql'],
|
||||
rows: [
|
||||
{ insert_sql: "insert into c_user(userid) values('168')" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let renderer!: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'hydee', query: "p_get_select 'c_user','userid = ''168''',1" })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(dataGridState.latestProps?.columnNames).toEqual(['insert_sql']);
|
||||
expect(dataGridState.latestProps?.data?.[0]).toMatchObject({
|
||||
insert_sql: "insert into c_user(userid) values('168')",
|
||||
});
|
||||
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
||||
});
|
||||
|
||||
it('prefers sqlserver print output messages over affected-row status results', async () => {
|
||||
storeState.connections[0].config.type = 'sqlserver';
|
||||
storeState.connections[0].config.database = 'hydee';
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [
|
||||
{ statementIndex: 1, columns: ['affectedRows'], rows: [{ affectedRows: 0 }] },
|
||||
{
|
||||
statementIndex: 1,
|
||||
columns: [],
|
||||
rows: [],
|
||||
messages: [
|
||||
"insert into c_dyscript(projectid,name) values (1,'demo')",
|
||||
"insert into c_dyscript(projectid,name) values (2,'next')",
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let renderer!: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'hydee', query: "p_get_select c_dyscript,'projectid = 1',1" })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(textContent(renderer!.toJSON())).toContain('消息 2');
|
||||
expect(textContent(renderer!.toJSON())).toContain("insert into c_dyscript(projectid,name) values (1,'demo')");
|
||||
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
||||
expect(dataGridState.latestProps).toBeNull();
|
||||
});
|
||||
|
||||
it('renders top-level sqlserver print messages when result sets contain only status rows', async () => {
|
||||
storeState.connections[0].config.type = 'sqlserver';
|
||||
storeState.connections[0].config.database = 'hydee';
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [
|
||||
{ statementIndex: 1, columns: ['affectedRows'], rows: [{ affectedRows: 0 }] },
|
||||
],
|
||||
messages: [
|
||||
"insert into c_dyscript(projectid,name) values (1,'demo')",
|
||||
],
|
||||
});
|
||||
|
||||
let renderer!: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'hydee', query: "p_get_select c_dyscript,'projectid = 1',1" })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(textContent(renderer!.toJSON())).toContain('消息 2');
|
||||
expect(textContent(renderer!.toJSON())).toContain("insert into c_dyscript(projectid,name) values (1,'demo')");
|
||||
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
||||
expect(dataGridState.latestProps).toBeNull();
|
||||
});
|
||||
|
||||
it('keeps both tabs when rerunning the same single sqlserver statement with multiple result sets', async () => {
|
||||
storeState.connections[0].config.type = 'sqlserver';
|
||||
storeState.connections[0].config.database = 'master';
|
||||
|
||||
Reference in New Issue
Block a user