fix(grid): keep table filters available without metadata

This commit is contained in:
t8y2
2026-06-03 21:04:19 +08:00
parent 4d50af05fd
commit f64fd09b7a
4 changed files with 126 additions and 10 deletions
@@ -41,6 +41,7 @@ import { useQueryStore } from "@/stores/queryStore";
import { canCancelQueryExecution, queryExecutionLabelKey } from "@/lib/queryExecutionState";
import { databaseDisplayNameForTab } from "@/lib/tabPresentation";
import { isTableDataEditable } from "@/lib/tableEditing";
import { tableMetaForDataTab } from "@/lib/tableDataTabMeta";
import type { QueryTab, ConnectionConfig } from "@/types/database";
import type { SqlFormatDialect } from "@/lib/sqlFormatter";
@@ -116,6 +117,8 @@ const columnVisibilityOptions = computed(
);
const redisKeyBrowserRef = ref<SearchableBrowserHandle>();
const objectBrowserRef = ref<SearchableBrowserHandle>();
const activeTableMeta = computed(() => props.activeTab.tableMeta);
const activeDataTabTableMeta = computed(() => tableMetaForDataTab(props.activeTab));
const activeSqlFormatDialect = computed<SqlFormatDialect>(() => {
switch (props.activeConnection?.db_type) {
@@ -766,13 +769,13 @@ defineExpose({ focusSearch, refreshData, handleModRTarget });
:initial-order-by-input="activeTab.orderByInput"
:sql="activeTab.sql"
:loading="activeTab.isExecuting"
:editable="isTableDataEditable(activeConnection?.db_type, activeTab.tableMeta?.primaryKeys ?? [])"
:editable="isTableDataEditable(activeConnection?.db_type, activeTableMeta?.primaryKeys ?? [])"
context="table-data"
:initial-where-input="activeTab.whereInput"
:database-type="activeConnection?.db_type"
:connection-id="activeTab.connectionId"
:database="activeTab.database"
:table-meta="activeTab.tableMeta"
:table-meta="activeDataTabTableMeta"
:page-offset="activeTab.resultPageOffset"
:page-limit="activeTab.resultPageLimit"
:on-execute-sql="async (sql: string) => emit('executeSql', sql)"
@@ -5,6 +5,7 @@ import { useQueryStore } from "@/stores/queryStore";
import { useSettingsStore } from "@/stores/settingsStore";
import { buildTableSelectSql, quoteTableIdentifier } from "@/lib/tableSelectSql";
import { editablePrimaryKeys, usesSyntheticRowIdKey } from "@/lib/tableEditing";
import { tableMetaForDataTab } from "@/lib/tableDataTabMeta";
import * as api from "@/lib/api";
import type { QueryTab } from "@/types/database";
import { useToast } from "@/composables/useToast";
@@ -26,20 +27,23 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
options: { orderBy?: string; limit?: number; offset?: number; whereInput?: string } = {},
): Promise<string> {
const config = connectionStore.getConfig(tab.connectionId);
const primaryKeys = tab.tableMeta ? editablePrimaryKeys(config?.db_type, tab.tableMeta.columns) : [];
const tableMeta = tableMetaForDataTab(tab);
const primaryKeys = tab.tableMeta
? editablePrimaryKeys(config?.db_type, tab.tableMeta.columns)
: (tableMeta?.primaryKeys ?? []);
if (tab.tableMeta && primaryKeys.join("\0") !== tab.tableMeta.primaryKeys.join("\0")) {
tab.tableMeta.primaryKeys = primaryKeys;
}
const fallbackOrderColumns =
config?.db_type === "sqlserver" && !primaryKeys.length
? tab.tableMeta?.columns.slice(0, 1).map((column) => column.name)
? tableMeta?.columns.slice(0, 1).map((column) => column.name)
: undefined;
const useRowId = usesSyntheticRowIdKey(config?.db_type, primaryKeys);
return buildTableSelectSql({
databaseType: config?.db_type,
schema: tab.tableMeta?.schema,
tableName: tab.tableMeta?.tableName ?? "",
columns: tab.tableMeta?.columns.map((column) => column.name),
schema: tableMeta?.schema,
tableName: tableMeta?.tableName ?? "",
columns: tableMeta?.columns.map((column) => column.name),
primaryKeys,
fallbackOrderColumns,
includeRowId: useRowId,
@@ -65,7 +69,7 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
) {
const tab = activeTab.value;
if (!tab) return;
if (tab.mode === "data" && tab.tableMeta) {
if (tab.mode === "data" && tableMetaForDataTab(tab)) {
tab.whereInput = whereInput ?? "";
const pageLimit = limit ?? settingsStore.editorSettings.pageSize;
const pageOffset = offset ?? 0;
@@ -116,7 +120,7 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
return;
}
if (!tab.tableMeta) return;
if (!tableMetaForDataTab(tab)) return;
tab.whereInput = whereInput ?? "";
const sql = await buildTableSql(tab, { limit, offset, whereInput, orderBy });
queryStore.updateSql(tab.id, sql);
@@ -134,7 +138,7 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
tab.resultSortDirection = direction ?? undefined;
if (tab.mode === "data") {
if (!tab.tableMeta) return;
if (!tableMetaForDataTab(tab)) return;
tab.whereInput = whereInput ?? "";
const config = connectionStore.getConfig(tab.connectionId);
const quotedColumn = quoteIdent(tab, column);
+28
View File
@@ -0,0 +1,28 @@
import type { QueryTab, ColumnInfo } from "@/types/database";
export type DataTabTableMeta = NonNullable<QueryTab["tableMeta"]>;
function fallbackColumnInfo(name: string): ColumnInfo {
return {
name,
data_type: "",
is_nullable: true,
column_default: null,
is_primary_key: false,
extra: null,
};
}
export function tableMetaForDataTab(tab: QueryTab | undefined): DataTabTableMeta | undefined {
if (!tab || tab.mode !== "data") return undefined;
if (tab.tableMeta) return tab.tableMeta;
const tableName = tab.title.trim();
if (!tableName) return undefined;
return {
schema: tab.schema,
tableName,
columns: (tab.result?.columns ?? []).map(fallbackColumnInfo),
primaryKeys: [],
};
}
@@ -0,0 +1,81 @@
import { strict as assert } from "node:assert";
import test from "node:test";
import { tableMetaForDataTab } from "../../apps/desktop/src/lib/tableDataTabMeta.ts";
import type { QueryTab } from "../../apps/desktop/src/types/database.ts";
function tab(overrides: Partial<QueryTab> = {}): QueryTab {
return {
id: "tab-1",
title: "users",
connectionId: "conn-1",
database: "app",
schema: "public",
sql: "select * from users",
isExecuting: false,
isCancelling: false,
isExplaining: false,
mode: "data",
...overrides,
};
}
test("returns persisted table metadata for a data tab", () => {
const tableMeta = {
schema: "public",
tableName: "users",
columns: [
{
name: "id",
data_type: "integer",
is_nullable: false,
column_default: null,
is_primary_key: true,
extra: null,
},
],
primaryKeys: ["id"],
};
assert.equal(tableMetaForDataTab(tab({ tableMeta })), tableMeta);
});
test("builds fallback metadata from a data tab when column metadata is unavailable", () => {
const meta = tableMetaForDataTab(
tab({
result: {
columns: ["id", "name"],
rows: [],
affected_rows: 0,
execution_time_ms: 1,
},
}),
);
assert.deepEqual(meta, {
schema: "public",
tableName: "users",
columns: [
{
name: "id",
data_type: "",
is_nullable: true,
column_default: null,
is_primary_key: false,
extra: null,
},
{
name: "name",
data_type: "",
is_nullable: true,
column_default: null,
is_primary_key: false,
extra: null,
},
],
primaryKeys: [],
});
});
test("does not infer table metadata for query tabs", () => {
assert.equal(tableMetaForDataTab(tab({ mode: "query" })), undefined);
});