fix(sqlserver): suggest database names in query completion
This commit is contained in:
1 parent
3285cb21a4
commit
79873443e4
9 files changed
+284
-45
No files matched your search
@@ -44,7 +44,7 @@ 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 { resolveSqlCompletionRoutineLookupTarget, resolveSqlCompletionTableLookupTarget } from "@/lib/sql/sqlCompletionLookupTarget";
|
||||
import { mergeSqlCompletionQualifierNames, resolveSqlCompletionRoutineLookupTarget, resolveSqlCompletionSchemaLookupDatabase, resolveSqlCompletionTableLookupTarget } from "@/lib/sql/sqlCompletionLookupTarget";
|
||||
import { usesOracleSessionCompletionColumns as shouldUseOracleSessionCompletionColumns } from "@/lib/sql/oracleCompletionSession";
|
||||
import { extractIdentifierDetailsAt, isSqlKeyword, matchTable, mergeSqlObjectNavigationType, splitQualifiedIdentifier, sqlObjectHoverDetail, sqlObjectNavigationTarget, type SqlObjectNavigationTarget } from "@/lib/sql/sqlNavigation";
|
||||
import { buildHoverTableSql, hoverTableMatchesScope, quoteQualifiedName, reformatHoverDdl, scopeHoverTables, type HoverTableScope } from "@/lib/editor/hoverTableSql";
|
||||
@@ -77,7 +77,7 @@ import { sqlSemanticTableNameSpansForSyntaxTree } from "@/lib/editor/codemirrorS
|
||||
import { startsQueryEditorRectangularSelection } from "@/lib/editor/queryEditorPointerSelection";
|
||||
import { LARGE_PASTE_HISTORY_USER_EVENT, normalizeQueryEditorPasteText, recoverableNativePasteSuffix, shouldRecoverLargeTauriPaste } from "@/lib/editor/queryEditorLargePaste";
|
||||
import type { StatementExecutionMarker } from "@/lib/tabs/tabPresentation";
|
||||
import { isSchemaAware, isSingleDatabase, supportsDatabaseSchemaQualifier, supportsSqlInListPaste } from "@/lib/database/databaseFeatureSupport";
|
||||
import { isSchemaAware, isSingleDatabase, supportsDatabaseNameCompletion, supportsDatabaseSchemaQualifier, supportsSqlInListPaste } from "@/lib/database/databaseFeatureSupport";
|
||||
import { metadataSchemaForConnection, sqlSnippetDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { usesLocalOnlyEditorCompletionMetadata, usesOnDemandOnlyEditorColumnMetadata } from "@/lib/metadata/completionMetadataPolicy";
|
||||
import { loadTableMetadata, type TableMetadataLoadResult } from "@/lib/metadata/tableMetadataCache";
|
||||
@@ -2496,21 +2496,25 @@ function buildCompletionResult(items: QueryCompletionItem[], from: number, valid
|
||||
};
|
||||
}
|
||||
|
||||
function mergeCompletionQualifierNames(primary: string[], secondary: string[]): string[] {
|
||||
const seen = new Set<string>();
|
||||
const merged: string[] = [];
|
||||
for (const name of [...primary, ...secondary]) {
|
||||
const key = name.toLowerCase();
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
merged.push(name);
|
||||
}
|
||||
return merged;
|
||||
function localCompletionDatabaseNames(completionContext: ReturnType<typeof getSqlCompletionContext>): string[] {
|
||||
if (!supportsDatabaseNameCompletion(props.databaseType) || !completionContext.suggestTables || completionContext.insertTable || !props.connectionId) return [];
|
||||
return connectionStore.lookupLocalCompletionDatabases(props.connectionId, completionContext.qualifier || completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
}
|
||||
|
||||
function localCompletionDatabaseNames(completionContext: ReturnType<typeof getSqlCompletionContext>): string[] {
|
||||
if (!supportsDatabaseQualifierCompletion() || !completionContext.suggestTables || completionContext.insertTable || !props.connectionId) return [];
|
||||
return connectionStore.lookupLocalCompletionDatabases(props.connectionId, completionContext.qualifier || completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
function mayCompleteDatabaseSchemaQualifier(completionContext: ReturnType<typeof getSqlCompletionContext>): boolean {
|
||||
if (!supportsDatabaseNameCompletion(props.databaseType) || !supportsDatabaseSchemaQualifierCompletion() || !completionContext.suggestTables || completionContext.insertTable) return false;
|
||||
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 [];
|
||||
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));
|
||||
}
|
||||
|
||||
function shouldInsertSqlCompletionSpace(): boolean {
|
||||
@@ -2836,7 +2840,14 @@ function shouldStartSqlCompletionAfterInput(insertedText: string, removedText: s
|
||||
function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
|
||||
if (!props.connectionId || props.database == null) return null;
|
||||
const databaseNames = localCompletionDatabaseNames(completionContext);
|
||||
const shouldLoadTables = completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext));
|
||||
const currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames);
|
||||
const schemaLookupDatabase = resolveSqlCompletionSchemaLookupDatabase({
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
knownDatabases: databaseNames,
|
||||
knownSchemas: currentDatabaseSchemaNames,
|
||||
});
|
||||
const shouldLoadTables = !schemaLookupDatabase && (completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext)));
|
||||
const tableLookupTarget = resolveSqlCompletionTableLookupTarget({
|
||||
currentDatabase: props.database,
|
||||
currentSchema: props.schema,
|
||||
@@ -2846,13 +2857,19 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
knownDatabases: databaseNames,
|
||||
});
|
||||
const globalOracleTableSearch = props.databaseType === "oracle" && completionContext.suggestTables && !completionContext.qualifier;
|
||||
const tables = shouldLoadTables ? connectionStore.lookupLocalCompletionTables(props.connectionId, tableLookupTarget.database, tableLookupTarget.filter, MAX_COMPLETION_TABLES, globalOracleTableSearch ? undefined : tableLookupTarget.schema, props.catalog) : cachedTables;
|
||||
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 schemaNames =
|
||||
completionContext.suggestTables && !completionContext.qualifier && !completionContext.insertTable ? mergeCompletionQualifierNames(connectionStore.lookupLocalCompletionSchemas(props.connectionId, props.database, completionContext.prefix, MAX_COMPLETION_TABLES), databaseNames) : [];
|
||||
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)
|
||||
: []
|
||||
: [];
|
||||
|
||||
const columnsByTable = new Map<string, SqlCompletionColumn[]>();
|
||||
if (completionContext.insertTable) {
|
||||
@@ -2940,15 +2957,23 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
const tableNameCompletion = isTableNameCompletionContext(completionContext);
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const databaseNames = localCompletionDatabaseNames(completionContext);
|
||||
const currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames);
|
||||
const schemaLookupDatabase = resolveSqlCompletionSchemaLookupDatabase({
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
knownDatabases: databaseNames,
|
||||
knownSchemas: currentDatabaseSchemaNames,
|
||||
});
|
||||
const tableLookupTarget = resolveSqlCompletionTableLookupTarget({
|
||||
currentDatabase: database,
|
||||
currentSchema: props.schema,
|
||||
supportsDatabaseQualifier: supportsDatabaseQualifierCompletion(),
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
knownDatabases: localCompletionDatabaseNames(completionContext),
|
||||
knownDatabases: databaseNames,
|
||||
});
|
||||
if (!localOnlyMetadata && (completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext)))) {
|
||||
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)
|
||||
@@ -2971,10 +2996,14 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
if (!localOnlyMetadata && completionContext.suggestTables && !completionContext.qualifier && !completionContext.insertTable) {
|
||||
void connectionStore.refreshCompletionSchemas(connectionId, database).catch(() => {});
|
||||
if (supportsDatabaseQualifierCompletion()) {
|
||||
void connectionStore.refreshCompletionDatabases(connectionId).catch(() => {});
|
||||
if (!localOnlyMetadata && completionContext.suggestTables && !completionContext.insertTable) {
|
||||
if (schemaLookupDatabase) {
|
||||
void connectionStore.refreshCompletionSchemas(connectionId, schemaLookupDatabase).catch(() => {});
|
||||
} else if (!completionContext.qualifier) {
|
||||
void connectionStore.refreshCompletionSchemas(connectionId, database).catch(() => {});
|
||||
if (supportsDatabaseNameCompletion(props.databaseType)) {
|
||||
void connectionStore.refreshCompletionDatabases(connectionId).catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!onDemandOnlyColumns && completionContext.insertTable) {
|
||||
@@ -3129,16 +3158,22 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
}
|
||||
}
|
||||
|
||||
const shouldLoadTables = completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext));
|
||||
let databaseNames = localCompletionDatabaseNames(completionContext);
|
||||
if (!localOnlyMetadata && supportsDatabaseQualifierCompletion() && completionContext.suggestTables && !completionContext.insertTable && !completionContext.qualifier) {
|
||||
try {
|
||||
databaseNames = await connectionStore.listCompletionDatabases(props.connectionId!);
|
||||
if (epoch !== completionEpoch) return null;
|
||||
} catch {
|
||||
databaseNames = [];
|
||||
}
|
||||
let currentDatabaseSchemaNames = localCompletionSchemasForDatabaseDisambiguation(completionContext, databaseNames);
|
||||
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)]);
|
||||
databaseNames = databasesResult.status === "fulfilled" ? databasesResult.value : [];
|
||||
if (schemasResult.status === "fulfilled") currentDatabaseSchemaNames = mergeSqlCompletionQualifierNames(props.schema ? [props.schema] : [], schemasResult.value);
|
||||
if (epoch !== completionEpoch) return null;
|
||||
}
|
||||
const schemaLookupDatabase = resolveSqlCompletionSchemaLookupDatabase({
|
||||
supportsDatabaseSchemaQualifier: supportsDatabaseSchemaQualifierCompletion(),
|
||||
completionContext,
|
||||
knownDatabases: databaseNames,
|
||||
knownSchemas: currentDatabaseSchemaNames,
|
||||
});
|
||||
const shouldLoadTables = !schemaLookupDatabase && (completionContext.suggestTables || (!!completionContext.qualifier && !isReferencedTableQualifier(completionContext)));
|
||||
const tableLookupTarget = resolveSqlCompletionTableLookupTarget({
|
||||
currentDatabase: props.database!,
|
||||
currentSchema: props.schema,
|
||||
@@ -3148,11 +3183,13 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
knownDatabases: databaseNames,
|
||||
});
|
||||
const globalOracleTableSearch = props.databaseType === "oracle" && completionContext.suggestTables && !completionContext.qualifier;
|
||||
let tables = 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)
|
||||
: cachedTables;
|
||||
let tables = schemaLookupDatabase
|
||||
? []
|
||||
: 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)
|
||||
: 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);
|
||||
}
|
||||
@@ -3173,23 +3210,25 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
|
||||
|
||||
// Fetch schemas for schema completion
|
||||
let schemaNames: string[] = [];
|
||||
if (completionContext.suggestTables && !completionContext.qualifier && !completionContext.insertTable) {
|
||||
if (completionContext.suggestTables && !completionContext.insertTable && (schemaLookupDatabase || !completionContext.qualifier)) {
|
||||
const database = schemaLookupDatabase ?? props.database!;
|
||||
if (localOnlyMetadata) {
|
||||
schemaNames = mergeCompletionQualifierNames(connectionStore.lookupLocalCompletionSchemas(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES), databaseNames);
|
||||
const schemas = connectionStore.lookupLocalCompletionSchemas(props.connectionId!, database, completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
schemaNames = schemaLookupDatabase ? schemas : mergeSqlCompletionQualifierNames(schemas, databaseNames);
|
||||
} else {
|
||||
try {
|
||||
const schemas = await connectionStore.listCompletionSchemas(props.connectionId!, props.database!);
|
||||
schemaNames = mergeCompletionQualifierNames(schemas, databaseNames);
|
||||
const schemas = await connectionStore.listCompletionSchemas(props.connectionId!, database);
|
||||
schemaNames = schemaLookupDatabase ? schemas : mergeSqlCompletionQualifierNames(schemas, databaseNames);
|
||||
if (epoch !== completionEpoch) return null;
|
||||
} catch {
|
||||
schemaNames = databaseNames;
|
||||
schemaNames = schemaLookupDatabase ? [] : databaseNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If qualifier didn't match any table names, try it as a schema name
|
||||
let qualifierIsSchema = false;
|
||||
if (completionContext.qualifier && !tableLookupTarget.qualifierDatabase && !isReferencedTableQualifier(completionContext) && tables.length === 0 && (completionContext.suggestTables || completionContext.exclusiveColumnSuggestions)) {
|
||||
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);
|
||||
if (!localOnlyMetadata) {
|
||||
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier);
|
||||
|
||||
Reference in new issue
Block a user