fix(sqlserver): honor USE context in completions
This commit is contained in:
1 parent
f64d363abe
commit
486a73ef09
16 files changed
+974
-114
No files matched your search
@@ -45,7 +45,18 @@ import { buildSqlSemanticModel } from "@/lib/sql/semantic/model";
|
||||
import { mergeSqlSemanticReferenceAnalysis, resolveSqlSemanticNavigationTarget } from "@/lib/sql/semantic/references";
|
||||
import { buildElasticsearchCompletionItemsFromContext, getElasticsearchCompletionContext, getElasticsearchCompletionResultValidFor, shouldAutoOpenElasticsearchCompletion, type ElasticsearchCompletionItem } from "@/lib/elasticsearch/elasticsearchCompletion";
|
||||
import { buildMongoCompletionItemsFromContext, getMongoCompletionContext, getMongoCompletionResultValidFor, mongoCompletionNeedsCollections, mongoCompletionNeedsFields, shouldAutoOpenMongoCompletion, type MongoCompletionItem } from "@/lib/mongo/mongoCompletion";
|
||||
import { mergeSqlCompletionQualifierNames, resolveSqlCompletionRoutineLookupTarget, resolveSqlCompletionSchemaLookupDatabase, resolveSqlCompletionTableLookupTarget } from "@/lib/sql/sqlCompletionLookupTarget";
|
||||
import {
|
||||
buildSqlServerUseDatabaseCompletionItems,
|
||||
mergeSqlCompletionQualifierNames,
|
||||
resolveSqlCompletionRoutineLookupTarget,
|
||||
resolveSqlCompletionSchemaLookupDatabase,
|
||||
resolveSqlCompletionScope,
|
||||
resolveSqlCompletionTableLookupTarget,
|
||||
resolveSqlServerUseDatabaseCompletion,
|
||||
sqlServerUseCompletionDatabaseNames,
|
||||
sqlServerUseDatabaseBeforeCursor,
|
||||
type SqlCompletionScope,
|
||||
} from "@/lib/sql/sqlCompletionLookupTarget";
|
||||
import { usesOracleSessionCompletionColumns as shouldUseOracleSessionCompletionColumns } from "@/lib/sql/oracleCompletionSession";
|
||||
import { extractIdentifierDetailsAt, isSqlKeyword, matchTable, mergeSqlObjectNavigationType, splitQualifiedIdentifier, sqlObjectHoverDetail, sqlObjectNavigationSourceKind, sqlObjectNavigationTarget, type SqlObjectNavigationTarget } from "@/lib/sql/sqlNavigation";
|
||||
import { buildHoverTableSql, ddlForHoverPreview, hoverTableMatchesScope, quoteQualifiedName, reformatHoverDdl, scopeHoverTables, type HoverTableScope } from "@/lib/editor/hoverTableSql";
|
||||
@@ -102,7 +113,7 @@ import { sqlReferenceAnalysisDialectFor } from "@/lib/sql/semantic/dialect";
|
||||
import { buildRedisSyntaxDiagnostics, shouldRunRedisDiagnostics } from "@/lib/redis/redisSyntaxDiagnostics";
|
||||
import { buildRedisCompletionItemsFromContext, getRedisCompletionContext, getRedisCompletionResultValidFor, shouldAutoOpenRedisCompletion, takesKeyArgument, type RedisCompletionItem } from "@/lib/redis/redisCompletion";
|
||||
import type { SqlCompletionColumn, SqlCompletionForeignKey, SqlCompletionItem, SqlCompletionObject, SqlCompletionReferencedTable, SqlCompletionTable } from "@/lib/sql/sqlCompletion";
|
||||
import type { CompletionAssistantObjectKind, ColumnInfo, DatabaseType, IndexInfo, SqlReferenceAnalysis, SqlTableReference, SqlTextSpan } from "@/types/database";
|
||||
import type { CompletionAssistantObjectKind, ColumnInfo, DatabaseType, IndexInfo, SqlReferenceAnalysis, SqlServerCompletionContext, SqlTableReference, SqlTextSpan } from "@/types/database";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
@@ -422,7 +433,7 @@ function editorThemeAppearance() {
|
||||
|
||||
// Completion cache
|
||||
let cachedTables: SqlCompletionTable[] = [];
|
||||
let cachedCompletionObjects: SqlCompletionObject[] = [];
|
||||
const cachedCompletionObjectsByScope = new Map<string, SqlCompletionObject[]>();
|
||||
// Persistent column cache keyed by "schema.table" or "table"
|
||||
const cachedColumnsByTable = new Map<string, SqlCompletionColumn[]>();
|
||||
const cachedInsertValueHintColumnsByTable = new Map<string, string[]>();
|
||||
@@ -1612,9 +1623,12 @@ function identifierRangeAt(sql: string, pos: number): { from: number; to: number
|
||||
return { from, to, text };
|
||||
}
|
||||
|
||||
function completionCacheKey(table: { name: string; catalog?: string | null; database?: string | null; schema?: string | null }) {
|
||||
const schema = table.schema ?? props.schema;
|
||||
const database = supportsDatabaseSchemaQualifierCompletion() ? table.database : undefined;
|
||||
type CompletionMetadataScope = Pick<SqlCompletionScope, "database" | "schema">;
|
||||
|
||||
function completionCacheKey(table: { name: string; catalog?: string | null; database?: string | null; schema?: string | null }, scope?: CompletionMetadataScope) {
|
||||
const schema = table.schema ?? scope?.schema ?? props.schema;
|
||||
const scopedDatabase = scope && scope.database !== props.database ? scope.database : undefined;
|
||||
const database = supportsDatabaseSchemaQualifierCompletion() ? (table.database ?? scopedDatabase) : undefined;
|
||||
return schema ? `${database ? `${database}.` : ""}${schema}.${table.name}` : table.name;
|
||||
}
|
||||
|
||||
@@ -1697,15 +1711,16 @@ function allowsOnDemandQualifiedTableCompletion(prefix: string): boolean {
|
||||
return prefix.trim().length >= PRESTO_ON_DEMAND_TABLE_COMPLETION_MIN_PREFIX;
|
||||
}
|
||||
|
||||
function completionMetadataTarget(table: { name: string; catalog?: string | null; database?: string | null; schema?: string | null }): { database: string; schema?: string; catalog?: string } | null {
|
||||
if (props.database == null) return null;
|
||||
function completionMetadataTarget(table: { name: string; catalog?: string | null; database?: string | null; schema?: string | null }, scope?: CompletionMetadataScope): { database: string; schema?: string; catalog?: string } | null {
|
||||
const currentDatabase = scope?.database ?? props.database;
|
||||
if (currentDatabase == null) return null;
|
||||
if (supportsDatabaseSchemaQualifierCompletion() && table.database) {
|
||||
return { database: table.database, schema: table.schema ?? undefined, catalog: table.catalog ?? props.catalog };
|
||||
}
|
||||
if (supportsDatabaseQualifierCompletion() && table.schema) {
|
||||
return { database: table.schema, catalog: table.catalog ?? props.catalog };
|
||||
}
|
||||
return { database: props.database, schema: table.schema ?? props.schema, catalog: table.catalog ?? props.catalog };
|
||||
return { database: currentDatabase, schema: table.schema ?? scope?.schema ?? props.schema, catalog: table.catalog ?? props.catalog };
|
||||
}
|
||||
|
||||
function isVirtualCompletionTableReference(table: { name: string; database?: string | null; schema?: string | null }): boolean {
|
||||
@@ -2446,7 +2461,8 @@ function isTypedCompletionActivation(explicit: boolean) {
|
||||
}
|
||||
|
||||
function markCompletionAccepted(item: QueryCompletionItem) {
|
||||
suppressNextSqlCompletionAutoStartUntil = shouldChainSqlCompletionAfterAccept(item) ? 0 : Date.now() + 750;
|
||||
const shouldContinueCompletion = shouldChainSqlCompletionAfterAccept(item) || (props.databaseType === "sqlserver" && item.type === "keyword" && item.label.toUpperCase() === "USE");
|
||||
suppressNextSqlCompletionAutoStartUntil = shouldContinueCompletion ? 0 : Date.now() + 750;
|
||||
completionEpoch++;
|
||||
}
|
||||
|
||||
@@ -2479,15 +2495,17 @@ function mayCompleteDatabaseSchemaQualifier(completionContext: ReturnType<typeof
|
||||
return (completionContext.qualifierParts?.filter(Boolean).length ?? completionContext.qualifier?.split(".").filter(Boolean).length ?? 0) === 1;
|
||||
}
|
||||
|
||||
function localCompletionSchemasForDatabaseDisambiguation(completionContext: ReturnType<typeof getSqlCompletionContext>, databaseNames: string[]): string[] {
|
||||
if (!props.connectionId || props.database == null || !mayCompleteDatabaseSchemaQualifier(completionContext)) return [];
|
||||
function localCompletionSchemasForDatabaseDisambiguation(completionContext: ReturnType<typeof getSqlCompletionContext>, databaseNames: string[], scope?: CompletionMetadataScope): string[] {
|
||||
const currentDatabase = scope?.database ?? props.database;
|
||||
const currentSchema = scope?.schema ?? props.schema;
|
||||
if (!props.connectionId || currentDatabase == null || !mayCompleteDatabaseSchemaQualifier(completionContext)) return [];
|
||||
const database = resolveSqlCompletionSchemaLookupDatabase({
|
||||
supportsDatabaseSchemaQualifier: true,
|
||||
completionContext,
|
||||
knownDatabases: databaseNames,
|
||||
});
|
||||
if (!database) return [];
|
||||
return mergeSqlCompletionQualifierNames(props.schema ? [props.schema] : [], connectionStore.lookupLocalCompletionSchemas(props.connectionId, props.database, completionContext.qualifier, MAX_COMPLETION_TABLES));
|
||||
return mergeSqlCompletionQualifierNames(currentSchema ? [currentSchema] : [], connectionStore.lookupLocalCompletionSchemas(props.connectionId, currentDatabase, completionContext.qualifier, MAX_COMPLETION_TABLES));
|
||||
}
|
||||
|
||||
function shouldInsertSqlCompletionSpace(): boolean {
|
||||
@@ -2672,11 +2690,44 @@ async function provideSqlCompletions(context: CompletionContext) {
|
||||
|
||||
try {
|
||||
if (isSqlCompletionSuppressedContext(fullDoc, position)) return null;
|
||||
if (!explicit && !shouldAutoOpenSqlCompletion(fullDoc, position, sqlCompletionDialectOptions())) return null;
|
||||
const useDatabaseCompletion = resolveSqlServerUseDatabaseCompletion({
|
||||
sql: fullDoc,
|
||||
cursor: position,
|
||||
databaseType: props.databaseType,
|
||||
});
|
||||
if (!explicit && !useDatabaseCompletion && !shouldAutoOpenSqlCompletion(fullDoc, position, sqlCompletionDialectOptions())) return null;
|
||||
|
||||
if (useDatabaseCompletion) {
|
||||
const currentDatabase = props.database ?? "";
|
||||
if (!currentDatabase) return null;
|
||||
let sqlServerContext: SqlServerCompletionContext;
|
||||
try {
|
||||
sqlServerContext = await connectionStore.getSqlServerCompletionContext(props.connectionId, currentDatabase);
|
||||
} catch {
|
||||
// Without a server-reported capability, do not suggest a USE target
|
||||
// that the current SQL Server session may be unable to switch to.
|
||||
return null;
|
||||
}
|
||||
if (sqlServerContext.supports_session_database_switch) {
|
||||
try {
|
||||
await connectionStore.listCompletionDatabases(props.connectionId);
|
||||
} catch {
|
||||
// Keep locally indexed database names available when metadata refresh fails.
|
||||
}
|
||||
}
|
||||
if (epoch !== completionEpoch) return null;
|
||||
const databaseNames = sqlServerUseCompletionDatabaseNames({
|
||||
databaseNames: connectionStore.lookupLocalCompletionDatabases(props.connectionId, useDatabaseCompletion.prefix, MAX_COMPLETION_TABLES),
|
||||
currentDatabase,
|
||||
supportsSessionDatabaseSwitch: sqlServerContext.supports_session_database_switch,
|
||||
});
|
||||
const items = buildSqlServerUseDatabaseCompletionItems(databaseNames, useDatabaseCompletion);
|
||||
return buildCompletionResult(items, useDatabaseCompletion.from);
|
||||
}
|
||||
|
||||
const legacyCompletionContext = getSqlCompletionContext(fullDoc, position, sqlCompletionDialectOptions());
|
||||
const semanticModel = SEMANTIC_SQL_COMPLETION_ENABLED ? buildSqlSemanticModel(fullDoc, position, sqlCompletionDialectOptions()) : null;
|
||||
const completionContext = semanticModel ? sqlCompletionContextFromSemantic(semanticModel, legacyCompletionContext) : legacyCompletionContext;
|
||||
let completionContext = semanticModel ? sqlCompletionContextFromSemantic(semanticModel, legacyCompletionContext) : legacyCompletionContext;
|
||||
|
||||
if (!hasDatabase) {
|
||||
const items = buildSqlCompletionItemsFromContext(completionContext, {
|
||||
@@ -2695,6 +2746,45 @@ async function provideSqlCompletions(context: CompletionContext) {
|
||||
return buildCompletionResult(items, position - completionContext.prefix.length, getSqlCompletionResultValidFor(fullDoc, position));
|
||||
}
|
||||
|
||||
const useDatabase = props.databaseType === "sqlserver" ? sqlServerUseDatabaseBeforeCursor(fullDoc, position) : undefined;
|
||||
let knownUseDatabases: string[] | undefined;
|
||||
let supportsSessionDatabaseSwitch: boolean | undefined;
|
||||
let useDatabaseDefaultSchema: string | undefined;
|
||||
if (useDatabase) {
|
||||
try {
|
||||
const currentContext = await connectionStore.getSqlServerCompletionContext(props.connectionId, props.database!);
|
||||
supportsSessionDatabaseSwitch = currentContext.supports_session_database_switch;
|
||||
knownUseDatabases = [props.database!];
|
||||
if (supportsSessionDatabaseSwitch) {
|
||||
knownUseDatabases = mergeSqlCompletionQualifierNames(knownUseDatabases, connectionStore.lookupLocalCompletionDatabases(props.connectionId, "", MAX_COMPLETION_TABLES));
|
||||
if (!knownUseDatabases.some((database) => database.toLowerCase() === useDatabase.toLowerCase())) {
|
||||
knownUseDatabases = mergeSqlCompletionQualifierNames(knownUseDatabases, await connectionStore.listCompletionDatabases(props.connectionId));
|
||||
}
|
||||
}
|
||||
const targetDatabase = knownUseDatabases.find((database) => database.toLowerCase() === useDatabase.toLowerCase());
|
||||
if (targetDatabase) {
|
||||
const targetContext = targetDatabase.toLowerCase() === props.database!.toLowerCase() ? currentContext : await connectionStore.getSqlServerCompletionContext(props.connectionId, targetDatabase);
|
||||
useDatabaseDefaultSchema = targetContext.default_schema;
|
||||
}
|
||||
} catch {
|
||||
// An unverified USE target must not replace the selected database.
|
||||
}
|
||||
if (epoch !== completionEpoch) return null;
|
||||
}
|
||||
|
||||
const completionScope = resolveSqlCompletionScope({
|
||||
sql: fullDoc,
|
||||
cursor: position,
|
||||
databaseType: props.databaseType,
|
||||
currentDatabase: props.database!,
|
||||
currentSchema: props.schema,
|
||||
knownDatabases: knownUseDatabases,
|
||||
supportsSessionDatabaseSwitch,
|
||||
useDatabaseDefaultSchema,
|
||||
completionContext,
|
||||
});
|
||||
completionContext = completionScope.completionContext;
|
||||
|
||||
const needsAsyncData =
|
||||
completionContext.suggestTables || completionContext.suggestRoutines || completionContext.exclusiveRoutineSuggestions || !!completionContext.qualifier || !!completionContext.insertTable || completionContext.exclusiveColumnSuggestions || completionContext.referencedTables.length > 0;
|
||||
|
||||
@@ -2718,14 +2808,14 @@ async function provideSqlCompletions(context: CompletionContext) {
|
||||
const tableNameCompletion = isTableNameCompletionContext(completionContext);
|
||||
const shouldResolveColumnCompletion = completionContext.suggestColumns && completionContext.referencedTables.length > 0 && (completionContext.prefix.length > 0 || typedActivation);
|
||||
const shouldResolveAsyncCompletion = tableNameCompletion || shouldResolveColumnCompletion;
|
||||
const localResult = buildLocalSqlCompletionResult(completionContext, fullDoc, position);
|
||||
const localResult = buildLocalSqlCompletionResult(completionContext, fullDoc, position, completionScope);
|
||||
if (localResult) {
|
||||
scheduleCompletionMetadataRefresh(completionContext, fullDoc, position);
|
||||
scheduleCompletionMetadataRefresh(completionContext, fullDoc, position, completionScope);
|
||||
const hasLocalColumnResult = localResult.options.some((option) => option.type === "column");
|
||||
if ((!explicit || typedActivation) && (!shouldResolveColumnCompletion || hasLocalColumnResult)) return localResult;
|
||||
}
|
||||
if ((!explicit || typedActivation) && !shouldResolveAsyncCompletion) {
|
||||
scheduleCompletionMetadataRefresh(completionContext, fullDoc, position);
|
||||
scheduleCompletionMetadataRefresh(completionContext, fullDoc, position, completionScope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2749,7 +2839,7 @@ async function provideSqlCompletions(context: CompletionContext) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await performAsyncCompletionWithResult(epoch, completionContext, fullDoc, position);
|
||||
const result = await performAsyncCompletionWithResult(epoch, completionContext, fullDoc, position, completionScope);
|
||||
resolve(result ?? localResult);
|
||||
} catch {
|
||||
resolve(localResult);
|
||||
@@ -2785,7 +2875,9 @@ function flushImeComposition() {
|
||||
emit("cursorChange", currentView.state.selection.main.head);
|
||||
latestSelection = readEditorSelection(currentView);
|
||||
if (editorIsActive) emitEditorSelection(latestSelection);
|
||||
if (shouldAutoOpenSqlCompletion(currentView.state.doc.toString(), currentView.state.selection.main.head, sqlCompletionDialectOptions())) {
|
||||
const fullDoc = currentView.state.doc.toString();
|
||||
const position = currentView.state.selection.main.head;
|
||||
if (resolveSqlServerUseDatabaseCompletion({ sql: fullDoc, cursor: position, databaseType: props.databaseType }) || shouldAutoOpenSqlCompletion(fullDoc, position, sqlCompletionDialectOptions())) {
|
||||
scheduleSqlCompletionStart(currentView);
|
||||
}
|
||||
}
|
||||
@@ -2793,6 +2885,7 @@ function flushImeComposition() {
|
||||
function shouldStartSqlCompletionAfterInput(insertedText: string, removedText: string, currentView: EditorViewType): boolean {
|
||||
const position = currentView.state.selection.main.head;
|
||||
const fullDoc = currentView.state.doc.toString();
|
||||
if (resolveSqlServerUseDatabaseCompletion({ sql: fullDoc, cursor: position, databaseType: props.databaseType })) return true;
|
||||
if (!insertedText && removedText) {
|
||||
const completionContext = getSqlCompletionContext(fullDoc, position, sqlCompletionDialectOptions());
|
||||
return isTableNameCompletionContext(completionContext) && shouldAutoOpenSqlCompletion(fullDoc, position, sqlCompletionDialectOptions());
|
||||
@@ -2810,10 +2903,10 @@ function shouldStartSqlCompletionAfterInput(insertedText: string, removedText: s
|
||||
return isTableNameCompletionContext(completionContext) || shouldAutoOpenSqlCompletion(fullDoc, position, sqlCompletionDialectOptions());
|
||||
}
|
||||
|
||||
function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
|
||||
function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number, scope: CompletionMetadataScope) {
|
||||
if (!props.connectionId || props.database == null) return null;
|
||||
const databaseNames = localCompletionDatabaseNames(completionContext);
|
||||
const currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames);
|
||||
const currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames, scope);
|
||||
const schemaLookupDatabase = resolveSqlCompletionSchemaLookupDatabase({
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
@@ -2822,8 +2915,8 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
});
|
||||
const shouldLoadTables = !schemaLookupDatabase && (completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext)));
|
||||
const tableLookupTarget = resolveSqlCompletionTableLookupTarget({
|
||||
currentDatabase: props.database,
|
||||
currentSchema: props.schema,
|
||||
currentDatabase: scope.database,
|
||||
currentSchema: scope.schema,
|
||||
supportsDatabaseQualifier: supportsDatabaseQualifierCompletion(),
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
@@ -2833,35 +2926,37 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
const tables = schemaLookupDatabase ? [] : shouldLoadTables ? connectionStore.lookupLocalCompletionTables(props.connectionId, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, globalOracleTableSearch ? undefined : tableLookupTarget.schema, props.catalog) : cachedTables;
|
||||
|
||||
const shouldLoadObjects = shouldLoadCompletionObjects(completionContext);
|
||||
const completionObjects = shouldLoadObjects ? lookupLocalCompletionObjectsForContext(completionContext) : cachedCompletionObjects;
|
||||
const completionObjectScope = routineCompletionScopeForContext(completionContext, scope);
|
||||
const scopedCachedCompletionObjects = completionObjectsForScope(completionObjectScope);
|
||||
const completionObjects = shouldLoadObjects ? lookupLocalCompletionObjectsForContext(completionContext, scope) : scopedCachedCompletionObjects;
|
||||
|
||||
const schemaNames =
|
||||
completionContext.suggestTables && !completionContext.insertTable
|
||||
? schemaLookupDatabase
|
||||
? connectionStore.lookupLocalCompletionSchemas(props.connectionId, schemaLookupDatabase, completionContext.prefix, MAX_COMPLETION_TABLES)
|
||||
: !completionContext.qualifier
|
||||
? mergeSqlCompletionQualifierNames(connectionStore.lookupLocalCompletionSchemas(props.connectionId, props.database, completionContext.prefix, MAX_COMPLETION_TABLES), databaseNames)
|
||||
? mergeSqlCompletionQualifierNames(connectionStore.lookupLocalCompletionSchemas(props.connectionId, scope.database, completionContext.prefix, MAX_COMPLETION_TABLES), databaseNames)
|
||||
: []
|
||||
: [];
|
||||
|
||||
const columnsByTable = new Map<string, SqlCompletionColumn[]>();
|
||||
if (completionContext.insertTable) {
|
||||
const insertDatabase = (supportsDatabaseSchemaQualifierCompletion() ? completionContext.insertDatabase : undefined) ?? props.database;
|
||||
const insertSchema = completionContext.insertSchema ?? props.schema;
|
||||
const insertDatabase = (supportsDatabaseSchemaQualifierCompletion() ? completionContext.insertDatabase : undefined) ?? scope.database;
|
||||
const insertSchema = completionContext.insertSchema ?? scope.schema;
|
||||
const insertColumns = usesOracleSessionCompletionColumns(insertSchema) ? [] : connectionStore.lookupLocalCompletionColumns(props.connectionId, insertDatabase, completionContext.insertTable, insertSchema, props.catalog);
|
||||
if (insertColumns.length > 0) {
|
||||
columnsByTable.set(completionCacheKey({ name: completionContext.insertTable, database: completionContext.insertDatabase, schema: insertSchema }), insertColumns);
|
||||
columnsByTable.set(completionCacheKey({ name: completionContext.insertTable, database: completionContext.insertDatabase, schema: insertSchema }, scope), insertColumns);
|
||||
}
|
||||
}
|
||||
|
||||
const qualifiedColumnTarget = completionQualifiedTableTarget(completionContext);
|
||||
if (qualifiedColumnTarget) {
|
||||
const cacheKey = completionCacheKey(qualifiedColumnTarget);
|
||||
const cacheKey = completionCacheKey(qualifiedColumnTarget, scope);
|
||||
const cached = cachedColumnsByTable.get(cacheKey);
|
||||
if (cached) {
|
||||
columnsByTable.set(cacheKey, cached);
|
||||
} else {
|
||||
const target = completionMetadataTarget(qualifiedColumnTarget);
|
||||
const target = completionMetadataTarget(qualifiedColumnTarget, scope);
|
||||
const localColumns = target && !usesOracleSessionCompletionColumns(target.schema) ? connectionStore.lookupLocalCompletionColumns(props.connectionId, target.database, qualifiedColumnTarget.name, target.schema, target.catalog) : [];
|
||||
if (localColumns.length > 0) {
|
||||
columnsByTable.set(cacheKey, localColumns);
|
||||
@@ -2884,13 +2979,13 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const cacheKey = completionCacheKey(refTable);
|
||||
const cacheKey = completionCacheKey(refTable, scope);
|
||||
const cached = cachedColumnsByTable.get(cacheKey);
|
||||
if (cached) {
|
||||
columnsByTable.set(cacheKey, cached);
|
||||
continue;
|
||||
}
|
||||
const target = completionMetadataTarget(refTable);
|
||||
const target = completionMetadataTarget(refTable, scope);
|
||||
const localColumns = target && !usesOracleSessionCompletionColumns(target.schema) ? connectionStore.lookupLocalCompletionColumns(props.connectionId, target.database, refTable.name, target.schema, target.catalog, refTable) : [];
|
||||
if (localColumns.length > 0) {
|
||||
columnsByTable.set(cacheKey, localColumns);
|
||||
@@ -2915,7 +3010,7 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
snippets: settingsStore.editorSettings.snippets,
|
||||
dialect: props.dialect,
|
||||
databaseType: snippetDatabaseType.value,
|
||||
currentSchema: props.schema,
|
||||
currentSchema: scope.schema,
|
||||
keywordCase: settingsStore.editorSettings.sqlFormatter.keywordCase,
|
||||
autoAliasTables: settingsStore.editorSettings.autoAliasTables,
|
||||
});
|
||||
@@ -2923,15 +3018,15 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
return buildCompletionResult(items, position - completionContext.prefix.length, getSqlCompletionResultValidFor(fullDoc, position));
|
||||
}
|
||||
|
||||
function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
|
||||
function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number, scope: CompletionMetadataScope) {
|
||||
if (!props.connectionId || props.database == null) return;
|
||||
const localOnlyMetadata = usesLocalOnlyCompletionMetadata();
|
||||
const onDemandOnlyColumns = usesOnDemandOnlyCompletionColumns();
|
||||
const tableNameCompletion = isTableNameCompletionContext(completionContext);
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const database = scope.database;
|
||||
const databaseNames = localCompletionDatabaseNames(completionContext);
|
||||
const currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames);
|
||||
const currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames, scope);
|
||||
const schemaLookupDatabase = resolveSqlCompletionSchemaLookupDatabase({
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
@@ -2940,7 +3035,7 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
});
|
||||
const tableLookupTarget = resolveSqlCompletionTableLookupTarget({
|
||||
currentDatabase: database,
|
||||
currentSchema: props.schema,
|
||||
currentSchema: scope.schema,
|
||||
supportsDatabaseQualifier: supportsDatabaseQualifierCompletion(),
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
@@ -2949,7 +3044,7 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
if (!localOnlyMetadata && !schemaLookupDatabase && (completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext)))) {
|
||||
const globalOracleTableSearch = props.databaseType === "oracle" && completionContext.suggestTables && !completionContext.qualifier;
|
||||
void connectionStore
|
||||
.refreshCompletionTables(connectionId, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, tableLookupTarget.schema, globalOracleTableSearch, props.schema, props.catalog)
|
||||
.refreshCompletionTables(connectionId, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, tableLookupTarget.schema, globalOracleTableSearch, scope.schema, props.catalog)
|
||||
.then((tables) => {
|
||||
const scopedTables = tables.map((table) => ({ ...table, database: table.database ?? tableLookupTarget.database }));
|
||||
cachedTables = mergeCompletionTables(cachedTables, scopedTables);
|
||||
@@ -2960,11 +3055,13 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
.catch(() => {});
|
||||
}
|
||||
if (!localOnlyMetadata && shouldLoadCompletionObjects(completionContext)) {
|
||||
void listCompletionObjectsForContext(completionContext)
|
||||
const completionObjectScope = routineCompletionScopeForContext(completionContext, scope);
|
||||
void listCompletionObjectsForContext(completionContext, scope)
|
||||
.then((objects) => {
|
||||
const merged = mergeCompletionObjects(cachedCompletionObjects, objects);
|
||||
const changed = completionObjectsDiffer(cachedCompletionObjects, merged);
|
||||
cachedCompletionObjects = merged;
|
||||
const cachedObjects = completionObjectsForScope(completionObjectScope);
|
||||
const merged = mergeCompletionObjects(cachedObjects, objects);
|
||||
const changed = completionObjectsDiffer(cachedObjects, merged);
|
||||
cachedCompletionObjectsByScope.set(completionObjectScopeKey(completionObjectScope), merged);
|
||||
if (changed) refreshActiveSqlCompletion(fullDoc, position, completionContext);
|
||||
})
|
||||
.catch(() => {});
|
||||
@@ -2982,17 +3079,17 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
if (!onDemandOnlyColumns && completionContext.insertTable) {
|
||||
const insertTable = completionContext.insertTable;
|
||||
const insertDatabase = (supportsDatabaseSchemaQualifierCompletion() ? completionContext.insertDatabase : undefined) ?? database;
|
||||
void refreshCompletionColumnsForEditor(connectionId, insertDatabase, insertTable, completionContext.insertSchema ?? props.schema)
|
||||
void refreshCompletionColumnsForEditor(connectionId, insertDatabase, insertTable, completionContext.insertSchema ?? scope.schema)
|
||||
.then((columns) => {
|
||||
const insertSchema = completionContext.insertSchema ?? props.schema;
|
||||
cachedColumnsByTable.set(completionCacheKey({ name: insertTable, database: completionContext.insertDatabase, schema: insertSchema }), columns);
|
||||
const insertSchema = completionContext.insertSchema ?? scope.schema;
|
||||
cachedColumnsByTable.set(completionCacheKey({ name: insertTable, database: completionContext.insertDatabase, schema: insertSchema }, scope), columns);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
const qualifiedColumnTarget = completionQualifiedTableTarget(completionContext);
|
||||
const qualifiedColumnCacheKey = qualifiedColumnTarget ? completionCacheKey(qualifiedColumnTarget) : undefined;
|
||||
const qualifiedColumnCacheKey = qualifiedColumnTarget ? completionCacheKey(qualifiedColumnTarget, scope) : undefined;
|
||||
if (!onDemandOnlyColumns && qualifiedColumnTarget && qualifiedColumnCacheKey && !cachedColumnsByTable.has(qualifiedColumnCacheKey)) {
|
||||
const target = completionMetadataTarget(qualifiedColumnTarget);
|
||||
const target = completionMetadataTarget(qualifiedColumnTarget, scope);
|
||||
if (target) {
|
||||
void refreshCompletionColumnsForEditor(connectionId, target.database, qualifiedColumnTarget.name, target.schema, target.catalog)
|
||||
.then((columns) => {
|
||||
@@ -3005,10 +3102,10 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
for (const refTable of completionContext.referencedTables) {
|
||||
if (isVirtualCompletionTableReference(refTable)) continue;
|
||||
if (refTable.columns && refTable.columns.length > 0) continue;
|
||||
const cacheKey = completionCacheKey(refTable);
|
||||
const cacheKey = completionCacheKey(refTable, scope);
|
||||
if (cacheKey === qualifiedColumnCacheKey) continue;
|
||||
if (cachedColumnsByTable.has(cacheKey)) continue;
|
||||
const target = completionMetadataTarget(refTable);
|
||||
const target = completionMetadataTarget(refTable, scope);
|
||||
if (!target) continue;
|
||||
void refreshCompletionColumnsForEditor(connectionId, target.database, refTable.name, target.schema, target.catalog, refTable)
|
||||
.then((columns) => {
|
||||
@@ -3048,9 +3145,9 @@ function withCompletionLatencyBudget<T>(remote: Promise<T>, local: T): Promise<T
|
||||
return Promise.race([remote, new Promise<T>((resolve) => setTimeout(() => resolve(local), COMPLETION_REMOTE_LATENCY_BUDGET_MS))]);
|
||||
}
|
||||
|
||||
function listCompletionTablesWithLatencyBudget(connectionId: string, database: string, filter: string, limit: number, schema?: string, globalSearch = false, catalog = props.catalog): Promise<SqlCompletionTable[]> {
|
||||
function listCompletionTablesWithLatencyBudget(connectionId: string, database: string, filter: string, limit: number, schema?: string, globalSearch = false, catalog = props.catalog, currentSchema = props.schema): Promise<SqlCompletionTable[]> {
|
||||
const local = connectionStore.lookupLocalCompletionTables(connectionId, database, filter, limit, globalSearch ? undefined : schema, catalog).map((table) => ({ ...table, catalog: table.catalog ?? catalog, database: table.database ?? database }));
|
||||
const remote = connectionStore.listCompletionTables(connectionId, database, filter, limit, schema, globalSearch, props.schema, catalog).then((tables) => {
|
||||
const remote = connectionStore.listCompletionTables(connectionId, database, filter, limit, schema, globalSearch, currentSchema, catalog).then((tables) => {
|
||||
const scopedTables = tables.map((table) => ({ ...table, catalog: table.catalog ?? catalog, database: table.database ?? database }));
|
||||
cachedTables = mergeCompletionTables(cachedTables, scopedTables);
|
||||
return scopedTables;
|
||||
@@ -3083,24 +3180,39 @@ function oracleRoutineCompletionTargets(completionContext: ReturnType<typeof get
|
||||
return [{ schema: parts[parts.length - 2], parentName: parts[parts.length - 1] }];
|
||||
}
|
||||
|
||||
function lookupLocalCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>): SqlCompletionObject[] {
|
||||
if (!props.connectionId || props.database == null) return [];
|
||||
if (props.databaseType === "oracle") {
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, props.database, completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
}
|
||||
const target = resolveSqlCompletionRoutineLookupTarget({ currentSchema: props.schema, completionContext });
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, props.database, target.mask, MAX_COMPLETION_TABLES, target.schema);
|
||||
function routineCompletionTargetForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope) {
|
||||
return resolveSqlCompletionRoutineLookupTarget({
|
||||
currentDatabase: scope.database,
|
||||
currentSchema: scope.schema,
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
});
|
||||
}
|
||||
|
||||
async function listCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>): Promise<SqlCompletionObject[]> {
|
||||
function routineCompletionScopeForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope): CompletionMetadataScope {
|
||||
if (props.databaseType === "oracle") return scope;
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
return { database: target.database, schema: target.schema };
|
||||
}
|
||||
|
||||
function lookupLocalCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope): SqlCompletionObject[] {
|
||||
if (!props.connectionId || props.database == null) return [];
|
||||
if (props.databaseType === "oracle") {
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, scope.database, completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
}
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, target.database, target.mask, MAX_COMPLETION_TABLES, target.schema);
|
||||
}
|
||||
|
||||
async function listCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope): Promise<SqlCompletionObject[]> {
|
||||
if (!props.connectionId || props.database == null) return [];
|
||||
const objectKinds = completionObjectKindsForContext(completionContext);
|
||||
if (props.databaseType !== "oracle") {
|
||||
const target = resolveSqlCompletionRoutineLookupTarget({ currentSchema: props.schema, completionContext });
|
||||
return connectionStore.listCompletionObjects(props.connectionId, props.database, target.mask, MAX_COMPLETION_TABLES, target.schema, undefined, false, props.schema, objectKinds);
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
return connectionStore.listCompletionObjects(props.connectionId, target.database, target.mask, MAX_COMPLETION_TABLES, target.schema, undefined, false, scope.schema, objectKinds);
|
||||
}
|
||||
const groups = await Promise.all(
|
||||
oracleRoutineCompletionTargets(completionContext).map((target) => connectionStore.listCompletionObjects(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, target.schema, target.parentName, target.globalSearch, props.schema, objectKinds)),
|
||||
oracleRoutineCompletionTargets(completionContext).map((target) => connectionStore.listCompletionObjects(props.connectionId!, scope.database, completionContext.prefix, MAX_COMPLETION_TABLES, target.schema, target.parentName, target.globalSearch, scope.schema, objectKinds)),
|
||||
);
|
||||
return groups.reduce((objects, group) => mergeCompletionObjects(objects, group), [] as SqlCompletionObject[]);
|
||||
}
|
||||
@@ -3111,19 +3223,19 @@ function completionObjectKindsForContext(completionContext: ReturnType<typeof ge
|
||||
return ["routine"];
|
||||
}
|
||||
|
||||
async function performAsyncCompletionWithResult(epoch: number, completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
|
||||
async function performAsyncCompletionWithResult(epoch: number, completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number, scope: CompletionMetadataScope) {
|
||||
const localOnlyMetadata = usesLocalOnlyCompletionMetadata();
|
||||
const onDemandOnlyColumns = usesOnDemandOnlyCompletionColumns();
|
||||
// Handle INSERT column list: fetch columns for the target table
|
||||
let insertColumnsByTable = new Map<string, SqlCompletionColumn[]>();
|
||||
if (completionContext.insertTable) {
|
||||
try {
|
||||
const insertDatabase = (supportsDatabaseSchemaQualifierCompletion() ? completionContext.insertDatabase : undefined) ?? props.database!;
|
||||
const insertCols = await listCompletionColumnsForEditor(props.connectionId!, insertDatabase, completionContext.insertTable, completionContext.insertSchema ?? props.schema);
|
||||
const insertDatabase = (supportsDatabaseSchemaQualifierCompletion() ? completionContext.insertDatabase : undefined) ?? scope.database;
|
||||
const insertCols = await listCompletionColumnsForEditor(props.connectionId!, insertDatabase, completionContext.insertTable, completionContext.insertSchema ?? scope.schema);
|
||||
if (epoch !== completionEpoch) return null;
|
||||
if (insertCols.length > 0) {
|
||||
const insertSchema = completionContext.insertSchema ?? props.schema;
|
||||
const insertKey = completionCacheKey({ name: completionContext.insertTable, database: completionContext.insertDatabase, schema: insertSchema });
|
||||
const insertSchema = completionContext.insertSchema ?? scope.schema;
|
||||
const insertKey = completionCacheKey({ name: completionContext.insertTable, database: completionContext.insertDatabase, schema: insertSchema }, scope);
|
||||
insertColumnsByTable.set(insertKey, insertCols);
|
||||
}
|
||||
} catch {
|
||||
@@ -3132,12 +3244,12 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
}
|
||||
|
||||
let databaseNames = localCompletionDatabaseNames(completionContext);
|
||||
let currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames);
|
||||
let currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames, scope);
|
||||
const mayCompleteDatabaseSchema = mayCompleteDatabaseSchemaQualifier(completionContext);
|
||||
if (!localOnlyMetadata && supportsDatabaseNameCompletion(props.databaseType) && completionContext.suggestTables && !completionContext.insertTable && (!completionContext.qualifier || mayCompleteDatabaseSchema)) {
|
||||
const [databasesResult, schemasResult] = await Promise.allSettled([connectionStore.listCompletionDatabases(props.connectionId!), mayCompleteDatabaseSchema ? connectionStore.listCompletionSchemas(props.connectionId!, props.database!) : Promise.resolve(currentDatabaseSchemaNames)]);
|
||||
const [databasesResult, schemasResult] = await Promise.allSettled([connectionStore.listCompletionDatabases(props.connectionId!), mayCompleteDatabaseSchema ? connectionStore.listCompletionSchemas(props.connectionId!, scope.database) : Promise.resolve(currentDatabaseSchemaNames)]);
|
||||
databaseNames = databasesResult.status === "fulfilled" ? databasesResult.value : [];
|
||||
if (schemasResult.status === "fulfilled") currentDatabaseSchemaNames = mergeSqlCompletionQualifierNames(props.schema ? [props.schema] : [], schemasResult.value);
|
||||
if (schemasResult.status === "fulfilled") currentDatabaseSchemaNames = mergeSqlCompletionQualifierNames(scope.schema ? [scope.schema] : [], schemasResult.value);
|
||||
if (epoch !== completionEpoch) return null;
|
||||
}
|
||||
const schemaLookupDatabase = resolveSqlCompletionSchemaLookupDatabase({
|
||||
@@ -3148,8 +3260,8 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
});
|
||||
const shouldLoadTables = !schemaLookupDatabase && (completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext)));
|
||||
const tableLookupTarget = resolveSqlCompletionTableLookupTarget({
|
||||
currentDatabase: props.database!,
|
||||
currentSchema: props.schema,
|
||||
currentDatabase: scope.database,
|
||||
currentSchema: scope.schema,
|
||||
supportsDatabaseQualifier: supportsDatabaseQualifierCompletion(),
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
@@ -3161,30 +3273,33 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
: shouldLoadTables
|
||||
? localOnlyMetadata
|
||||
? connectionStore.lookupLocalCompletionTables(props.connectionId!, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, globalOracleTableSearch ? undefined : tableLookupTarget.schema, props.catalog)
|
||||
: await listCompletionTablesWithLatencyBudget(props.connectionId!, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, tableLookupTarget.schema, globalOracleTableSearch)
|
||||
: await listCompletionTablesWithLatencyBudget(props.connectionId!, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, tableLookupTarget.schema, globalOracleTableSearch, props.catalog, scope.schema)
|
||||
: cachedTables;
|
||||
if (localOnlyMetadata && tables.length === 0 && supportsDatabaseSchemaQualifierCompletion() && (completionContext.qualifierParts?.length ?? 0) >= 2 && allowsOnDemandQualifiedTableCompletion(completionContext.prefix)) {
|
||||
tables = await listCompletionTablesWithLatencyBudget(props.connectionId!, tableLookupTarget.database, tableLookupTarget.filter, PRESTO_ON_DEMAND_TABLE_COMPLETION_LIMIT, tableLookupTarget.schema);
|
||||
tables = await listCompletionTablesWithLatencyBudget(props.connectionId!, tableLookupTarget.database, tableLookupTarget.filter, PRESTO_ON_DEMAND_TABLE_COMPLETION_LIMIT, tableLookupTarget.schema, false, props.catalog, scope.schema);
|
||||
}
|
||||
if (epoch !== completionEpoch) return null;
|
||||
|
||||
const shouldLoadObjects = shouldLoadCompletionObjects(completionContext);
|
||||
let completionObjects = shouldLoadObjects ? (localOnlyMetadata ? lookupLocalCompletionObjectsForContext(completionContext) : await listCompletionObjectsForContext(completionContext)) : cachedCompletionObjects;
|
||||
const completionObjectScope = routineCompletionScopeForContext(completionContext, scope);
|
||||
const scopedCachedCompletionObjects = completionObjectsForScope(completionObjectScope);
|
||||
let completionObjects = shouldLoadObjects ? (localOnlyMetadata ? lookupLocalCompletionObjectsForContext(completionContext, scope) : await listCompletionObjectsForContext(completionContext, scope)) : scopedCachedCompletionObjects;
|
||||
if (epoch !== completionEpoch) return null;
|
||||
|
||||
if (!props.catalog && props.databaseType !== "oracle" && !localOnlyMetadata && completionContext.qualifier && completionObjects.length === 0) {
|
||||
const schemaObjects = await connectionStore.listCompletionObjects(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier);
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
const schemaObjects = await connectionStore.listCompletionObjects(props.connectionId!, target.database, target.mask, MAX_COMPLETION_TABLES, target.schema, undefined, false, scope.schema);
|
||||
if (schemaObjects.length > 0) {
|
||||
completionObjects = schemaObjects;
|
||||
}
|
||||
if (epoch !== completionEpoch) return null;
|
||||
}
|
||||
cachedCompletionObjects = mergeCompletionObjects(cachedCompletionObjects, completionObjects);
|
||||
cachedCompletionObjectsByScope.set(completionObjectScopeKey(completionObjectScope), mergeCompletionObjects(scopedCachedCompletionObjects, completionObjects));
|
||||
|
||||
// Fetch schemas for schema completion
|
||||
let schemaNames: string[] = [];
|
||||
if (completionContext.suggestTables && !completionContext.insertTable && (schemaLookupDatabase || !completionContext.qualifier)) {
|
||||
const database = schemaLookupDatabase ?? props.database!;
|
||||
const database = schemaLookupDatabase ?? scope.database;
|
||||
if (localOnlyMetadata) {
|
||||
const schemas = connectionStore.lookupLocalCompletionSchemas(props.connectionId!, database, completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
schemaNames = schemaLookupDatabase ? schemas : mergeSqlCompletionQualifierNames(schemas, databaseNames);
|
||||
@@ -3202,11 +3317,11 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
// If qualifier didn't match any table names, try it as a schema name
|
||||
let qualifierIsSchema = false;
|
||||
if (completionContext.qualifier && !schemaLookupDatabase && !tableLookupTarget.qualifierDatabase && !isReferencedTableQualifier(completionContext) && tables.length === 0 && (completionContext.suggestTables || completionContext.exclusiveColumnSuggestions)) {
|
||||
let schemaTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier, props.catalog);
|
||||
let schemaTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, scope.database, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier, props.catalog);
|
||||
if (!localOnlyMetadata) {
|
||||
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier);
|
||||
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, scope.database, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier, false, props.catalog, scope.schema);
|
||||
} else if (schemaTables.length === 0 && allowsOnDemandQualifiedTableCompletion(completionContext.prefix)) {
|
||||
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, props.database!, completionContext.prefix, PRESTO_ON_DEMAND_TABLE_COMPLETION_LIMIT, completionContext.qualifier);
|
||||
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, scope.database, completionContext.prefix, PRESTO_ON_DEMAND_TABLE_COMPLETION_LIMIT, completionContext.qualifier, false, props.catalog, scope.schema);
|
||||
}
|
||||
if (schemaTables.length > 0) {
|
||||
tables = schemaTables;
|
||||
@@ -3228,7 +3343,12 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
});
|
||||
const unresolvedRefs = refs.filter((rt) => !usesOracleSessionCompletionColumns(rt.schema) && !rt.schema && !rt.columns && !isVirtualCompletionTableReference(rt));
|
||||
if (!localOnlyMetadata && unresolvedRefs.length > 0) {
|
||||
const lookupGroups = await Promise.all(unresolvedRefs.map((rt) => connectionStore.listCompletionTables(props.connectionId!, props.database!, rt.name, 20, props.schema, false, props.schema, props.catalog)));
|
||||
const lookupGroups = await Promise.all(
|
||||
unresolvedRefs.map((rt) => {
|
||||
const target = completionMetadataTarget(rt, scope);
|
||||
return connectionStore.listCompletionTables(props.connectionId!, target?.database ?? scope.database, rt.name, 20, target?.schema ?? scope.schema, false, scope.schema, target?.catalog ?? props.catalog);
|
||||
}),
|
||||
);
|
||||
if (epoch !== completionEpoch) return null;
|
||||
const lookupTables = lookupGroups.flat();
|
||||
refs = refs.map((rt) => {
|
||||
@@ -3268,10 +3388,10 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
refs.map(async (refTable) => {
|
||||
if (isVirtualCompletionTableReference(refTable)) return;
|
||||
if (refTable.columns && refTable.columns.length > 0) return;
|
||||
const cacheKey = completionCacheKey(refTable);
|
||||
const cacheKey = completionCacheKey(refTable, scope);
|
||||
if (cachedColumnsByTable.has(cacheKey)) return;
|
||||
try {
|
||||
const target = completionMetadataTarget(refTable);
|
||||
const target = completionMetadataTarget(refTable, scope);
|
||||
if (!target) return;
|
||||
const columns = await listCompletionColumnsForEditor(props.connectionId!, target.database, refTable.name, target.schema, target.catalog, refTable);
|
||||
if (epoch !== completionEpoch) return;
|
||||
@@ -3312,14 +3432,14 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const cacheKey = completionCacheKey(refTable);
|
||||
const cacheKey = completionCacheKey(refTable, scope);
|
||||
const cached = cachedColumnsByTable.get(cacheKey);
|
||||
if (cached) {
|
||||
columnsByTable.set(cacheKey, cached);
|
||||
}
|
||||
let cachedForeignKeys = cachedForeignKeysByTable.get(cacheKey);
|
||||
if (!cachedForeignKeys) {
|
||||
const target = completionMetadataTarget(refTable);
|
||||
const target = completionMetadataTarget(refTable, scope);
|
||||
cachedForeignKeys = target ? connectionStore.lookupLocalCompletionForeignKeys(props.connectionId!, target.database, refTable.name, target.schema) : [];
|
||||
if (cachedForeignKeys.length > 0) cachedForeignKeysByTable.set(cacheKey, cachedForeignKeys);
|
||||
}
|
||||
@@ -3349,7 +3469,7 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
snippets: settingsStore.editorSettings.snippets,
|
||||
dialect: props.dialect,
|
||||
databaseType: snippetDatabaseType.value,
|
||||
currentSchema: props.schema,
|
||||
currentSchema: scope.schema,
|
||||
keywordCase: settingsStore.editorSettings.sqlFormatter.keywordCase,
|
||||
autoAliasTables: settingsStore.editorSettings.autoAliasTables,
|
||||
});
|
||||
@@ -3384,6 +3504,14 @@ function mergeCompletionObjects(existing: SqlCompletionObject[], incoming: SqlCo
|
||||
return merged;
|
||||
}
|
||||
|
||||
function completionObjectScopeKey(scope: CompletionMetadataScope): string {
|
||||
return `${scope.database}:${scope.schema ?? ""}`.toLowerCase();
|
||||
}
|
||||
|
||||
function completionObjectsForScope(scope: CompletionMetadataScope): SqlCompletionObject[] {
|
||||
return cachedCompletionObjectsByScope.get(completionObjectScopeKey(scope)) ?? [];
|
||||
}
|
||||
|
||||
function completionObjectIdentityKey(object: SqlCompletionObject): string {
|
||||
return `${object.type}:${object.schema ?? ""}:${object.name}:${object.parentName ?? ""}:${object.signature?.trim() ?? ""}`.toLowerCase();
|
||||
}
|
||||
@@ -3419,7 +3547,7 @@ function refreshActiveSqlCompletion(fullDoc: string, position: number, completio
|
||||
|
||||
function refreshCompletionCache() {
|
||||
cachedTables = [];
|
||||
cachedCompletionObjects = [];
|
||||
cachedCompletionObjectsByScope.clear();
|
||||
cachedColumnsByTable.clear();
|
||||
cachedInsertValueHintColumnsByTable.clear();
|
||||
loadedColumnsByTable.clear();
|
||||
@@ -4217,7 +4345,7 @@ onMounted(async () => {
|
||||
registerTableReferenceDropListener();
|
||||
|
||||
cachedTables = [];
|
||||
cachedCompletionObjects = [];
|
||||
cachedCompletionObjectsByScope.clear();
|
||||
scheduleSemanticDiagnostics();
|
||||
|
||||
if (props.autoFocus) {
|
||||
|
||||
Reference in new issue
Block a user