Skip to content

Commit 9876ab8

Browse files
committed
feat(sql): 连接未指定库时查询构建器内可选库(列库/切库,表名自动带库限定)
1 parent 5d47500 commit 9876ab8

3 files changed

Lines changed: 93 additions & 11 deletions

File tree

src/components/QueryBuilder.vue

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@
5151

5252
<div v-if="loading" class="flex-1 flex items-center justify-center text-sm text-gray-400">{{ t('qb.loading') }}</div>
5353
<div v-else-if="error" class="flex-1 flex items-center justify-center text-sm text-red-500 px-6 text-center">{{ error }}</div>
54+
<!-- MySQL 连接未指定库:先选库 -->
55+
<div v-else-if="srcNoDb && !pickedDb" class="flex-1 flex flex-col items-center justify-center gap-3 text-sm text-gray-400 px-6 text-center">
56+
<span>{{ t('qb.pickDbFirst') }}</span>
57+
<select class="text-sm rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-3 py-1.5 focus:outline-none cursor-pointer min-w-[200px]"
58+
:value="''" @change="onPickDb($event)">
59+
<option value="" disabled>{{ t('qb.selectDb') }}</option>
60+
<option v-for="d in databases" :key="d" :value="d">{{ d }}</option>
61+
</select>
62+
</div>
5463
<div v-else-if="tables.length === 0" class="flex-1 flex items-center justify-center text-sm text-gray-400 px-6 text-center">{{ t('qb.noTables') }}</div>
5564

5665
<div v-else class="flex-1 flex min-h-0">
@@ -82,6 +91,12 @@
8291
<div class="rounded border border-gray-200 dark:border-gray-700">
8392
<div class="px-2.5 py-1 text-[11px] font-medium text-gray-500">FROM / JOIN</div>
8493
<div class="px-2.5 pb-2 flex flex-col gap-1.5">
94+
<div v-if="srcNoDb" class="flex items-center gap-1.5">
95+
<span class="text-[11px] text-gray-400 w-10 flex-shrink-0">{{ t('qb.db') }}</span>
96+
<select :value="pickedDb" class="text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 focus:outline-none min-w-[160px]" @change="onPickDb($event)">
97+
<option v-for="d in databases" :key="d" :value="d">{{ d }}</option>
98+
</select>
99+
</div>
85100
<div class="flex items-center gap-1.5">
86101
<span class="text-[11px] text-gray-400 w-10 flex-shrink-0">FROM</span>
87102
<select v-model="table" class="text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 focus:outline-none min-w-[160px]">
@@ -332,6 +347,11 @@ const loading = ref(false)
332347
const error = ref('')
333348
const tables = ref<Tbl[]>([])
334349
350+
// MySQL 连接未指定库时:需先在构建器内选库
351+
const srcNoDb = ref(false)
352+
const databases = ref<string[]>([])
353+
const pickedDb = ref('')
354+
335355
const table = ref('')
336356
const joins = ref<Join[]>([])
337357
const selectItems = ref<SelItem[]>([])
@@ -475,6 +495,8 @@ watch(table, () => {
475495
const kind = () => resolveActiveSource().kind
476496
const q = (name: string) => quoteIdent(kind(), name)
477497
const qRef = (r: ColRef) => (multiTable.value ? `${q(r.t)}.${q(r.c)}` : q(r.c))
498+
// FROM/JOIN 表名:连接未指定库时用 库.表 限定
499+
const qTable = (name: string) => (srcNoDb.value && pickedDb.value ? `${q(pickedDb.value)}.${q(name)}` : q(name))
478500
479501
// 列引用 + 可选函数与额外参数:fn(col, args)
480502
const exprSql = (r: ColRef & { fn?: string; args?: string }) => wrapFunc(qRef(r), r.fn, r.args)
@@ -540,9 +562,9 @@ const sql = computed(() => {
540562
return ''
541563
}
542564
const cols = selectItems.value.length === 0 ? '*' : selectItems.value.map(itemSql).join(', ')
543-
let out = `SELECT ${distinct.value ? 'DISTINCT ' : ''}${cols} FROM ${q(table.value)}`
565+
let out = `SELECT ${distinct.value ? 'DISTINCT ' : ''}${cols} FROM ${qTable(table.value)}`
544566
for (const j of joins.value) {
545-
out += ` ${j.type} JOIN ${q(j.table)} ON ${qRef({t: j.leftT, c: j.leftC})} = ${q(j.table)}.${q(j.rightC)}`
567+
out += ` ${j.type} JOIN ${qTable(j.table)} ON ${qRef({t: j.leftT, c: j.leftC})} = ${q(j.table)}.${q(j.rightC)}`
546568
}
547569
const conds = wheres.value.map(condSql).filter(Boolean)
548570
if (conds.length) {
@@ -567,6 +589,7 @@ const sql = computed(() => {
567589
// ---- 状态记忆:按数据源持久化 ----
568590
interface QbState {
569591
table: string
592+
pickedDb?: string
570593
joins?: Join[]
571594
selectItems?: any[]
572595
distinct?: boolean
@@ -584,6 +607,7 @@ const persist = () => {
584607
}
585608
kvSetJSON(stateKey(), {
586609
table: table.value,
610+
pickedDb: pickedDb.value,
587611
joins: joins.value,
588612
selectItems: selectItems.value,
589613
distinct: distinct.value,
@@ -633,6 +657,7 @@ const saveName = ref('')
633657
const showSave = ref(false)
634658
const currentState = (): QbState => ({
635659
table: table.value,
660+
pickedDb: pickedDb.value,
636661
joins: joins.value,
637662
selectItems: selectItems.value,
638663
distinct: distinct.value,
@@ -690,19 +715,37 @@ const reset = () => {
690715
})
691716
}
692717
718+
const runRows = async (source: any, sqlStr: string): Promise<any[][]> => {
719+
const res = await invoke<any>('run_sql', {sql: sqlStr, source})
720+
if (res.error) {
721+
throw new Error(res.error)
722+
}
723+
return (res.result_sets || [])[0]?.rows || []
724+
}
725+
const fetchTables = async (source: any, db?: string) => groupTables(await runRows(source, columnsSql(source.kind, db)))
726+
693727
const load = async () => {
694728
loading.value = true
695729
error.value = ''
696730
try {
697731
const source = resolveActiveSource()
698-
const db = source.kind === 'mysql' ? source.database || undefined : undefined
699-
const res = await invoke<any>('run_sql', {sql: columnsSql(source.kind, db), source})
700-
if (res.error) {
701-
throw new Error(res.error)
702-
}
703-
tables.value = groupTables((res.result_sets || [])[0]?.rows || [])
704732
savedList.value = kvGetJSON<SavedQuery[]>(savedKey(), [])
705-
const restored = await restoreState()
733+
srcNoDb.value = source.kind === 'mysql' && !source.database
734+
if (srcNoDb.value) {
735+
// 连接未指定库:先列出可选库,并尝试恢复上次所选库
736+
databases.value = (await runRows(source,
737+
'SELECT schema_name FROM information_schema.schemata '
738+
+ "WHERE schema_name NOT IN ('information_schema','mysql','performance_schema','sys') "
739+
+ 'ORDER BY schema_name')).map(r => String(r[0]))
740+
const savedDb = kvGetJSON<QbState | null>(stateKey(), null)?.pickedDb
741+
pickedDb.value = savedDb && databases.value.includes(savedDb) ? savedDb : ''
742+
tables.value = pickedDb.value ? await fetchTables(source, pickedDb.value) : []
743+
}
744+
else {
745+
pickedDb.value = ''
746+
tables.value = await fetchTables(source, undefined)
747+
}
748+
const restored = tables.value.length ? await restoreState() : false
706749
if (!restored && tables.value.length && !tables.value.find(tb => tb.name === table.value)) {
707750
table.value = tables.value[0].name
708751
}
@@ -716,6 +759,39 @@ const load = async () => {
716759
}
717760
}
718761
762+
// 选择/切换数据库:加载该库表并重置构建器
763+
const onPickDb = async (e: Event) => {
764+
const db = (e.target as HTMLSelectElement).value
765+
if (!db || db === pickedDb.value) {
766+
return
767+
}
768+
loading.value = true
769+
try {
770+
const source = resolveActiveSource()
771+
const list = await fetchTables(source, db)
772+
restoring = true
773+
pickedDb.value = db
774+
tables.value = list
775+
joins.value = []
776+
selectItems.value = []
777+
distinct.value = false
778+
groupBy.value = []
779+
wheres.value = []
780+
havings.value = []
781+
orders.value = []
782+
table.value = list[0]?.name || ''
783+
await nextTick()
784+
restoring = false
785+
persist()
786+
}
787+
catch (e: any) {
788+
error.value = e?.message || String(e)
789+
}
790+
finally {
791+
loading.value = false
792+
}
793+
}
794+
719795
const openBuilder = () => {
720796
visible.value = true
721797
load()

src/i18n/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,10 @@
10511051
"dropHere": "Release to add {col}",
10521052
"reset": "Reset",
10531053
"loading": "Loading schema…",
1054-
"noTables": "No tables in the current source (pick a database first for MySQL)",
1054+
"noTables": "No tables in the current source",
1055+
"db": "Database",
1056+
"pickDbFirst": "This connection has no database selected — pick one first",
1057+
"selectDb": "Select a database…",
10551058
"tables": "Tables",
10561059
"columns": "Columns",
10571060
"value": "Value",

src/i18n/locales/zh-CN.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,10 @@
10511051
"dropHere": "松开以添加 {col}",
10521052
"reset": "清空",
10531053
"loading": "加载表结构中…",
1054-
"noTables": "当前数据源没有可用的表(MySQL 请先选择数据库)",
1054+
"noTables": "当前数据源没有可用的表",
1055+
"db": "数据库",
1056+
"pickDbFirst": "当前连接未指定数据库,请先选择一个数据库",
1057+
"selectDb": "选择数据库…",
10551058
"tables": "",
10561059
"columns": "",
10571060
"value": "",

0 commit comments

Comments
 (0)