mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-30 16:21:39 +00:00
fix: filter SQL completion tables
This commit is contained in:
@@ -39,6 +39,7 @@ const connectionStore = useConnectionStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const MIN_FONT_SIZE = 10;
|
||||
const MAX_FONT_SIZE = 24;
|
||||
const MAX_COMPLETION_TABLES = 200;
|
||||
let editorViewModule: typeof import("@codemirror/view") | null = null;
|
||||
let fontThemeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let codeMirrorTheme: import("@codemirror/state").Compartment | null = null;
|
||||
@@ -126,9 +127,17 @@ async function provideSqlCompletions(currentState: import("@codemirror/state").E
|
||||
try {
|
||||
const fullDoc = currentState.doc.toString();
|
||||
const completionContext = getSqlCompletionContext(fullDoc, position);
|
||||
const tables = await connectionStore.listCompletionTables(props.connectionId, props.database);
|
||||
const shouldLoadTables = completionContext.suggestTables || !!completionContext.qualifier;
|
||||
const tables = shouldLoadTables
|
||||
? await connectionStore.listCompletionTables(
|
||||
props.connectionId,
|
||||
props.database,
|
||||
completionContext.qualifier || completionContext.prefix,
|
||||
MAX_COMPLETION_TABLES,
|
||||
)
|
||||
: cachedTables;
|
||||
|
||||
// Collect referenced tables — enrich with schema from cached tables list
|
||||
// Collect referenced tables — enrich with schema from filtered table lookup
|
||||
let refs = completionContext.referencedTables.map((rt) => {
|
||||
// If no schema, look it up in the cached tables
|
||||
if (!rt.schema) {
|
||||
@@ -139,6 +148,20 @@ async function provideSqlCompletions(currentState: import("@codemirror/state").E
|
||||
}
|
||||
return rt;
|
||||
});
|
||||
const unresolvedRefs = refs.filter((rt) => !rt.schema);
|
||||
if (unresolvedRefs.length > 0) {
|
||||
const lookupGroups = await Promise.all(
|
||||
unresolvedRefs.map((rt) =>
|
||||
connectionStore.listCompletionTables(props.connectionId!, props.database!, rt.name, 20),
|
||||
),
|
||||
);
|
||||
const lookupTables = lookupGroups.flat();
|
||||
refs = refs.map((rt) => {
|
||||
if (rt.schema) return rt;
|
||||
const matched = lookupTables.find((table) => table.name.toLowerCase() === rt.name.toLowerCase());
|
||||
return matched?.schema ? { ...rt, schema: matched.schema } : rt;
|
||||
});
|
||||
}
|
||||
|
||||
// If no referenced tables but qualifier exists, infer table from tables list
|
||||
if (refs.length === 0 && completionContext.qualifier) {
|
||||
@@ -200,13 +223,7 @@ async function provideSqlCompletions(currentState: import("@codemirror/state").E
|
||||
}
|
||||
|
||||
async function refreshCompletionCache() {
|
||||
if (!props.connectionId || !props.database) return;
|
||||
try {
|
||||
cachedTables = await connectionStore.listCompletionTables(props.connectionId, props.database);
|
||||
} catch {
|
||||
cachedTables = [];
|
||||
}
|
||||
// Clear column cache when connection/database changes
|
||||
cachedTables = [];
|
||||
cachedColumnsByTable.clear();
|
||||
}
|
||||
|
||||
@@ -370,7 +387,12 @@ onMounted(async () => {
|
||||
try {
|
||||
// Ensure table cache is populated
|
||||
if (cachedTables.length === 0) {
|
||||
cachedTables = await connectionStore.listCompletionTables(props.connectionId!, props.database!);
|
||||
cachedTables = await connectionStore.listCompletionTables(
|
||||
props.connectionId!,
|
||||
props.database!,
|
||||
identifier,
|
||||
MAX_COMPLETION_TABLES,
|
||||
);
|
||||
}
|
||||
|
||||
// 1. Check if it's a table name
|
||||
@@ -463,8 +485,7 @@ onMounted(async () => {
|
||||
|
||||
view.value = new EditorView({ state, parent: editorRef.value });
|
||||
|
||||
// Fetch completion data
|
||||
refreshCompletionCache();
|
||||
cachedTables = [];
|
||||
});
|
||||
|
||||
watch(
|
||||
|
||||
+8
-2
@@ -172,8 +172,14 @@ export async function listSchemas(connectionId: string, database: string): Promi
|
||||
return get(`/api/schema/schemas?${qs({ connection_id: connectionId, database })}`);
|
||||
}
|
||||
|
||||
export async function listTables(connectionId: string, database: string, schema: string): Promise<TableInfo[]> {
|
||||
return get(`/api/schema/tables?${qs({ connection_id: connectionId, database, schema })}`);
|
||||
export async function listTables(
|
||||
connectionId: string,
|
||||
database: string,
|
||||
schema: string,
|
||||
filter?: string,
|
||||
limit?: number,
|
||||
): Promise<TableInfo[]> {
|
||||
return get(`/api/schema/tables?${qs({ connection_id: connectionId, database, schema, filter, limit })}`);
|
||||
}
|
||||
|
||||
export async function listObjects(connectionId: string, database: string, schema: string): Promise<ObjectInfo[]> {
|
||||
|
||||
@@ -133,6 +133,7 @@ const SQL_KEYWORDS = [
|
||||
|
||||
const TABLE_TRIGGER_KEYWORDS = new Set(["from", "join", "update", "into", "table", "describe", "explain", "apply"]);
|
||||
const JOIN_MODIFIERS = new Set(["left", "right", "inner", "outer", "cross", "full", "natural"]);
|
||||
const MAX_TABLE_COMPLETION_ITEMS = 200;
|
||||
|
||||
export interface SqlCompletionTable {
|
||||
name: string;
|
||||
@@ -463,6 +464,7 @@ function unquoteIdentifier(value: string): string {
|
||||
function buildTableItems(prefix: string, tables: SqlCompletionTable[]): SqlCompletionItem[] {
|
||||
return tables
|
||||
.filter((table) => matchesPrefix(table.name, prefix))
|
||||
.slice(0, MAX_TABLE_COMPLETION_ITEMS)
|
||||
.map((table) => ({
|
||||
label: table.name,
|
||||
type: "table" as const,
|
||||
|
||||
+8
-2
@@ -138,8 +138,14 @@ export async function deleteSchemaCachePrefix(prefix: string): Promise<void> {
|
||||
return invoke("delete_schema_cache_prefix", { prefix });
|
||||
}
|
||||
|
||||
export async function listTables(connectionId: string, database: string, schema: string): Promise<TableInfo[]> {
|
||||
return invoke("list_tables", { connectionId, database, schema });
|
||||
export async function listTables(
|
||||
connectionId: string,
|
||||
database: string,
|
||||
schema: string,
|
||||
filter?: string,
|
||||
limit?: number,
|
||||
): Promise<TableInfo[]> {
|
||||
return invoke("list_tables", { connectionId, database, schema, filter, limit });
|
||||
}
|
||||
|
||||
export async function listObjects(connectionId: string, database: string, schema: string): Promise<ObjectInfo[]> {
|
||||
|
||||
@@ -990,8 +990,14 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function listCompletionTables(connectionId: string, database: string): Promise<SqlCompletionTable[]> {
|
||||
const cacheKey = `${connectionId}:${database}`;
|
||||
async function listCompletionTables(
|
||||
connectionId: string,
|
||||
database: string,
|
||||
filter = "",
|
||||
limit?: number,
|
||||
): Promise<SqlCompletionTable[]> {
|
||||
const normalizedFilter = filter.trim().toLowerCase();
|
||||
const cacheKey = `${connectionId}:${database}:${normalizedFilter}:${limit ?? ""}`;
|
||||
if (completionTablesCache.value[cacheKey]) {
|
||||
return completionTablesCache.value[cacheKey];
|
||||
}
|
||||
@@ -1000,6 +1006,29 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
|
||||
if (isSchemaAwareDatabase(connectionId)) {
|
||||
const schemas = await api.listSchemas(connectionId, database);
|
||||
if (normalizedFilter || limit) {
|
||||
const limitedTables: SqlCompletionTable[] = [];
|
||||
for (const schema of schemas) {
|
||||
try {
|
||||
const remaining = limit ? Math.max(limit - limitedTables.length, 0) : undefined;
|
||||
if (remaining === 0) break;
|
||||
const tables = await api.listTables(connectionId, database, schema, normalizedFilter, remaining);
|
||||
limitedTables.push(
|
||||
...tables.map((table) => ({
|
||||
name: table.name,
|
||||
schema,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
})),
|
||||
);
|
||||
} catch {
|
||||
/* ignore schema failures */
|
||||
}
|
||||
}
|
||||
completionTablesCache.value[cacheKey] = limitedTables;
|
||||
evictOldestCacheEntries(completionTablesCache.value, COMPLETION_CACHE_MAX);
|
||||
return completionTablesCache.value[cacheKey];
|
||||
}
|
||||
|
||||
const tableGroups = await Promise.all(
|
||||
schemas.map(async (schema) => {
|
||||
try {
|
||||
@@ -1019,7 +1048,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return completionTablesCache.value[cacheKey];
|
||||
}
|
||||
|
||||
const tables = await api.listTables(connectionId, database, database);
|
||||
const tables = await api.listTables(connectionId, database, database, normalizedFilter, limit);
|
||||
completionTablesCache.value[cacheKey] = tables.map((table) => ({
|
||||
name: table.name,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
|
||||
Reference in New Issue
Block a user