feat(saved-sql): run queries in the current database

This commit is contained in:
zhangyc authored and GitHub committed 2026-07-31 01:57:39 +08:00
1 parent 4062f11a26
commit e2ba75b102
22 files changed
+501 -51

No files matched your search

@@ -1404,7 +1404,10 @@ async function openMessageMention(mention: AiMessageMention) {
try {
if (mention.kind === "sqlFile") {
const file = await savedSqlStore.ensureFileContent(mention.id);
if (file) queryStore.openSavedSql(file);
if (file) {
const tabId = queryStore.openSavedSql(file);
connectionStore.activeConnectionId = queryStore.tabs.find((tab) => tab.id === tabId)?.connectionId ?? file.connectionId;
}
return;
}
await openTableTarget({
@@ -40,6 +40,7 @@ import {
type OpenTabsRestoreMode,
type SidebarObjectInfoMode,
type SqlSemanticDiagnosticsMode,
type SavedSqlOpenTargetMode,
type UpdateDownloadSource,
type CustomThemeColors,
type CustomTheme,
@@ -306,6 +307,7 @@ const editSqlSemanticDiagnosticsEnabled = ref(settingsStore.editorSettings.sqlSe
const editConfirmDangerousSqlExecution = ref(settingsStore.editorSettings.confirmDangerousSqlExecution);
const editContinueOnErrorOnBatch = ref(settingsStore.editorSettings.continueOnErrorOnBatch);
const editConfirmUnsavedSqlClose = ref(settingsStore.editorSettings.confirmUnsavedSqlClose);
const editSavedSqlOpenTargetMode = ref<SavedSqlOpenTargetMode>(settingsStore.editorSettings.savedSqlOpenTargetMode);
const editAppLayout = ref(settingsStore.editorSettings.appLayout);
const editTabLayout = ref(settingsStore.editorSettings.tabLayout);
const editShowTrayIcon = ref(settingsStore.desktopSettings.show_tray_icon);
@@ -465,6 +467,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
confirmDangerousSqlExecution: editConfirmDangerousSqlExecution.value,
continueOnErrorOnBatch: editContinueOnErrorOnBatch.value,
confirmUnsavedSqlClose: editConfirmUnsavedSqlClose.value,
savedSqlOpenTargetMode: editSavedSqlOpenTargetMode.value,
appLayout: editAppLayout.value,
tabLayout: editTabLayout.value,
showColumnCommentsInHeader: editShowColumnCommentsInHeader.value,
@@ -730,6 +733,7 @@ function syncEditorSettingsDraftFromStore() {
editConfirmDangerousSqlExecution.value = settingsStore.editorSettings.confirmDangerousSqlExecution;
editContinueOnErrorOnBatch.value = settingsStore.editorSettings.continueOnErrorOnBatch;
editConfirmUnsavedSqlClose.value = settingsStore.editorSettings.confirmUnsavedSqlClose;
editSavedSqlOpenTargetMode.value = settingsStore.editorSettings.savedSqlOpenTargetMode;
editAppLayout.value = settingsStore.editorSettings.appLayout;
editTabLayout.value = settingsStore.editorSettings.tabLayout;
editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader;
@@ -930,6 +934,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editConfirmDangerousSqlExecution.value = DEFAULT_EDITOR_SETTINGS.confirmDangerousSqlExecution;
editContinueOnErrorOnBatch.value = DEFAULT_EDITOR_SETTINGS.continueOnErrorOnBatch;
editConfirmUnsavedSqlClose.value = DEFAULT_EDITOR_SETTINGS.confirmUnsavedSqlClose;
editSavedSqlOpenTargetMode.value = DEFAULT_EDITOR_SETTINGS.savedSqlOpenTargetMode;
editClickTableNavigationTarget.value = DEFAULT_EDITOR_SETTINGS.clickTableNavigationTarget;
editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(DEFAULT_EDITOR_SETTINGS.sqlVariableSyntaxOverrides);
} else if (tab === "formatter") {
@@ -1018,6 +1023,7 @@ function resetAllDefaults() {
editSqlSemanticDiagnosticsEnabled.value = DEFAULT_EDITOR_SETTINGS.sqlSemanticDiagnosticsEnabled;
editConfirmDangerousSqlExecution.value = DEFAULT_EDITOR_SETTINGS.confirmDangerousSqlExecution;
editConfirmUnsavedSqlClose.value = DEFAULT_EDITOR_SETTINGS.confirmUnsavedSqlClose;
editSavedSqlOpenTargetMode.value = DEFAULT_EDITOR_SETTINGS.savedSqlOpenTargetMode;
editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(DEFAULT_EDITOR_SETTINGS.sqlVariableSyntaxOverrides);
editAppLayout.value = DEFAULT_EDITOR_SETTINGS.appLayout;
editShowTrayIcon.value = DEFAULT_DESKTOP_SETTINGS.show_tray_icon;
@@ -3334,6 +3340,24 @@ onUnmounted(cleanupPreviewEditor);
</div>
<div class="grid gap-3 md:grid-cols-2">
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2 md:col-span-2">
<div class="min-w-0 space-y-1">
<Label for="editor-saved-sql-open-target">{{ t("settings.savedSqlOpenTarget") }}</Label>
<p class="text-xs text-muted-foreground">
{{ editSavedSqlOpenTargetMode === "current" ? t("settings.savedSqlOpenTargetCurrentDescription") : t("settings.savedSqlOpenTargetSavedDescription") }}
</p>
</div>
<Select v-model="editSavedSqlOpenTargetMode">
<SelectTrigger id="editor-saved-sql-open-target" class="h-8 w-44 shrink-0 px-2 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="saved">{{ t("settings.savedSqlOpenTargetSaved") }}</SelectItem>
<SelectItem value="current">{{ t("settings.savedSqlOpenTargetCurrent") }}</SelectItem>
</SelectContent>
</Select>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="editor-sql-semantic-diagnostics">{{ t("settings.sqlSemanticDiagnosticsEnabled") }}</Label>
@@ -2,7 +2,7 @@
import { computed, nextTick, onBeforeUnmount, reactive, ref, watch } from "vue";
import type { CSSProperties } from "vue";
import { useI18n } from "vue-i18n";
import { ArrowDownWideNarrow, Download, FilePlus, FileText, FolderCog, FolderClosed, FolderOpen, FolderPlus, Library, LocateFixed, Pencil, Search, Trash2, Upload, X } from "@lucide/vue";
import { ArrowDownWideNarrow, Download, FilePlus, FileText, FolderCog, FolderClosed, FolderOpen, FolderPlus, Library, LocateFixed, Pencil, Play, Search, Trash2, Upload, X } from "@lucide/vue";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import CustomContextMenu, { type ContextMenuItem as CtxMenuItem } from "@/components/ui/CustomContextMenu.vue";
@@ -18,6 +18,7 @@ import { useSettingsStore } from "@/stores/settingsStore";
import { focusSidebarRenameInput } from "@/lib/sidebar/sidebarRenameFocus";
import { savedSqlFolderBranchFileCount } from "@/lib/savedSql/savedSqlFolderCounts";
import { ensureSqlExtension, stripSqlExtension } from "@/lib/savedSql/savedSqlFileName";
import { savedSqlExecutionTargetFromTab, type SavedSqlOpenTargetMode } from "@/lib/savedSql/savedSqlExecutionTarget";
import type { SavedSqlFile, SavedSqlFolder } from "@/types/database";
const { t } = useI18n();
@@ -456,7 +457,8 @@ async function openNewQueryInFolder(folder?: SavedSqlFolder) {
database: "",
sql: "",
});
queryStore.openSavedSql(file);
const tabId = queryStore.openSavedSql(file);
connectionStore.activeConnectionId = queryStore.tabs.find((tab) => tab.id === tabId)?.connectionId ?? file.connectionId;
}
// Batch selection state
@@ -468,6 +470,11 @@ const lastClickedItemIndex = ref<number | null>(null); // Unified index for both
const activeItemId = ref<string | null>(null);
const activeItemType = ref<"file" | "folder" | null>(null);
const activeSavedSqlId = computed(() => queryStore.tabs.find((tab) => tab.id === queryStore.activeTabId)?.savedSqlId ?? null);
const hasCurrentSavedSqlExecutionTarget = computed(() => {
const activeTab = queryStore.tabs.find((tab) => tab.id === queryStore.activeTabId);
const target = savedSqlExecutionTargetFromTab(activeTab);
return !!target && activeConnectionIds.value.has(target.connectionId);
});
watch(
activeSavedSqlId,
@@ -676,12 +683,12 @@ async function moveFilesToFolder(fileIds: string[], folderId?: string) {
toast(t("sqlLibrary.moveSuccess", { count: movableIds.length }), 2000);
}
async function openFile(file: SavedSqlFile) {
async function openFile(file: SavedSqlFile, targetMode?: SavedSqlOpenTargetMode) {
if (suppressNextRowClick.value) return;
const loadedFile = await savedSqlStore.ensureFileContent(file.id);
if (!loadedFile) return;
queryStore.openSavedSql(loadedFile);
connectionStore.activeConnectionId = loadedFile.connectionId;
const tabId = queryStore.openSavedSql(loadedFile, { targetMode });
connectionStore.activeConnectionId = queryStore.tabs.find((tab) => tab.id === tabId)?.connectionId ?? loadedFile.connectionId;
void savedSqlStore.recordFileUsage(loadedFile.id);
}
@@ -884,6 +891,12 @@ const contextMenuItems = computed<CtxMenuItem[]>(() => {
if ("sql" in target) {
return [
{ label: t("savedSql.open"), action: () => openFile(target), icon: FileText },
{
label: t("sqlLibrary.openInCurrentDatabase"),
action: () => openFile(target, "current"),
icon: Play,
disabled: !hasCurrentSavedSqlExecutionTarget.value,
},
{ label: t("sqlLibrary.exportFile"), action: () => exportSingleFile(target), icon: Upload },
{ label: t("sqlLibrary.moveToFolder"), icon: FolderClosed, children: folderMoveMenuItems([target.id]) },
{ label: "", separator: true },
@@ -1083,8 +1083,8 @@ async function openSavedSqlFile() {
if (node.type !== "saved-sql-file" || !node.savedSqlId) return;
const file = await savedSqlStore.ensureFileContent(node.savedSqlId);
if (!file) return;
queryStore.openSavedSql(file);
connectionStore.activeConnectionId = file.connectionId;
const tabId = queryStore.openSavedSql(file);
connectionStore.activeConnectionId = queryStore.tabs.find((tab) => tab.id === tabId)?.connectionId ?? file.connectionId;
void savedSqlStore.recordFileUsage(file.id);
}
@@ -3744,8 +3744,8 @@ function savedSqlHistoryScopeForNode(node: TreeNode): SavedSqlHistoryScope | nul
async function openSavedSqlHistoryFile(fileId: string) {
const file = await savedSqlStore.ensureFileContent(fileId);
if (!file) return;
queryStore.openSavedSql(file);
connectionStore.activeConnectionId = file.connectionId;
const tabId = queryStore.openSavedSql(file);
connectionStore.activeConnectionId = queryStore.tabs.find((tab) => tab.id === tabId)?.connectionId ?? file.connectionId;
void savedSqlStore.recordFileUsage(file.id);
}