fix(connection): move query timeout into per-connection settings
This commit is contained in:
1 parent
9f72c38253
commit
486d9bb3a6
20 files changed
+97
-60
No files matched your search
@@ -40,7 +40,7 @@ type DbOption = { value: string; label: string };
|
||||
type DbCategory = { key: string; title: string; options: DbOption[] };
|
||||
type DialogStep = "select" | "config";
|
||||
type DbPickerView = "icon" | "list";
|
||||
type ConfigTab = "connection" | "tls" | "ssh" | "proxy";
|
||||
type ConfigTab = "connection" | "advanced" | "tls" | "ssh" | "proxy";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
@@ -88,6 +88,7 @@ const defaultForm = (): Omit<ConnectionConfig, "id"> => ({
|
||||
ssh_expose_lan: false,
|
||||
ssh_connect_timeout_secs: 5,
|
||||
connect_timeout_secs: 5,
|
||||
query_timeout_secs: 30,
|
||||
proxy_enabled: false,
|
||||
proxy_type: "socks5",
|
||||
proxy_host: "",
|
||||
@@ -347,6 +348,7 @@ watch(
|
||||
ssh_expose_lan: config.ssh_expose_lan || false,
|
||||
ssh_connect_timeout_secs: config.ssh_connect_timeout_secs || 5,
|
||||
connect_timeout_secs: config.connect_timeout_secs || 5,
|
||||
query_timeout_secs: config.query_timeout_secs ?? 30,
|
||||
proxy_enabled: config.proxy_enabled || false,
|
||||
proxy_type: config.proxy_type || "socks5",
|
||||
proxy_host: config.proxy_host || "",
|
||||
@@ -741,6 +743,8 @@ function connectionConfigForSubmit(id: string): ConnectionConfig {
|
||||
config.ssh_connect_timeout_secs = Number.isFinite(sshTimeout) && sshTimeout > 0 ? sshTimeout : 5;
|
||||
const connectTimeout = Number(config.connect_timeout_secs);
|
||||
config.connect_timeout_secs = Number.isFinite(connectTimeout) && connectTimeout > 0 ? connectTimeout : 5;
|
||||
const queryTimeout = Number(config.query_timeout_secs);
|
||||
config.query_timeout_secs = Number.isFinite(queryTimeout) && queryTimeout >= 0 ? queryTimeout : 30;
|
||||
const proxyPort = Number(config.proxy_port);
|
||||
config.proxy_port = Number.isFinite(proxyPort) && proxyPort > 0 ? proxyPort : 1080;
|
||||
if (!config.one_time) config.one_time = undefined;
|
||||
@@ -1440,15 +1444,13 @@ function openExternalUrl(url: string) {
|
||||
<template v-else>
|
||||
<div class="space-y-3">
|
||||
<Tabs v-model="configTab" class="min-h-0">
|
||||
<div
|
||||
v-if="supportsTlsToggle || canUseSsh || canUseProxy"
|
||||
class="flex items-center justify-between border-b pb-2"
|
||||
>
|
||||
<div class="flex items-center justify-between border-b pb-2">
|
||||
<TabsList>
|
||||
<TabsTrigger value="connection">{{ t("connection.basicTab") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="supportsTlsToggle" value="tls">{{ t("connection.tlsTab") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="canUseSsh" value="ssh">{{ t("connection.sshTunnel") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="canUseProxy" value="proxy">{{ t("connection.proxy") }}</TabsTrigger>
|
||||
<TabsTrigger value="advanced">{{ t("connection.advancedTab") }}</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -2223,6 +2225,33 @@ function openExternalUrl(url: string) {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="advanced" class="m-0">
|
||||
<div class="grid gap-4 py-4 pr-2 max-h-[65vh] overflow-y-auto">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">{{ t("connection.connectTimeout") }}</Label>
|
||||
<Input
|
||||
v-model.number="form.connect_timeout_secs"
|
||||
type="number"
|
||||
min="1"
|
||||
max="300"
|
||||
step="1"
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">{{ t("connection.queryTimeout") }}</Label>
|
||||
<Input
|
||||
v-model.number="form.query_timeout_secs"
|
||||
type="number"
|
||||
min="0"
|
||||
max="300"
|
||||
step="1"
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent v-if="canUseSsh" value="ssh" class="m-0">
|
||||
<div class="grid gap-4 py-4 pr-2 max-h-[65vh] overflow-y-auto">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
@@ -2318,17 +2347,6 @@ function openExternalUrl(url: string) {
|
||||
:disabled="!form.ssh_enabled"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">{{ t("connection.connectTimeout") }}</Label>
|
||||
<Input
|
||||
v-model.number="form.connect_timeout_secs"
|
||||
type="number"
|
||||
min="1"
|
||||
max="300"
|
||||
step="1"
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@@ -96,7 +96,6 @@ const editShowTrayIcon = ref(settingsStore.desktopSettings.show_tray_icon);
|
||||
const editShowColumnCommentsInHeader = ref(settingsStore.editorSettings.showColumnCommentsInHeader);
|
||||
const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions);
|
||||
const editRedisScanPageSize = ref(settingsStore.editorSettings.redisScanPageSize);
|
||||
const editQueryTimeoutSecs = ref(settingsStore.editorSettings.queryTimeoutSecs);
|
||||
const editShortcuts = ref(normalizeShortcutSettings(settingsStore.editorSettings.shortcuts));
|
||||
const editSidebarActivation = ref(settingsStore.editorSettings.sidebarActivation);
|
||||
const editAutoSelectActiveSidebarNode = ref(settingsStore.editorSettings.autoSelectActiveSidebarNode);
|
||||
@@ -230,7 +229,6 @@ watch(
|
||||
editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader;
|
||||
editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions;
|
||||
editRedisScanPageSize.value = settingsStore.editorSettings.redisScanPageSize;
|
||||
editQueryTimeoutSecs.value = settingsStore.editorSettings.queryTimeoutSecs;
|
||||
editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);
|
||||
editSidebarActivation.value = settingsStore.editorSettings.sidebarActivation;
|
||||
editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode;
|
||||
@@ -268,7 +266,6 @@ function hasChanges(): boolean {
|
||||
editShowColumnCommentsInHeader.value !== settingsStore.editorSettings.showColumnCommentsInHeader ||
|
||||
editCompactColumnHeaderActions.value !== settingsStore.editorSettings.compactColumnHeaderActions ||
|
||||
editRedisScanPageSize.value !== settingsStore.editorSettings.redisScanPageSize ||
|
||||
editQueryTimeoutSecs.value !== settingsStore.editorSettings.queryTimeoutSecs ||
|
||||
JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts) ||
|
||||
editSidebarActivation.value !== settingsStore.editorSettings.sidebarActivation ||
|
||||
editAutoSelectActiveSidebarNode.value !== settingsStore.editorSettings.autoSelectActiveSidebarNode ||
|
||||
@@ -292,7 +289,6 @@ async function applySettings() {
|
||||
showColumnCommentsInHeader: editShowColumnCommentsInHeader.value,
|
||||
compactColumnHeaderActions: editCompactColumnHeaderActions.value,
|
||||
redisScanPageSize: editRedisScanPageSize.value,
|
||||
queryTimeoutSecs: editQueryTimeoutSecs.value,
|
||||
shortcuts: editShortcuts.value,
|
||||
sidebarActivation: editSidebarActivation.value,
|
||||
autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value,
|
||||
@@ -318,7 +314,6 @@ function resetDefaults() {
|
||||
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
|
||||
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
||||
editRedisScanPageSize.value = DEFAULT_EDITOR_SETTINGS.redisScanPageSize;
|
||||
editQueryTimeoutSecs.value = DEFAULT_EDITOR_SETTINGS.queryTimeoutSecs;
|
||||
editShortcuts.value = normalizeShortcutSettings(DEFAULT_EDITOR_SETTINGS.shortcuts);
|
||||
editSidebarActivation.value = DEFAULT_EDITOR_SETTINGS.sidebarActivation;
|
||||
editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode;
|
||||
@@ -1075,26 +1070,6 @@ watch(
|
||||
|
||||
<Separator />
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="query-timeout-secs">{{ t("settings.queryTimeoutSecs") }}</Label>
|
||||
<Input
|
||||
id="query-timeout-secs"
|
||||
type="number"
|
||||
min="0"
|
||||
:model-value="String(editQueryTimeoutSecs)"
|
||||
@update:model-value="
|
||||
(v: any) => {
|
||||
const n = Number(v);
|
||||
if (Number.isFinite(n) && n >= 0) editQueryTimeoutSecs = n;
|
||||
}
|
||||
"
|
||||
class="h-9 w-28"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">{{ t("settings.queryTimeoutSecsDescription") }}</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<!-- Live Preview -->
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.preview") }}</Label>
|
||||
|
||||
@@ -207,6 +207,7 @@ export default {
|
||||
groupDeleted: "Group deleted",
|
||||
loadFailed: "Failed to load saved connections: {message}",
|
||||
sshTunnel: "SSH Tunnel",
|
||||
advancedTab: "Advanced",
|
||||
sshEnable: "Connect via SSH tunnel",
|
||||
sshHost: "SSH Host",
|
||||
sshUser: "SSH User",
|
||||
@@ -219,6 +220,7 @@ export default {
|
||||
sshExposeLan: "Expose tunnel to LAN",
|
||||
sshConnectTimeout: "SSH Timeout (seconds)",
|
||||
connectTimeout: "Connection Timeout (seconds)",
|
||||
queryTimeout: "Query Timeout (seconds)",
|
||||
proxy: "Proxy",
|
||||
proxyEnable: "Connect database through proxy",
|
||||
proxyType: "Proxy Type",
|
||||
@@ -1497,8 +1499,6 @@ export default {
|
||||
shortcutPressShortcut: "Press shortcut",
|
||||
shortcutConflict: "This shortcut conflicts with another action in the same scope.",
|
||||
preview: "Live Preview",
|
||||
queryTimeoutSecs: "Query Timeout (seconds)",
|
||||
queryTimeoutSecsDescription: "Maximum execution time per query. 0 = no timeout. Default 30s.",
|
||||
jdbcPlugin: "DBX JDBC plugin",
|
||||
jdbcPluginInstall: "Install JDBC plugin",
|
||||
jdbcPluginInstallSuccess: "JDBC plugin installed",
|
||||
|
||||
@@ -199,6 +199,7 @@ export default {
|
||||
groupDeleted: "Grupo eliminado",
|
||||
loadFailed: "Error al cargar las conexiones guardadas: {message}",
|
||||
sshTunnel: "Túnel SSH",
|
||||
advancedTab: "Avanzado",
|
||||
sshEnable: "Conectar mediante túnel SSH",
|
||||
sshHost: "Host SSH",
|
||||
sshUser: "Usuario SSH",
|
||||
@@ -211,6 +212,7 @@ export default {
|
||||
sshExposeLan: "Exponer túnel a la red local",
|
||||
sshConnectTimeout: "Tiempo de espera SSH (segundos)",
|
||||
connectTimeout: "Tiempo de espera de conexión (segundos)",
|
||||
queryTimeout: "Tiempo de espera de consulta (segundos)",
|
||||
proxy: "Proxy",
|
||||
proxyEnable: "Conectar la base de datos mediante proxy",
|
||||
proxyType: "Tipo de proxy",
|
||||
|
||||
@@ -203,6 +203,7 @@ export default {
|
||||
groupDeleted: "分组已删除",
|
||||
loadFailed: "加载已保存连接失败:{message}",
|
||||
sshTunnel: "SSH 隧道",
|
||||
advancedTab: "高级",
|
||||
sshEnable: "通过 SSH 隧道连接",
|
||||
sshHost: "SSH 主机",
|
||||
sshUser: "SSH 用户",
|
||||
@@ -215,6 +216,7 @@ export default {
|
||||
sshExposeLan: "允许局域网访问隧道",
|
||||
sshConnectTimeout: "SSH 超时时间(秒)",
|
||||
connectTimeout: "连接超时(秒)",
|
||||
queryTimeout: "查询超时(秒)",
|
||||
proxy: "代理",
|
||||
proxyEnable: "通过代理连接数据库",
|
||||
proxyType: "代理类型",
|
||||
@@ -1463,8 +1465,6 @@ export default {
|
||||
shortcutPressShortcut: "按下快捷键",
|
||||
shortcutConflict: "这个快捷键与同一作用域内的其他操作冲突。",
|
||||
preview: "实时预览",
|
||||
queryTimeoutSecs: "查询超时(秒)",
|
||||
queryTimeoutSecsDescription: "每条查询的最大执行时间。0 = 不限制。默认 30 秒。",
|
||||
jdbcPlugin: "DBX JDBC 插件",
|
||||
jdbcPluginInstall: "安装 JDBC 插件",
|
||||
jdbcPluginInstallSuccess: "JDBC 插件已安装",
|
||||
|
||||
@@ -231,6 +231,7 @@ function buildConnection(
|
||||
ssh_expose_lan: false,
|
||||
ssh_connect_timeout_secs: 5,
|
||||
connect_timeout_secs: 5,
|
||||
query_timeout_secs: 30,
|
||||
ssl: false,
|
||||
oracle_connection_type: profile.dbType === "oracle" ? parsedUrl.oracleConnectionType || "service_name" : undefined,
|
||||
connection_string: profile.dbType === "jdbc" || profile.dbType === "mongodb" ? url || undefined : undefined,
|
||||
|
||||
@@ -165,6 +165,7 @@ async function parseConnection(node: ParsedNode): Promise<ConnectionConfig | nul
|
||||
ssh_expose_lan: false,
|
||||
ssh_connect_timeout_secs: 5,
|
||||
connect_timeout_secs: 5,
|
||||
query_timeout_secs: 30,
|
||||
ssl: false,
|
||||
oracle_connection_type: oracleConnectionType,
|
||||
connection_string: undefined,
|
||||
|
||||
@@ -243,6 +243,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
: [],
|
||||
ssh_connect_timeout_secs: config.ssh_connect_timeout_secs || 5,
|
||||
connect_timeout_secs: config.connect_timeout_secs || 5,
|
||||
query_timeout_secs: config.query_timeout_secs ?? 30,
|
||||
proxy_type: config.proxy_type || "socks5",
|
||||
proxy_port: config.proxy_port || 1080,
|
||||
};
|
||||
|
||||
@@ -560,6 +560,12 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const connStore = useConnectionStore();
|
||||
const conn = connStore.getConfig(tab.connectionId);
|
||||
const useAgentCursor = !!conn?.db_type && AGENT_DRIVER_TYPES.has(conn.db_type);
|
||||
const queryTimeoutSecs =
|
||||
typeof conn?.query_timeout_secs === "number" &&
|
||||
Number.isFinite(conn.query_timeout_secs) &&
|
||||
conn.query_timeout_secs >= 0
|
||||
? conn.query_timeout_secs
|
||||
: 30;
|
||||
const settingsStore = useSettingsStore();
|
||||
await closeResultSession(tab, options?.pagination?.sessionId);
|
||||
if (tab.mode === "query") {
|
||||
@@ -656,7 +662,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
: { maxRows: pageLimit, fetchSize: pageLimit }
|
||||
: {}),
|
||||
clientSessionId: tab.id,
|
||||
timeoutSecs: settingsStore.editorSettings.queryTimeoutSecs,
|
||||
timeoutSecs: queryTimeoutSecs,
|
||||
};
|
||||
const results = await api.executeMulti(
|
||||
tab.connectionId,
|
||||
@@ -741,6 +747,13 @@ export const useQueryStore = defineStore("query", () => {
|
||||
async function explainTabSql(id: string, sql: string, databaseType?: DatabaseType) {
|
||||
const tab = tabs.value.find((t) => t.id === id);
|
||||
if (!tab) return { ok: false as const, reason: "empty" as const };
|
||||
const conn = useConnectionStore().getConfig(tab.connectionId);
|
||||
const queryTimeoutSecs =
|
||||
typeof conn?.query_timeout_secs === "number" &&
|
||||
Number.isFinite(conn.query_timeout_secs) &&
|
||||
conn.query_timeout_secs >= 0
|
||||
? conn.query_timeout_secs
|
||||
: 30;
|
||||
|
||||
const built = await buildExplainSql(databaseType, sql);
|
||||
if (!built.ok) {
|
||||
@@ -758,7 +771,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
try {
|
||||
const result = await api.executeQuery(tab.connectionId, tab.database, built.sql, tab.schema, executionId, {
|
||||
clientSessionId: tab.id,
|
||||
timeoutSecs: useSettingsStore().editorSettings.queryTimeoutSecs,
|
||||
timeoutSecs: queryTimeoutSecs,
|
||||
});
|
||||
const current = tabs.value.find((t) => t.id === id);
|
||||
if (current?.explainExecutionId === executionId) {
|
||||
|
||||
@@ -191,8 +191,6 @@ export interface EditorSettings {
|
||||
columnFormatters: Record<string, ColumnFormatterConfig>;
|
||||
customColumnFormatters: Record<string, CustomColumnFormatterConfig>;
|
||||
snippets: SqlSnippet[];
|
||||
/** Query timeout in seconds. 0 = no timeout. Default 30s. */
|
||||
queryTimeoutSecs: number;
|
||||
}
|
||||
|
||||
export const EDITOR_THEMES: { value: EditorTheme; label: string; dark: boolean }[] = [
|
||||
@@ -242,7 +240,6 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
columnFormatters: {},
|
||||
customColumnFormatters: {},
|
||||
snippets: DEFAULT_SQL_SNIPPETS,
|
||||
queryTimeoutSecs: 30,
|
||||
};
|
||||
|
||||
export const STORAGE_KEY = "dbx-editor-settings";
|
||||
@@ -301,11 +298,6 @@ function normalizeSqlSnippets(value: unknown, existing?: SqlSnippet[]): SqlSnipp
|
||||
return valid;
|
||||
}
|
||||
|
||||
function normalizeQueryTimeoutSecs(value: unknown): number {
|
||||
if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
|
||||
return DEFAULT_EDITOR_SETTINGS.queryTimeoutSecs;
|
||||
}
|
||||
|
||||
export function normalizeEditorSettings(settings: Partial<EditorSettings>, existing?: EditorSettings): EditorSettings {
|
||||
return {
|
||||
fontFamily: settings.fontFamily ?? DEFAULT_EDITOR_SETTINGS.fontFamily,
|
||||
@@ -335,7 +327,6 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
||||
columnFormatters: normalizeColumnFormatters(settings.columnFormatters),
|
||||
customColumnFormatters: normalizeCustomColumnFormatters(settings.customColumnFormatters),
|
||||
snippets: normalizeSqlSnippets(settings.snippets, existing?.snippets),
|
||||
queryTimeoutSecs: normalizeQueryTimeoutSecs(settings.queryTimeoutSecs),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -464,8 +455,6 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
if (partial.customColumnFormatters !== undefined)
|
||||
editorSettings.value.customColumnFormatters = partial.customColumnFormatters;
|
||||
if (partial.snippets !== undefined) editorSettings.value.snippets = normalizeSqlSnippets(partial.snippets);
|
||||
if (partial.queryTimeoutSecs !== undefined)
|
||||
editorSettings.value.queryTimeoutSecs = normalizeQueryTimeoutSecs(partial.queryTimeoutSecs);
|
||||
saveEditorSettings(editorSettings.value);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ export interface ConnectionConfig {
|
||||
ssh_expose_lan?: boolean;
|
||||
ssh_connect_timeout_secs?: number;
|
||||
connect_timeout_secs?: number;
|
||||
query_timeout_secs?: number;
|
||||
proxy_enabled?: boolean;
|
||||
proxy_type?: "socks5" | "http";
|
||||
proxy_host?: string;
|
||||
|
||||
Reference in new issue
Block a user