From 265f95a149227be3db655afe0ecf5d932ee06290 Mon Sep 17 00:00:00 2001 From: SuLe Date: Sat, 2 May 2026 13:49:46 +0800 Subject: [PATCH] Fix SQL editor and table query UX --- src/App.vue | 97 ++++++++++++++++++-------------- src/components/grid/DataGrid.vue | 82 +++++++++++++++++++++------ src/i18n/locales/en.ts | 10 ++++ src/i18n/locales/zh-CN.ts | 10 ++++ src/lib/sqlCompletion.ts | 2 +- src/lib/tableSelectSql.ts | 55 ++++++++++++++++++ tests/tableSelectSql.test.ts | 40 +++++++++++++ 7 files changed, 235 insertions(+), 61 deletions(-) create mode 100644 src/lib/tableSelectSql.ts create mode 100644 tests/tableSelectSql.test.ts diff --git a/src/App.vue b/src/App.vue index a743a53a6..60e3da68d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -51,6 +51,7 @@ import { getVersion } from "@tauri-apps/api/app"; import * as api from "@/lib/tauri"; import { canCancelQueryExecution, queryExecutionLabelKey } from "@/lib/queryExecutionState"; import { resolveExecutableSql } from "@/lib/sqlExecutionTarget"; +import { buildTableSelectSql, quoteTableIdentifier } from "@/lib/tableSelectSql"; import type { SqlFormatDialect } from "@/lib/sqlFormatter"; import { isCloseTabShortcut, isExecuteSqlShortcut } from "@/lib/keyboardShortcuts"; @@ -307,6 +308,14 @@ function tabDisplayTitle(tab: typeof queryStore.tabs[number]): string { return tab.title; } +function tabModeLabel(tab: typeof queryStore.tabs[number]): string { + if (tab.mode === "data") return t("tabs.table"); + if (tab.mode === "query") return t("tabs.sql"); + if (tab.mode === "mongo") return t("tabs.mongo"); + if (tab.mode === "redis") return t("tabs.redis"); + return tab.mode; +} + function databaseDisplayNameForTab(connectionId: string, database: string): string { const connection = connectionStore.getConfig(connectionId); if (connection?.db_type === "redis" && database !== "") return `db${database}`; @@ -472,7 +481,8 @@ async function changeActiveConnection(connectionId: any) { async function onExecuteSql(sql: string) { const tab = activeTab.value; if (!tab) return; - await api.executeQuery(tab.connectionId, tab.database, sql); + queryStore.updateSql(tab.id, sql); + await queryStore.executeTabSql(tab.id, sql); } async function onReloadData() { @@ -488,61 +498,36 @@ type ActiveTab = NonNullable; function quoteIdent(tab: ActiveTab, name: string): string { const config = connectionStore.getConfig(tab.connectionId); - return config?.db_type === "mysql" - ? `\`${name.replace(/`/g, "``")}\`` - : `"${name.replace(/"/g, '""')}"`; -} - -function qualifiedTableName(tab: NonNullable): string { - const config = connectionStore.getConfig(tab.connectionId); - if (!tab.tableMeta) return ""; - if ((config?.db_type === "postgres" || config?.db_type === "oracle" || config?.db_type === "sqlserver") && tab.tableMeta.schema) { - return `${quoteIdent(tab, tab.tableMeta.schema)}.${quoteIdent(tab, tab.tableMeta.tableName)}`; - } - return quoteIdent(tab, tab.tableMeta.tableName); -} - -function defaultOrderBy(tab: NonNullable): string | undefined { - const primaryKeys = tab.tableMeta?.primaryKeys ?? []; - if (primaryKeys.length === 0) return undefined; - return primaryKeys.map((pk) => `${quoteIdent(tab, pk)} ASC`).join(", "); + return quoteTableIdentifier(config?.db_type, name); } function buildTableSql( tab: NonNullable, - options: { orderBy?: string; limit?: number; offset?: number } = {}, + options: { orderBy?: string; limit?: number; offset?: number; whereInput?: string } = {}, ): string { const config = connectionStore.getConfig(tab.connectionId); - const limit = options.limit ?? 100; - const orderBy = options.orderBy ?? defaultOrderBy(tab); - const order = orderBy ? ` ORDER BY ${orderBy}` : ""; - - if (config?.db_type === "oracle") { - const offset = options.offset ? ` OFFSET ${options.offset} ROWS` : ""; - return `SELECT * FROM ${qualifiedTableName(tab)}${order}${offset} FETCH FIRST ${limit} ROWS ONLY`; - } - - if (config?.db_type === "sqlserver") { - return `SELECT TOP ${limit} * FROM ${qualifiedTableName(tab)}${order}`; - } - - const offset = options.offset ? ` OFFSET ${options.offset}` : ""; - return `SELECT * FROM ${qualifiedTableName(tab)}${order} LIMIT ${limit}${offset};`; + return buildTableSelectSql({ + databaseType: config?.db_type, + schema: tab.tableMeta?.schema, + tableName: tab.tableMeta?.tableName ?? "", + primaryKeys: tab.tableMeta?.primaryKeys, + ...options, + }); } -async function onPaginate(offset: number, limit: number) { +async function onPaginate(offset: number, limit: number, whereInput?: string) { const tab = activeTab.value; if (!tab?.tableMeta) return; - const sql = buildTableSql(tab, { limit, offset }); + const sql = buildTableSql(tab, { limit, offset, whereInput }); queryStore.updateSql(tab.id, sql); await queryStore.executeCurrentTab(); } -async function onSort(column: string, direction: "asc" | "desc" | null) { +async function onSort(column: string, direction: "asc" | "desc" | null, whereInput?: string) { const tab = activeTab.value; if (!tab?.tableMeta) return; - const orderBy = direction ? `${quoteIdent(tab, column)} ${direction.toUpperCase()}` : defaultOrderBy(tab); - const sql = buildTableSql(tab, { orderBy }); + const orderBy = direction ? `${quoteIdent(tab, column)} ${direction.toUpperCase()}` : undefined; + const sql = buildTableSql(tab, { orderBy, whereInput }); queryStore.updateSql(tab.id, sql); await queryStore.executeCurrentTab(); } @@ -863,7 +848,18 @@ async function setupFileDrop() { @click="queryStore.activeTabId = tab.id" > + {{ tabDisplayTitle(tab) }} + + {{ tabModeLabel(tab) }} +