feat(query): add manual transaction controls
This commit is contained in:
1 parent
f871e00330
commit
d55a708d6a
27 files changed
+927
-40
No files matched your search
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, watchEffect } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Play, Loader2, Square, Database, Check, Table2, AlignLeft, GitBranch, Save, FolderOpen, Layers, X, Shield, Upload } from "@lucide/vue";
|
||||
import { Play, Loader2, Square, Database, Check, Table2, AlignLeft, GitBranch, Save, FolderOpen, Layers, X, Shield, Upload, RotateCcw, AlertTriangle } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { SearchableSelect } from "@/components/ui/searchable-select";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
@@ -13,7 +13,7 @@ import { useSchemaOptions } from "@/composables/useSchemaOptions";
|
||||
import { connectionIconType } from "@/lib/connectionPresentation";
|
||||
import { formatDatabaseLabel, isDefaultDatabase } from "@/lib/defaultDatabase";
|
||||
import { connectionDisplayName } from "@/lib/tabPresentation";
|
||||
import { isSingleDatabase } from "@/lib/databaseCapabilities";
|
||||
import { isSingleDatabase, supportsTransaction as supportsTransactionFeature } from "@/lib/databaseCapabilities";
|
||||
import { hexToRgba } from "@/lib/color";
|
||||
import type { QueryTab, ConnectionConfig } from "@/types/database";
|
||||
|
||||
@@ -25,6 +25,9 @@ const props = defineProps<{
|
||||
blockDangerousRedisCommands?: boolean;
|
||||
sqlKeywordCase: "preserve" | "upper" | "lower";
|
||||
databaseRequiredSignal?: number;
|
||||
autoCommit?: boolean;
|
||||
txnSessionId?: string;
|
||||
txnAutoRolledBack?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -43,6 +46,10 @@ const emit = defineEmits<{
|
||||
setDefaultDatabase: [];
|
||||
clearDefaultDatabase: [];
|
||||
"update:blockDangerousRedisCommands": [value: boolean];
|
||||
"update:autoCommit": [value: boolean];
|
||||
commit: [];
|
||||
rollback: [];
|
||||
dismissTxnRolledBack: [];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -64,12 +71,22 @@ const supportsExplain = computed(() => {
|
||||
return dbType !== "redis" && dbType !== "mongodb" && dbType !== "elasticsearch" && dbType !== "qdrant" && dbType !== "milvus" && dbType !== "weaviate" && dbType !== "chromadb" && dbType !== "etcd" && dbType !== "zookeeper" && dbType !== "mq" && dbType !== "nacos";
|
||||
});
|
||||
const isSingleDb = computed(() => isSingleDatabase(props.activeConnection?.db_type));
|
||||
const supportsTransaction = computed(() => supportsTransactionFeature(props.activeConnection?.db_type));
|
||||
const hasDefaultDatabaseOption = computed(() => activeDatabaseOptions.value.includes(""));
|
||||
const schemaDatabaseKey = computed(() => props.activeTab.database || (isSingleDb.value ? "_" : ""));
|
||||
const saveTooltip = computed(() => (props.activeTab.objectSource ? t("objects.saveSource") : t("toolbar.saveSql")));
|
||||
const canSaveSql = computed(() => !!props.activeTab.externalSqlPath || !!props.activeTab.sql.trim());
|
||||
const keywordCaseIsLower = computed(() => props.sqlKeywordCase === "lower");
|
||||
const keywordCaseToggleTooltip = computed(() => (keywordCaseIsLower.value ? t("toolbar.keywordCaseUpper") : t("toolbar.keywordCaseLower")));
|
||||
const transactionTooltip = computed(() => {
|
||||
const isAgent = (props.activeConnection?.db_type as string) === "agent";
|
||||
const isManual = props.autoCommit === false;
|
||||
if (isAgent && isManual) return t("toolbar.manualTransactionAgent");
|
||||
if (isAgent) return t("toolbar.autoCommitAgent");
|
||||
return isManual ? t("toolbar.manualTransaction") : t("toolbar.autoCommit");
|
||||
});
|
||||
|
||||
const isTransactionActive = computed(() => !!props.txnSessionId);
|
||||
|
||||
const showSchemaSelector = computed(() => {
|
||||
const connection = props.activeConnection;
|
||||
@@ -175,6 +192,41 @@ function connectionById(connectionId: string): ConnectionConfig | undefined {
|
||||
>
|
||||
<span class="font-bold" style="font-size: 9px">A</span>
|
||||
</Button>
|
||||
<!-- Transaction toggle -->
|
||||
<Tooltip v-if="supportsTransaction">
|
||||
<TooltipTrigger as-child>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-6 w-6"
|
||||
:class="isTransactionActive || autoCommit === false ? 'text-orange-600 bg-orange-100 dark:text-orange-300 dark:bg-orange-900/30' : 'text-muted-foreground/50'"
|
||||
:disabled="activeTab.isExecuting || activeTab.isExplaining"
|
||||
@click="emit('update:autoCommit', autoCommit === false)"
|
||||
>
|
||||
<span class="font-bold" style="font-size: 9px">Tx</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ transactionTooltip }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<!-- Commit button (only when transaction is active) -->
|
||||
<Tooltip v-if="isTransactionActive">
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 text-green-600 hover:bg-green-500/10 hover:text-green-700 dark:text-green-300 dark:hover:text-green-200" :disabled="activeTab.isExecuting" @click="emit('commit')">
|
||||
<Check class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("toolbar.commit") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<!-- Rollback button (only when transaction is active) -->
|
||||
<Tooltip v-if="isTransactionActive">
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 text-red-600 hover:bg-red-500/10 hover:text-red-700 dark:text-red-300 dark:hover:text-red-200" :disabled="activeTab.isExecuting" @click="emit('rollback')">
|
||||
<RotateCcw class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("toolbar.rollback") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 text-amber-600 hover:bg-amber-500/10 hover:text-amber-700 dark:text-amber-300 dark:hover:text-amber-200" :disabled="activeTab.isExecuting || activeTab.isExplaining || !activeTab.sql.trim()" @click="emit('formatSql')">
|
||||
@@ -342,6 +394,13 @@ function connectionById(connectionId: string): ConnectionConfig | undefined {
|
||||
<span class="truncate">{{ activeTab.tableMeta.columns.length }} {{ t("tree.columns") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="txnAutoRolledBack" class="flex items-center gap-2 px-3 py-1 text-xs bg-amber-500/10 text-amber-700 dark:text-amber-300 border-b border-amber-500/20">
|
||||
<AlertTriangle class="h-3.5 w-3.5 shrink-0" />
|
||||
<span>{{ t("toolbar.txnAutoRolledBack") }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="emit('dismissTxnRolledBack')">
|
||||
<X class="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in new issue
Block a user