mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
@@ -108,6 +108,7 @@ import {
|
||||
type ClickTableNavigationTarget,
|
||||
type EditorSettings,
|
||||
type SqlCompletionTriggerMode,
|
||||
type SqlTableCompletionSchemaQualification,
|
||||
type TableHoverLookupMode,
|
||||
SIDEBAR_INDENT_MIN,
|
||||
SIDEBAR_INDENT_MAX,
|
||||
@@ -623,6 +624,15 @@ const editShowLineNumbers = ref(settingsStore.editorSettings.showLineNumbers);
|
||||
const editShowCurrentStatementFrame = ref(settingsStore.editorSettings.showCurrentStatementFrame);
|
||||
const editShowInsertValueHints = ref(settingsStore.editorSettings.showInsertValueHints);
|
||||
const editAutoAliasTables = ref(settingsStore.editorSettings.autoAliasTables);
|
||||
const editTableCompletionSchemaQualification = ref<SqlTableCompletionSchemaQualification>(settingsStore.editorSettings.tableCompletionSchemaQualification);
|
||||
const tableCompletionSchemaQualificationDescription = computed(() => {
|
||||
const key = {
|
||||
never: "settings.tableCompletionSchemaQualificationNeverDescription",
|
||||
collision: "settings.tableCompletionSchemaQualificationCollisionDescription",
|
||||
always: "settings.tableCompletionSchemaQualificationAlwaysDescription",
|
||||
}[editTableCompletionSchemaQualification.value];
|
||||
return t(key);
|
||||
});
|
||||
const editInsertSpaceAfterCompletion = ref(settingsStore.editorSettings.insertSpaceAfterCompletion);
|
||||
const editSqlServerSpaceConfirmsCompletion = ref(settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion);
|
||||
const showSqlServerSpaceConfirmsCompletion = computed(() => hasSqlServerConnection.value || settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion || editSqlServerSpaceConfirmsCompletion.value);
|
||||
@@ -991,6 +1001,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
|
||||
showCurrentStatementFrame: editShowCurrentStatementFrame.value,
|
||||
showInsertValueHints: editShowInsertValueHints.value,
|
||||
autoAliasTables: editAutoAliasTables.value,
|
||||
tableCompletionSchemaQualification: editTableCompletionSchemaQualification.value,
|
||||
insertSpaceAfterCompletion: editInsertSpaceAfterCompletion.value,
|
||||
sqlServerSpaceConfirmsCompletion: editSqlServerSpaceConfirmsCompletion.value,
|
||||
sortCompletionColumnsAlphabetically: editSortCompletionColumnsAlphabetically.value,
|
||||
@@ -1637,6 +1648,7 @@ function syncEditorSettingsDraftFromStore() {
|
||||
editShowCurrentStatementFrame.value = settingsStore.editorSettings.showCurrentStatementFrame;
|
||||
editShowInsertValueHints.value = settingsStore.editorSettings.showInsertValueHints;
|
||||
editAutoAliasTables.value = settingsStore.editorSettings.autoAliasTables;
|
||||
editTableCompletionSchemaQualification.value = settingsStore.editorSettings.tableCompletionSchemaQualification;
|
||||
editInsertSpaceAfterCompletion.value = settingsStore.editorSettings.insertSpaceAfterCompletion;
|
||||
editSqlServerSpaceConfirmsCompletion.value = settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion;
|
||||
editSortCompletionColumnsAlphabetically.value = settingsStore.editorSettings.sortCompletionColumnsAlphabetically;
|
||||
@@ -1775,6 +1787,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
|
||||
showCurrentStatementFrame: editShowCurrentStatementFrame,
|
||||
showInsertValueHints: editShowInsertValueHints,
|
||||
autoAliasTables: editAutoAliasTables,
|
||||
tableCompletionSchemaQualification: editTableCompletionSchemaQualification,
|
||||
insertSpaceAfterCompletion: editInsertSpaceAfterCompletion,
|
||||
sqlServerSpaceConfirmsCompletion: editSqlServerSpaceConfirmsCompletion,
|
||||
sortCompletionColumnsAlphabetically: editSortCompletionColumnsAlphabetically,
|
||||
@@ -2263,6 +2276,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
|
||||
editShowCurrentStatementFrame.value = DEFAULT_EDITOR_SETTINGS.showCurrentStatementFrame;
|
||||
editShowInsertValueHints.value = DEFAULT_EDITOR_SETTINGS.showInsertValueHints;
|
||||
editAutoAliasTables.value = DEFAULT_EDITOR_SETTINGS.autoAliasTables;
|
||||
editTableCompletionSchemaQualification.value = DEFAULT_EDITOR_SETTINGS.tableCompletionSchemaQualification;
|
||||
editInsertSpaceAfterCompletion.value = DEFAULT_EDITOR_SETTINGS.insertSpaceAfterCompletion;
|
||||
editSqlServerSpaceConfirmsCompletion.value = DEFAULT_EDITOR_SETTINGS.sqlServerSpaceConfirmsCompletion;
|
||||
editSortCompletionColumnsAlphabetically.value = DEFAULT_EDITOR_SETTINGS.sortCompletionColumnsAlphabetically;
|
||||
@@ -2421,6 +2435,7 @@ function resetAllDefaults() {
|
||||
editShowCurrentStatementFrame.value = DEFAULT_EDITOR_SETTINGS.showCurrentStatementFrame;
|
||||
editShowInsertValueHints.value = DEFAULT_EDITOR_SETTINGS.showInsertValueHints;
|
||||
editAutoAliasTables.value = DEFAULT_EDITOR_SETTINGS.autoAliasTables;
|
||||
editTableCompletionSchemaQualification.value = DEFAULT_EDITOR_SETTINGS.tableCompletionSchemaQualification;
|
||||
editInsertSpaceAfterCompletion.value = DEFAULT_EDITOR_SETTINGS.insertSpaceAfterCompletion;
|
||||
editSqlServerSpaceConfirmsCompletion.value = DEFAULT_EDITOR_SETTINGS.sqlServerSpaceConfirmsCompletion;
|
||||
editSortCompletionColumnsAlphabetically.value = DEFAULT_EDITOR_SETTINGS.sortCompletionColumnsAlphabetically;
|
||||
@@ -2669,6 +2684,12 @@ function onCompletionTriggerModeChange(v: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function onTableCompletionSchemaQualificationChange(v: any) {
|
||||
if (v === "never" || v === "collision" || v === "always") {
|
||||
editTableCompletionSchemaQualification.value = v;
|
||||
}
|
||||
}
|
||||
|
||||
function onTableHoverLookupModeChange(v: any) {
|
||||
if (v === "current" || v === "fallback" || v === "always") {
|
||||
editTableHoverLookupMode.value = v;
|
||||
@@ -6449,6 +6470,25 @@ onUnmounted(() => {
|
||||
</div>
|
||||
<Switch id="editor-auto-alias-tables" v-model="editAutoAliasTables" class="mt-0.5" />
|
||||
</div>
|
||||
|
||||
<div class="settings-item flex items-start justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2" data-editor-table-completion-schema-qualification>
|
||||
<div class="min-w-0 space-y-1">
|
||||
<Label>{{ t("settings.tableCompletionSchemaQualification") }}</Label>
|
||||
<p class="text-xs leading-tight text-muted-foreground">
|
||||
{{ tableCompletionSchemaQualificationDescription }}
|
||||
</p>
|
||||
</div>
|
||||
<Select :model-value="editTableCompletionSchemaQualification" @update:model-value="onTableCompletionSchemaQualificationChange">
|
||||
<SelectTrigger class="h-8 w-44 shrink-0">
|
||||
<SelectValue :placeholder="t('settings.tableCompletionSchemaQualification')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="never">{{ t("settings.tableCompletionSchemaQualificationNever") }}</SelectItem>
|
||||
<SelectItem value="collision">{{ t("settings.tableCompletionSchemaQualificationCollision") }}</SelectItem>
|
||||
<SelectItem value="always">{{ t("settings.tableCompletionSchemaQualificationAlways") }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ function createHarness(overrides: Partial<QueryEditorProps> = {}) {
|
||||
listElasticsearchCompletionFields: vi.fn(async () => []),
|
||||
};
|
||||
const connectionStore = store as unknown as Options["connectionStore"];
|
||||
const settings = reactive({ editorSettings: { completionTriggerMode: "positional", snippets: [], sqlFormatter: { keywordCase: "upper", functionCase: "upper" }, autoAliasTables: false, generateSqlQuoteIdentifiers: false } });
|
||||
const settings = reactive({ editorSettings: { completionTriggerMode: "positional", snippets: [], sqlFormatter: { keywordCase: "upper", functionCase: "upper" }, autoAliasTables: false, tableCompletionSchemaQualification: "collision", generateSqlQuoteIdentifiers: false } });
|
||||
const metadata = useQueryEditorCompletionMetadata({ props, view, connectionStore, sqlBehaviorDialect: () => props.dialect, remoteLatencyBudgetMs: 40, maxCompletionTables: 100, onDemandMinPrefix: 2, semanticCompletionEnabled: false });
|
||||
const startCompletion = vi.fn(() => true);
|
||||
const runtime: Options["runtime"] = { codeMirrorStartCompletion: startCompletion, codeMirrorInsertCompletionText: insertCompletionText, codeMirrorSnippetCompletion: snippetCompletion, codeMirrorCompletionStatus: () => null, imeCompositionActive: false };
|
||||
|
||||
@@ -811,6 +811,7 @@ export function useQueryEditorCompletion(options: QueryEditorCompletionOptions)
|
||||
keywordCase: settingsStore.editorSettings.sqlFormatter.keywordCase,
|
||||
functionCase: settingsStore.editorSettings.sqlFormatter.functionCase,
|
||||
autoAliasTables: settingsStore.editorSettings.autoAliasTables,
|
||||
tableCompletionSchemaQualification: settingsStore.editorSettings.tableCompletionSchemaQualification,
|
||||
quoteIdentifiers: settingsStore.editorSettings.generateSqlQuoteIdentifiers,
|
||||
});
|
||||
return buildSqlCompletionResult(items, completionContext, fullDoc, position);
|
||||
@@ -873,6 +874,7 @@ export function useQueryEditorCompletion(options: QueryEditorCompletionOptions)
|
||||
keywordCase: settingsStore.editorSettings.sqlFormatter.keywordCase,
|
||||
functionCase: settingsStore.editorSettings.sqlFormatter.functionCase,
|
||||
autoAliasTables: settingsStore.editorSettings.autoAliasTables,
|
||||
tableCompletionSchemaQualification: settingsStore.editorSettings.tableCompletionSchemaQualification,
|
||||
quoteIdentifiers: settingsStore.editorSettings.generateSqlQuoteIdentifiers,
|
||||
});
|
||||
return buildSqlCompletionResult(items, completionContext, fullDoc, position);
|
||||
@@ -1194,6 +1196,7 @@ export function useQueryEditorCompletion(options: QueryEditorCompletionOptions)
|
||||
keywordCase: settingsStore.editorSettings.sqlFormatter.keywordCase,
|
||||
functionCase: settingsStore.editorSettings.sqlFormatter.functionCase,
|
||||
autoAliasTables: settingsStore.editorSettings.autoAliasTables,
|
||||
tableCompletionSchemaQualification: settingsStore.editorSettings.tableCompletionSchemaQualification,
|
||||
quoteIdentifiers: settingsStore.editorSettings.generateSqlQuoteIdentifiers,
|
||||
});
|
||||
|
||||
@@ -1655,6 +1658,7 @@ export function useQueryEditorCompletion(options: QueryEditorCompletionOptions)
|
||||
keywordCase: settingsStore.editorSettings.sqlFormatter.keywordCase,
|
||||
functionCase: settingsStore.editorSettings.sqlFormatter.functionCase,
|
||||
autoAliasTables: settingsStore.editorSettings.autoAliasTables,
|
||||
tableCompletionSchemaQualification: settingsStore.editorSettings.tableCompletionSchemaQualification,
|
||||
quoteIdentifiers: settingsStore.editorSettings.generateSqlQuoteIdentifiers,
|
||||
});
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ const FEATURE_MESSAGES: FeatureMessages[] = [
|
||||
{ feature: "sidebar search skip hint", keys: ["sidebar.searchConnectionSkipped"] },
|
||||
{ feature: "PostgreSQL legacy TLS", keys: ["connection.postgresLegacyTls", "connection.postgresLegacyTlsHint"] },
|
||||
{ feature: "settings search sections", keys: ["settings.syncWebDavWebDescription", "settings.performanceSection"] },
|
||||
{ feature: "SQL table completion schema qualification (#9219)", keys: ["settings.tableCompletionSchemaQualification*"], translated: true },
|
||||
{ feature: "Redis batch expiration", keys: under("redis", ["batchExpiry", "batchExpiryTitle", "batchExpirySelected", "batchExpiryApply", "batchExpirySuccess", "batchExpiryPartial"]) },
|
||||
{
|
||||
feature: "plugin AI tools and data access",
|
||||
|
||||
@@ -7740,6 +7740,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "SQL Server üslublu yer tutucuları əvəz etmək üçün dəyər soruş (sessiya dəyişənləri ilə toqquşa bilər).",
|
||||
sqlVariableSyntax_atSet: "Sətirdaxili dəyişənlərin açılması",
|
||||
sqlVariableSyntax_atSetDescription: "İşlətməzdən əvvəl sətirdaxili dəyişən elanlarını aç və dəyərlərini yerinə qoy.",
|
||||
tableCompletionSchemaQualification: "Cədvəl tamamlamasında sxem ixtisaslaşdırması",
|
||||
tableCompletionSchemaQualificationDescription: "Tamamlanan cədvəl və görünüş adlarına sxemin nə vaxt əlavə ediləcəyini seçin.",
|
||||
tableCompletionSchemaQualificationNever: "Heç vaxt",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Yalnız cədvəl və ya görünüş adını daxil edir.",
|
||||
tableCompletionSchemaQualificationCollision: "Ad toqquşduqda",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Cari avtomatik davranışdan istifadə edir və sxemi yalnız lazım olduqda əlavə edir.",
|
||||
tableCompletionSchemaQualificationAlways: "Həmişə",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Sxem metadata-da olduqda həmişə əlavə edir.",
|
||||
autoAliasTables: "Cədvəl ləqəblərini avtomatik əlavə et",
|
||||
autoAliasTablesDescription: "FROM və ya JOIN daxilində cədvəl tamamlaması seçilərkən order_items AS oi kimi yaradılmış ləqəb daxil et.",
|
||||
sqlServerSpaceConfirmsCompletion: "Boşluqla tamamlamanı təsdiqlə (SQL Server üslubu)",
|
||||
|
||||
@@ -8414,6 +8414,14 @@ export default {
|
||||
sqlVariableSyntax_sqlserverDescription: "Prompt for a value to substitute SQL Server-style placeholders (may collide with session variables).",
|
||||
sqlVariableSyntax_atSet: "Inline variable expansion",
|
||||
sqlVariableSyntax_atSetDescription: "Expand inline variable declarations and substitute their values before running.",
|
||||
tableCompletionSchemaQualification: "Schema qualification for table completion",
|
||||
tableCompletionSchemaQualificationDescription: "Choose when completed table and view names include their schema.",
|
||||
tableCompletionSchemaQualificationNever: "Never",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Insert only the table or view name.",
|
||||
tableCompletionSchemaQualificationCollision: "On collision",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Use the current automatic behavior and include a schema only when needed.",
|
||||
tableCompletionSchemaQualificationAlways: "Always",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Include the schema whenever it is available in metadata.",
|
||||
autoAliasTables: "Automatically add table aliases",
|
||||
autoAliasTablesDescription: "When selecting a table completion in FROM or JOIN, insert a generated alias such as order_items AS oi.",
|
||||
sqlServerSpaceConfirmsCompletion: "Space confirms completion (SQL Server style)",
|
||||
|
||||
@@ -7749,6 +7749,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "Solicita un valor para sustituir los marcadores de estilo SQL Server (pueden entrar en conflicto con variables de sesión).",
|
||||
sqlVariableSyntax_atSet: "Expansión de variables en línea",
|
||||
sqlVariableSyntax_atSetDescription: "Expande las declaraciones de variables en línea y sustituye sus valores antes de ejecutar.",
|
||||
tableCompletionSchemaQualification: "Calificación de esquema al completar tablas",
|
||||
tableCompletionSchemaQualificationDescription: "Elige cuándo los nombres de tablas y vistas completados incluyen su esquema.",
|
||||
tableCompletionSchemaQualificationNever: "Nunca",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Inserta solo el nombre de la tabla o vista.",
|
||||
tableCompletionSchemaQualificationCollision: "Si hay conflicto",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Usa el comportamiento automático actual e incluye el esquema solo cuando sea necesario.",
|
||||
tableCompletionSchemaQualificationAlways: "Siempre",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Incluye el esquema siempre que esté disponible en los metadatos.",
|
||||
autoAliasTables: "Agregar alias de tabla automáticamente",
|
||||
autoAliasTablesDescription: "Al seleccionar una tabla en FROM o JOIN, inserta un alias generado como order_items AS oi.",
|
||||
sqlServerSpaceConfirmsCompletion: "Confirmar la sugerencia con espacio (estilo SQL Server)",
|
||||
|
||||
@@ -7674,6 +7674,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "Richiede un valore per sostituire i segnaposto in stile SQL Server (possono entrare in conflitto con le variabili di sessione).",
|
||||
sqlVariableSyntax_atSet: "Espansione di variabili inline",
|
||||
sqlVariableSyntax_atSetDescription: "Espande le dichiarazioni di variabili inline e ne sostituisce i valori prima dell'esecuzione.",
|
||||
tableCompletionSchemaQualification: "Qualificazione dello schema nel completamento tabelle",
|
||||
tableCompletionSchemaQualificationDescription: "Scegli quando i nomi completati di tabelle e viste includono lo schema.",
|
||||
tableCompletionSchemaQualificationNever: "Mai",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Inserisce solo il nome della tabella o della vista.",
|
||||
tableCompletionSchemaQualificationCollision: "In caso di conflitto",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Usa il comportamento automatico attuale e include lo schema solo quando necessario.",
|
||||
tableCompletionSchemaQualificationAlways: "Sempre",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Include lo schema ogni volta che è disponibile nei metadati.",
|
||||
autoAliasTables: "Aggiungi alias tabella automaticamente",
|
||||
autoAliasTablesDescription: "Quando scegli una tabella in FROM o JOIN, inserisce un alias generato come order_items AS oi.",
|
||||
sqlServerSpaceConfirmsCompletion: "Conferma il completamento con lo spazio (stile SQL Server)",
|
||||
|
||||
@@ -7679,6 +7679,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "SQL Server 形式プレースホルダーを置き換える値の入力を求めます(セッション変数と競合する場合があります)。",
|
||||
sqlVariableSyntax_atSet: "インライン変数展開",
|
||||
sqlVariableSyntax_atSetDescription: "実行前にインライン変数宣言を展開し、その値をインライン化します。",
|
||||
tableCompletionSchemaQualification: "テーブル補完のスキーマ修飾",
|
||||
tableCompletionSchemaQualificationDescription: "補完したテーブル名とビュー名にスキーマを含めるタイミングを選択します。",
|
||||
tableCompletionSchemaQualificationNever: "しない",
|
||||
tableCompletionSchemaQualificationNeverDescription: "テーブル名またはビュー名だけを挿入します。",
|
||||
tableCompletionSchemaQualificationCollision: "名前が重複する場合",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "現在の自動動作を使用し、必要な場合だけスキーマを挿入します。",
|
||||
tableCompletionSchemaQualificationAlways: "常に",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "メタデータにスキーマがある場合は常に挿入します。",
|
||||
autoAliasTables: "テーブル別名を自動追加",
|
||||
autoAliasTablesDescription: "FROM または JOIN でテーブル補完を選択すると、order_items AS oi のような生成済み別名を挿入します。",
|
||||
sqlServerSpaceConfirmsCompletion: "スペースで補完を確定(SQL Server 方式)",
|
||||
|
||||
@@ -7454,6 +7454,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "SQL Server 스타일 자리표시자를 치환할 값을 입력하라는 메시지를 표시합니다 (세션 변수와 충돌할 수 있음).",
|
||||
sqlVariableSyntax_atSet: "인라인 변수 확장",
|
||||
sqlVariableSyntax_atSetDescription: "실행 전에 인라인 변수 선언을 확장하고 값을 치환합니다.",
|
||||
tableCompletionSchemaQualification: "테이블 완성 스키마 한정",
|
||||
tableCompletionSchemaQualificationDescription: "완성된 테이블 및 뷰 이름에 스키마를 포함할 시점을 선택합니다.",
|
||||
tableCompletionSchemaQualificationNever: "안 함",
|
||||
tableCompletionSchemaQualificationNeverDescription: "테이블 또는 뷰 이름만 삽입합니다.",
|
||||
tableCompletionSchemaQualificationCollision: "이름 충돌 시",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "현재 자동 동작을 사용하여 필요한 경우에만 스키마를 삽입합니다.",
|
||||
tableCompletionSchemaQualificationAlways: "항상",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "메타데이터에 스키마가 있으면 항상 삽입합니다.",
|
||||
autoAliasTables: "자동으로 테이블 별칭 추가",
|
||||
autoAliasTablesDescription: "FROM이나 JOIN에서 테이블 완성을 선택할 때 order_items AS oi 같은 생성된 별칭을 삽입합니다.",
|
||||
sqlServerSpaceConfirmsCompletion: "공백으로 완성 확정(SQL Server 방식)",
|
||||
|
||||
@@ -7675,6 +7675,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "Solicita um valor para substituir os espaços reservados de estilo SQL Server (podem colidir com variáveis de sessão).",
|
||||
sqlVariableSyntax_atSet: "Expansão de variáveis inline",
|
||||
sqlVariableSyntax_atSetDescription: "Expande as declarações de variáveis inline e substitui seus valores antes de executar.",
|
||||
tableCompletionSchemaQualification: "Qualificação de schema no autocompletar de tabelas",
|
||||
tableCompletionSchemaQualificationDescription: "Escolha quando nomes de tabelas e views autocompletados incluem o schema.",
|
||||
tableCompletionSchemaQualificationNever: "Nunca",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Insere apenas o nome da tabela ou view.",
|
||||
tableCompletionSchemaQualificationCollision: "Em caso de conflito",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Usa o comportamento automático atual e inclui o schema apenas quando necessário.",
|
||||
tableCompletionSchemaQualificationAlways: "Sempre",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Inclui o schema sempre que ele estiver disponível nos metadados.",
|
||||
autoAliasTables: "Adicionar alias de tabela automaticamente",
|
||||
autoAliasTablesDescription: "Ao escolher uma tabela em FROM ou JOIN, insere um alias gerado como order_items AS oi.",
|
||||
sqlServerSpaceConfirmsCompletion: "Confirmar a conclusão com espaço (estilo SQL Server)",
|
||||
|
||||
@@ -8934,6 +8934,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "Запрашивать значение для подстановки заполнителей в стиле SQL Server (может конфликтовать с переменными сеанса).",
|
||||
sqlVariableSyntax_atSet: "Раскрытие встроенных переменных",
|
||||
sqlVariableSyntax_atSetDescription: "Раскрывать встроенные объявления переменных и подставлять их значения перед выполнением.",
|
||||
tableCompletionSchemaQualification: "Указание схемы при автодополнении таблиц",
|
||||
tableCompletionSchemaQualificationDescription: "Выберите, когда дополненные имена таблиц и представлений должны включать схему.",
|
||||
tableCompletionSchemaQualificationNever: "Никогда",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Вставлять только имя таблицы или представления.",
|
||||
tableCompletionSchemaQualificationCollision: "При совпадении имён",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Использовать текущее автоматическое поведение и добавлять схему только при необходимости.",
|
||||
tableCompletionSchemaQualificationAlways: "Всегда",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Добавлять схему всегда, когда она доступна в метаданных.",
|
||||
autoAliasTables: "Автоматически добавлять псевдонимы таблиц",
|
||||
autoAliasTablesDescription: "При выборе автодополнения таблицы в FROM или JOIN вставлять сгенерированный псевдоним, например order_items AS oi.",
|
||||
sqlServerSpaceConfirmsCompletion: "Пробел подтверждает автодополнение (стиль SQL Server)",
|
||||
|
||||
@@ -7636,6 +7636,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "SQL Server biçemli yer tutucuları değiştirmek için değer sor (oturum değişkenleriyle çakışabilir).",
|
||||
sqlVariableSyntax_atSet: "Satır içi değişken genişletme",
|
||||
sqlVariableSyntax_atSetDescription: "Satır içi değişken bildirimlerini genişlet ve çalıştırmadan önce değerlerini yerine koy.",
|
||||
tableCompletionSchemaQualification: "Tablo tamamlamasında şema nitelemesi",
|
||||
tableCompletionSchemaQualificationDescription: "Tamamlanan tablo ve görünüm adlarının ne zaman şema içereceğini seçin.",
|
||||
tableCompletionSchemaQualificationNever: "Hiçbir zaman",
|
||||
tableCompletionSchemaQualificationNeverDescription: "Yalnızca tablo veya görünüm adını ekler.",
|
||||
tableCompletionSchemaQualificationCollision: "Ad çakışmasında",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "Geçerli otomatik davranışı kullanır ve şemayı yalnızca gerektiğinde ekler.",
|
||||
tableCompletionSchemaQualificationAlways: "Her zaman",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "Şema meta verilerde bulunduğunda her zaman ekler.",
|
||||
autoAliasTables: "Tablo takma adlarını otomatik ekle",
|
||||
autoAliasTablesDescription: "FROM veya JOIN içinde bir tablo tamamlaması seçildiğinde order_items AS oi gibi üretilmiş bir takma ad ekle.",
|
||||
sqlServerSpaceConfirmsCompletion: "Boşlukla tamamlamayı onayla (SQL Server tarzı)",
|
||||
|
||||
@@ -8376,6 +8376,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "为 SQL Server 风格占位符提示输入替换值(可能与会话变量冲突)。",
|
||||
sqlVariableSyntax_atSet: "内联变量展开",
|
||||
sqlVariableSyntax_atSetDescription: "执行前展开内联变量声明并就地代入其值。",
|
||||
tableCompletionSchemaQualification: "SQL 表补全的模式限定",
|
||||
tableCompletionSchemaQualificationDescription: "选择表和视图补全插入时是否带模式名。",
|
||||
tableCompletionSchemaQualificationNever: "从不",
|
||||
tableCompletionSchemaQualificationNeverDescription: "仅插入表名或视图名。",
|
||||
tableCompletionSchemaQualificationCollision: "同名冲突时",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "保持当前自动行为,仅在需要时插入模式名。",
|
||||
tableCompletionSchemaQualificationAlways: "始终",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "元数据提供模式名时始终插入。",
|
||||
autoAliasTables: "自动添加表别名",
|
||||
autoAliasTablesDescription: "补全 FROM 或 JOIN 表名时自动添加别名。",
|
||||
sqlServerSpaceConfirmsCompletion: "空格确认补全(SQL Server 习惯)",
|
||||
|
||||
@@ -6983,6 +6983,14 @@ export default withEnglishFallback({
|
||||
sqlVariableSyntax_sqlserverDescription: "為 SQL Server 風格佔位符提示輸入替換值(可能與工作階段變數衝突)。",
|
||||
sqlVariableSyntax_atSet: "內聯變數展開",
|
||||
sqlVariableSyntax_atSetDescription: "執行前展開內聯變數宣告並就地代入其值。",
|
||||
tableCompletionSchemaQualification: "SQL 資料表補全的結構描述限定",
|
||||
tableCompletionSchemaQualificationDescription: "選擇資料表和檢視補全插入時是否帶結構描述名稱。",
|
||||
tableCompletionSchemaQualificationNever: "永不",
|
||||
tableCompletionSchemaQualificationNeverDescription: "僅插入資料表或檢視名稱。",
|
||||
tableCompletionSchemaQualificationCollision: "同名衝突時",
|
||||
tableCompletionSchemaQualificationCollisionDescription: "保持目前自動行為,僅在需要時插入結構描述名稱。",
|
||||
tableCompletionSchemaQualificationAlways: "一律",
|
||||
tableCompletionSchemaQualificationAlwaysDescription: "中繼資料提供結構描述名稱時一律插入。",
|
||||
autoAliasTables: "自動加入資料表別名",
|
||||
autoAliasTablesDescription: "在 FROM 或 JOIN 中選擇資料表補全時,自動插入類似 order_items AS oi 的資料表別名。",
|
||||
sqlServerSpaceConfirmsCompletion: "空格確認補全(SQL Server 習慣)",
|
||||
|
||||
@@ -30,6 +30,7 @@ function semanticCompletion(markedSql: string, input: Partial<SqlCompletionProvi
|
||||
databaseType: options.databaseType,
|
||||
keywordCase: input.keywordCase,
|
||||
autoAliasTables: input.autoAliasTables,
|
||||
tableCompletionSchemaQualification: input.tableCompletionSchemaQualification,
|
||||
});
|
||||
return { sql, cursor, model, context, items };
|
||||
}
|
||||
|
||||
@@ -1365,7 +1365,7 @@ describe("sqlCompletion scoped metadata ranking", () => {
|
||||
expect(items.findIndex((item) => item.label === "ORDERS_10K")).toBeLessThan(items.findIndex((item) => item.label === "TABLE"));
|
||||
});
|
||||
|
||||
it("qualifies same-name PostgreSQL tables from different schemas", () => {
|
||||
it("uses on-collision qualification by default for same-name PostgreSQL tables", () => {
|
||||
const sql = "SELECT * FROM shared";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
databaseType: "postgres",
|
||||
@@ -1381,6 +1381,86 @@ describe("sqlCompletion scoped metadata ranking", () => {
|
||||
expect(items.map((item) => item.apply).sort()).toEqual(["public.shared", "reporting.shared"]);
|
||||
});
|
||||
|
||||
it("never qualifies table names while keeping colliding candidates distinct", () => {
|
||||
const sql = "SELECT * FROM shared";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
databaseType: "postgres",
|
||||
dialect: "postgres",
|
||||
tables: [
|
||||
{ name: "shared", schema: "public", type: "table", applyName: "public.shared" },
|
||||
{ name: "shared", schema: "reporting", type: "view", applyName: "reporting.shared" },
|
||||
],
|
||||
columnsByTable: new Map(),
|
||||
tableCompletionSchemaQualification: "never",
|
||||
}).filter((item) => item.type === "table");
|
||||
|
||||
expect(items).toHaveLength(2);
|
||||
expect(items.map((item) => item.apply)).toEqual(["shared", "shared"]);
|
||||
expect(items.map((item) => item.detail).sort()).toEqual(["public.shared", "reporting.shared"]);
|
||||
});
|
||||
|
||||
it("always qualifies tables and views when schema metadata is available", () => {
|
||||
const sql = "SELECT * FROM ";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
databaseType: "postgres",
|
||||
dialect: "postgres",
|
||||
tables: [
|
||||
{ name: "Order Details", schema: "Sales Data", type: "view" },
|
||||
{ name: "scratch", type: "table" },
|
||||
],
|
||||
columnsByTable: new Map(),
|
||||
tableCompletionSchemaQualification: "always",
|
||||
}).filter((item) => item.type === "table");
|
||||
|
||||
expect(items.find((item) => item.label === "Order Details")?.apply).toBe('"Sales Data"."Order Details"');
|
||||
expect(items.find((item) => item.label === "scratch")?.apply).toBe("scratch");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["never", "customers"],
|
||||
["always", "sales.customers"],
|
||||
] as const)("applies the %s policy to foreign-key table suggestions", (tableCompletionSchemaQualification, expectedApply) => {
|
||||
const sql = "SELECT * FROM sales.orders o JOIN cus";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
databaseType: "postgres",
|
||||
dialect: "postgres",
|
||||
tables: [
|
||||
{ name: "orders", schema: "sales", type: "table" },
|
||||
{ name: "customers", schema: "sales", type: "table" },
|
||||
],
|
||||
columnsByTable: new Map(),
|
||||
foreignKeysByTable: new Map([["sales.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_table: "customers", ref_schema: "sales", ref_column: "id" }]]]),
|
||||
tableCompletionSchemaQualification,
|
||||
});
|
||||
const customers = items.filter((item) => item.type === "table" && item.label === "customers");
|
||||
|
||||
expect(customers).toHaveLength(1);
|
||||
expect(customers[0]?.apply).toBe(expectedApply);
|
||||
expect(customers[0]?.detail).toContain("related by");
|
||||
});
|
||||
|
||||
it("does not repeat an already typed quoted schema or SQL Server database/schema qualifier", () => {
|
||||
const postgresSql = 'SELECT * FROM "Sales Data".Ord';
|
||||
const postgresItems = buildSqlCompletionItems(postgresSql, postgresSql.length, {
|
||||
databaseType: "postgres",
|
||||
dialect: "postgres",
|
||||
tables: [{ name: "Order Details", schema: "Sales Data", type: "table" }],
|
||||
columnsByTable: new Map(),
|
||||
tableCompletionSchemaQualification: "always",
|
||||
});
|
||||
const sqlServerSql = "SELECT * FROM Reporting.dbo.Ord";
|
||||
const sqlServerItems = buildSqlCompletionItems(sqlServerSql, sqlServerSql.length, {
|
||||
databaseType: "sqlserver",
|
||||
dialect: "sqlserver",
|
||||
tables: [{ name: "Order Details", database: "Reporting", schema: "dbo", type: "table" }],
|
||||
columnsByTable: new Map(),
|
||||
tableCompletionSchemaQualification: "always",
|
||||
});
|
||||
|
||||
expect(postgresItems.find((item) => item.label === "Order Details")?.apply).toBe('"Order Details"');
|
||||
expect(sqlServerItems.find((item) => item.label === "Order Details")?.apply).toBe("[Order Details]");
|
||||
});
|
||||
|
||||
it("qualifies same-name tables for generic metadata providers", () => {
|
||||
const sql = "SELECT * FROM orders";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
|
||||
@@ -136,6 +136,10 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
|
||||
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("completionTriggerMode");
|
||||
});
|
||||
|
||||
it("includes tableCompletionSchemaQualification", () => {
|
||||
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("tableCompletionSchemaQualification");
|
||||
});
|
||||
|
||||
it("includes tableHoverLookupMode", () => {
|
||||
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("tableHoverLookupMode");
|
||||
});
|
||||
@@ -231,6 +235,11 @@ describe("editorSettingsDraftFromSettings", () => {
|
||||
const draft = editorSettingsDraftFromSettings(makeSettings({ completionTriggerMode: "always" as unknown } as Partial<EditorSettings>));
|
||||
expect(draft.completionTriggerMode).toBe("positional");
|
||||
});
|
||||
|
||||
it("maps and normalizes tableCompletionSchemaQualification", () => {
|
||||
expect(editorSettingsDraftFromSettings(makeSettings({ tableCompletionSchemaQualification: "always" })).tableCompletionSchemaQualification).toBe("always");
|
||||
expect(editorSettingsDraftFromSettings(makeSettings({ tableCompletionSchemaQualification: "invalid" } as any)).tableCompletionSchemaQualification).toBe("collision");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeTableOpenPageSizeDraft", () => {
|
||||
@@ -406,6 +415,16 @@ describe("editorSettingsPatchFromDraft", () => {
|
||||
const patch = editorSettingsPatchFromDraft(draft, base);
|
||||
expect(patch.completionTriggerMode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("includes tableCompletionSchemaQualification only when changed", () => {
|
||||
const settings = makeSettings({ tableCompletionSchemaQualification: "collision" });
|
||||
const draft = editorSettingsDraftFromSettings(settings);
|
||||
const base = editorSettingsDraftFromSettings(settings);
|
||||
|
||||
expect(editorSettingsPatchFromDraft(draft, base).tableCompletionSchemaQualification).toBeUndefined();
|
||||
draft.tableCompletionSchemaQualification = "never";
|
||||
expect(editorSettingsPatchFromDraft(draft, base).tableCompletionSchemaQualification).toBe("never");
|
||||
});
|
||||
});
|
||||
|
||||
describe("EDITOR_SETTINGS_DRAFT_KEYS - tabLayout", () => {
|
||||
|
||||
@@ -295,6 +295,7 @@ describe("settings search", () => {
|
||||
{ titleKey: "settings.sidebarObjectInfoMode", category: "navigation", targetId: "navigation" },
|
||||
{ titleKey: "settings.insertSpaceAfterCompletion", category: "editor", targetId: "editor" },
|
||||
{ titleKey: "settings.completionTriggerMode", category: "editor", targetId: "editor" },
|
||||
{ titleKey: "settings.tableCompletionSchemaQualification", category: "editor", targetId: "editor" },
|
||||
{ titleKey: "settings.autoAliasTables", category: "editor", targetId: "editor" },
|
||||
{ titleKey: "settings.clickTableNavigationTarget", category: "navigation", targetId: "navigation" },
|
||||
{ titleKey: "settings.prefillNewQueryWithSelect", category: "navigation", targetId: "navigation" },
|
||||
|
||||
@@ -68,6 +68,18 @@ describe("settingsTransfer", () => {
|
||||
expect(result.error.detail).toContain("updateDownloadSource");
|
||||
});
|
||||
|
||||
it("round-trips table completion schema qualification as an editor setting", () => {
|
||||
const result = parseSettingsTransferFile(fileWith({ tableCompletionSchemaQualification: "always" }));
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.value.editorSettings.tableCompletionSchemaQualification).toBe("always");
|
||||
expect(result.value.categories).toEqual(["editor"]);
|
||||
|
||||
const invalid = parseSettingsTransferFile(fileWith({ tableCompletionSchemaQualification: "sometimes" }));
|
||||
expect(invalid.ok).toBe(false);
|
||||
if (!invalid.ok) expect(invalid.error.detail).toContain("tableCompletionSchemaQualification");
|
||||
});
|
||||
|
||||
it("rejects out-of-range numbers that the normalizer clamps", () => {
|
||||
const result = parseSettingsTransferFile(fileWith({ sidebarIndent: 99999 }));
|
||||
expect(result.ok).toBe(false);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
|
||||
import { normalizeQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||
import { normalizeExternalSqlEditorMaxMb } from "@/lib/sql/sqlFileOpen";
|
||||
import { normalizeCompletionTriggerMode } from "@/lib/sql/sqlCompletionTriggerPolicy";
|
||||
import { normalizeSqlTableCompletionSchemaQualification } from "@/lib/sql/sqlCompletionSchemaQualification";
|
||||
import { normalizeTableHoverLookupMode } from "@/lib/editor/hoverTableLookup";
|
||||
import { normalizeRedisKeyTemplates } from "@/lib/redis/redisKeyTemplates";
|
||||
|
||||
@@ -25,6 +26,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
|
||||
"showCurrentStatementFrame",
|
||||
"showInsertValueHints",
|
||||
"autoAliasTables",
|
||||
"tableCompletionSchemaQualification",
|
||||
"insertSpaceAfterCompletion",
|
||||
"sqlServerSpaceConfirmsCompletion",
|
||||
"sortCompletionColumnsAlphabetically",
|
||||
@@ -158,6 +160,7 @@ function normalizedDraftValue(key: EditorSettingsDraftKey, value: unknown): unkn
|
||||
if (key === "queryResultMaxRows") return normalizeQueryResultMaxRowsDraft(value);
|
||||
if (key === "externalSqlEditorMaxMb") return normalizeExternalSqlEditorMaxMb(value);
|
||||
if (key === "completionTriggerMode") return normalizeCompletionTriggerMode(value);
|
||||
if (key === "tableCompletionSchemaQualification") return normalizeSqlTableCompletionSchemaQualification(value);
|
||||
if (key === "tableHoverLookupMode") return normalizeTableHoverLookupMode(value);
|
||||
if (key === "redisKeyTemplates") return normalizeRedisKeyTemplates(value);
|
||||
if (key === "backgroundImage") return normalizeBackgroundImageSettings(value);
|
||||
|
||||
@@ -145,6 +145,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
|
||||
{ id: "editor-completion-spacing", category: "editor", titleKey: "settings.insertSpaceAfterCompletion", descriptionKey: "settings.insertSpaceAfterCompletionDescription", targetId: "editor" },
|
||||
{ id: "editor-sqlserver-space-completion", category: "editor", titleKey: "settings.sqlServerSpaceConfirmsCompletion", descriptionKey: "settings.sqlServerSpaceConfirmsCompletionDescription", targetId: "editor", visible: sqlServerSpaceCompletionVisible },
|
||||
{ id: "editor-completion-trigger-mode", category: "editor", titleKey: "settings.completionTriggerMode", descriptionKey: "settings.completionTriggerModeDescription", targetId: "editor" },
|
||||
{ id: "editor-table-completion-schema-qualification", category: "editor", titleKey: "settings.tableCompletionSchemaQualification", descriptionKey: "settings.tableCompletionSchemaQualificationDescription", targetId: "editor" },
|
||||
{ id: "editor-auto-alias", category: "editor", titleKey: "settings.autoAliasTables", descriptionKey: "settings.autoAliasTablesDescription", targetId: "editor" },
|
||||
{ id: "editor-unsaved-close", category: "editor", titleKey: "settings.confirmUnsavedSqlClose", descriptionKey: "settings.confirmUnsavedSqlCloseDescription", targetId: "editor" },
|
||||
{ id: "editor-app-close-unsaved-tabs", category: "editor", titleKey: "settings.appCloseUnsavedTabsMode", descriptionKey: "settings.appCloseUnsavedTabsModeDescription", targetId: "editor" },
|
||||
|
||||
@@ -83,6 +83,7 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record<SettingsTransferCategoryId, readon
|
||||
"showCurrentStatementFrame",
|
||||
"showInsertValueHints",
|
||||
"autoAliasTables",
|
||||
"tableCompletionSchemaQualification",
|
||||
"insertSpaceAfterCompletion",
|
||||
"sqlServerSpaceConfirmsCompletion",
|
||||
"sortCompletionColumnsAlphabetically",
|
||||
|
||||
@@ -21,6 +21,7 @@ import { driverProfileCompletionObjects, driverProfileCompletionTableMetadata, d
|
||||
import { DORIS_FUNCTION_DOCS, DORIS_FUNCTION_SIGNATURES } from "@/lib/sql/doris/functions";
|
||||
import { rejectsAliasReferenceInHaving } from "@/lib/database/databaseFeatureSupport";
|
||||
import { isOracleCompletionDatabase } from "@/lib/sql/oracleCompletionSession";
|
||||
import { normalizeSqlTableCompletionSchemaQualification, type SqlTableCompletionSchemaQualification } from "@/lib/sql/sqlCompletionSchemaQualification";
|
||||
|
||||
export { DEFAULT_SQL_SNIPPETS, resolveSqlSnippetBodyForDatabase } from "@/lib/sql/sqlSnippetTemplates";
|
||||
|
||||
@@ -1616,6 +1617,7 @@ export interface SqlCompletionProviderInput {
|
||||
keywordCase?: SqlKeywordCase;
|
||||
functionCase?: SqlKeywordCase;
|
||||
autoAliasTables?: boolean;
|
||||
tableCompletionSchemaQualification?: SqlTableCompletionSchemaQualification;
|
||||
quoteIdentifiers?: boolean;
|
||||
}
|
||||
|
||||
@@ -1636,6 +1638,7 @@ export function buildSqlCompletionItems(
|
||||
keywordCase?: SqlKeywordCase;
|
||||
functionCase?: SqlKeywordCase;
|
||||
autoAliasTables?: boolean;
|
||||
tableCompletionSchemaQualification?: SqlTableCompletionSchemaQualification;
|
||||
quoteIdentifiers?: boolean;
|
||||
},
|
||||
): SqlCompletionItem[] {
|
||||
@@ -1768,8 +1771,9 @@ class SqlCompletionProvider {
|
||||
|
||||
if (!context.exclusiveColumnSuggestions && context.suggestTables) {
|
||||
const autoAliasTables = !!this.input.autoAliasTables && context.autoAliasTableCompletions && !context.tableCompletionTargetAliasUnsafe && supportsTableAliases(this.databaseType);
|
||||
this.items.push(...buildForeignKeyRelatedTableItems(context, completionTables, this.input.foreignKeysByTable, this.dialect, autoAliasTables, this.databaseType, this.input.currentSchema));
|
||||
this.items.push(...buildTableItems(context, completionTables, this.dialect, autoAliasTables, context.referencedTables, this.databaseType, this.input.currentSchema));
|
||||
const schemaQualification = normalizeSqlTableCompletionSchemaQualification(this.input.tableCompletionSchemaQualification);
|
||||
this.items.push(...buildForeignKeyRelatedTableItems(context, completionTables, this.input.foreignKeysByTable, this.dialect, autoAliasTables, this.databaseType, this.input.currentSchema, schemaQualification));
|
||||
this.items.push(...buildTableItems(context, completionTables, this.dialect, autoAliasTables, context.referencedTables, this.databaseType, this.input.currentSchema, schemaQualification));
|
||||
if (this.databaseType === "clickhouse") {
|
||||
this.items.push(...buildClickHouseFunctionItems(context.prefix, context.openingParenAfterCursor, "table"));
|
||||
}
|
||||
@@ -3932,27 +3936,30 @@ function resolveTableSchemaQualification(
|
||||
databaseType: DatabaseType | undefined,
|
||||
currentSchema: string | undefined,
|
||||
schemasByTableName: Map<string, Set<string>>,
|
||||
schemaQualificationMode: SqlTableCompletionSchemaQualification,
|
||||
): { ambiguousTableName: boolean; schemaQualification: boolean; defaultApplyName: string } {
|
||||
const oracleSchemaQualification = isOracleCompletionDatabase(databaseType) && table.schema && table.schema.toUpperCase() !== "PUBLIC" && (!currentSchema || normalizeIdentifierPart(table.schema) !== normalizeIdentifierPart(currentSchema));
|
||||
// A bare table name is ambiguous when metadata contains the same name in multiple schemas.
|
||||
// Keep Oracle's current-schema behavior, but qualify the generic/PostgreSQL/SQL Server paths.
|
||||
// Collision mode keeps Oracle's current-schema behavior and qualifies the
|
||||
// generic/PostgreSQL/SQL Server paths only when metadata is ambiguous.
|
||||
const ambiguousTableName = !isOracleCompletionDatabase(databaseType) && (schemasByTableName.get(normalizeIdentifierPart(table.name))?.size ?? 0) > 1;
|
||||
const schemaQualification = !!table.schema && (oracleSchemaQualification || ambiguousTableName);
|
||||
const schemaQualification = !!table.schema && (schemaQualificationMode === "always" || (schemaQualificationMode === "collision" && (oracleSchemaQualification || ambiguousTableName)));
|
||||
const defaultApplyName = schemaQualification ? `${quoteCompletionApplyIdentifier(table.schema!, dialect)}.${quoteCompletionApplyIdentifier(table.name, dialect)}` : quoteCompletionApplyIdentifier(table.name, dialect);
|
||||
return { ambiguousTableName, schemaQualification, defaultApplyName };
|
||||
}
|
||||
|
||||
function buildTableItems(
|
||||
context: Pick<SqlCompletionContext, "prefix" | "qualifier">,
|
||||
context: Pick<SqlCompletionContext, "prefix" | "qualifier" | "qualifierParts">,
|
||||
tables: SqlCompletionTable[],
|
||||
dialect?: SqlCompletionApplyDialect,
|
||||
autoAliasTables = false,
|
||||
referencedTables: SqlCompletionReferencedTable[] = [],
|
||||
databaseType?: DatabaseType,
|
||||
currentSchema?: string,
|
||||
schemaQualificationMode: SqlTableCompletionSchemaQualification = "collision",
|
||||
): SqlCompletionItem[] {
|
||||
const { prefix } = context;
|
||||
const qualifierSchema = context.qualifier?.split(".").filter(Boolean).pop();
|
||||
const qualifierSchema = context.qualifierParts?.[context.qualifierParts.length - 1] ?? context.qualifier?.split(".").filter(Boolean).pop();
|
||||
const existingAliases = new Set(referencedTables.map((ref) => ref.alias?.toLowerCase()).filter((alias): alias is string => !!alias));
|
||||
const matchingTables = tables.filter((table) => matchesPrefix(table.name, prefix));
|
||||
// Ambiguity is decided among prefix-matching tables only, matching prior behavior.
|
||||
@@ -3960,11 +3967,19 @@ function buildTableItems(
|
||||
return matchingTables
|
||||
.map((table) => {
|
||||
const qualifiedByContext = !!qualifierSchema && !!table.schema && normalizeIdentifierPart(qualifierSchema) === normalizeIdentifierPart(table.schema);
|
||||
const { ambiguousTableName, defaultApplyName } = resolveTableSchemaQualification(table, dialect, databaseType, currentSchema, schemasByTableName);
|
||||
const { ambiguousTableName, defaultApplyName } = resolveTableSchemaQualification(table, dialect, databaseType, currentSchema, schemasByTableName, schemaQualificationMode);
|
||||
const unqualifiedApplyName = quoteCompletionApplyIdentifier(table.name, dialect);
|
||||
const rawSuppliedApplyName = table.applyName?.trim();
|
||||
const suppliedApplyName = dialect === "sqlserver" && rawSuppliedApplyName ? quoteCompletionApplyName(rawSuppliedApplyName, dialect) : rawSuppliedApplyName;
|
||||
const suppliedApplyNameIsQualified = suppliedApplyName?.includes(".") === true;
|
||||
const applyName = qualifiedByContext ? quoteCompletionApplyIdentifier(table.name, dialect) : ambiguousTableName && !!table.schema && (!suppliedApplyName || !suppliedApplyNameIsQualified) ? defaultApplyName : (suppliedApplyName ?? defaultApplyName);
|
||||
const applyName =
|
||||
qualifiedByContext || schemaQualificationMode === "never"
|
||||
? unqualifiedApplyName
|
||||
: schemaQualificationMode === "always" && table.schema
|
||||
? defaultApplyName
|
||||
: ambiguousTableName && table.schema && (!suppliedApplyName || !suppliedApplyNameIsQualified)
|
||||
? defaultApplyName
|
||||
: (suppliedApplyName ?? defaultApplyName);
|
||||
const alias = autoAliasTables ? generateTableCompletionAlias(table.name, existingAliases) : "";
|
||||
const schemaDetail = ambiguousTableName && table.schema ? `${table.schema}.${table.name}` : undefined;
|
||||
const detail = table.detail && schemaDetail ? `${schemaDetail} ${table.detail}` : (table.detail ?? schemaDetail ?? (table.type === "table" ? undefined : table.type));
|
||||
@@ -3974,7 +3989,7 @@ function buildTableItems(
|
||||
detail,
|
||||
apply: formatTableAliasApply(applyName, alias),
|
||||
boost: computeBoost(table.name, prefix) + 1000 + (table.boost ?? 0),
|
||||
dedupeKey: table.applyName || ambiguousTableName || (isOracleCompletionDatabase(databaseType) && table.schema) ? applyName : undefined,
|
||||
dedupeKey: schemaQualificationMode === "never" && table.schema ? `${quoteCompletionApplyIdentifier(table.schema, dialect)}.${unqualifiedApplyName}` : table.applyName || ambiguousTableName || (isOracleCompletionDatabase(databaseType) && table.schema) ? applyName : undefined,
|
||||
};
|
||||
})
|
||||
.sort(compareCompletionItems)
|
||||
@@ -3989,6 +4004,7 @@ function buildForeignKeyRelatedTableItems(
|
||||
autoAliasTables = false,
|
||||
databaseType?: DatabaseType,
|
||||
currentSchema?: string,
|
||||
schemaQualificationMode: SqlTableCompletionSchemaQualification = "collision",
|
||||
): SqlCompletionItem[] {
|
||||
if (!foreignKeysByTable || context.referencedTables.length === 0) return [];
|
||||
const candidates = new Map<string, { table: SqlCompletionTable; detail: string }>();
|
||||
@@ -4016,11 +4032,14 @@ function buildForeignKeyRelatedTableItems(
|
||||
// `schema.table` exactly when regular candidates would. Built from the same
|
||||
// prefix-matching table set buildTableItems uses, keeping the two in lockstep.
|
||||
const schemasByTableName = collectSchemasByTableName(tables.filter((table) => matchesPrefix(table.name, context.prefix)));
|
||||
const qualifierSchema = context.qualifierParts?.[context.qualifierParts.length - 1] ?? context.qualifier?.split(".").filter(Boolean).pop();
|
||||
|
||||
return [...candidates.values()]
|
||||
.map(({ table, detail }) => {
|
||||
const { ambiguousTableName, defaultApplyName } = resolveTableSchemaQualification(table, dialect, databaseType, currentSchema, schemasByTableName);
|
||||
const applyName = defaultApplyName;
|
||||
const qualifiedByContext = !!qualifierSchema && !!table.schema && normalizeIdentifierPart(qualifierSchema) === normalizeIdentifierPart(table.schema);
|
||||
const { ambiguousTableName, defaultApplyName } = resolveTableSchemaQualification(table, dialect, databaseType, currentSchema, schemasByTableName, schemaQualificationMode);
|
||||
const unqualifiedApplyName = quoteCompletionApplyIdentifier(table.name, dialect);
|
||||
const applyName = qualifiedByContext || schemaQualificationMode === "never" ? unqualifiedApplyName : defaultApplyName;
|
||||
const alias = autoAliasTables ? generateTableCompletionAlias(table.name, existingAliases) : "";
|
||||
return {
|
||||
label: table.name,
|
||||
@@ -4030,7 +4049,7 @@ function buildForeignKeyRelatedTableItems(
|
||||
boost: computeBoost(table.name, context.prefix) + 3600,
|
||||
// Mirror buildTableItems' dedupeKey so an FK candidate and the regular
|
||||
// candidate for the same schema-qualified table collapse to one entry.
|
||||
dedupeKey: ambiguousTableName || (isOracleCompletionDatabase(databaseType) && table.schema) ? applyName : undefined,
|
||||
dedupeKey: schemaQualificationMode === "never" && table.schema ? `${quoteCompletionApplyIdentifier(table.schema, dialect)}.${unqualifiedApplyName}` : ambiguousTableName || (isOracleCompletionDatabase(databaseType) && table.schema) ? applyName : undefined,
|
||||
};
|
||||
})
|
||||
.sort(compareCompletionItems);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export type SqlTableCompletionSchemaQualification = "never" | "collision" | "always";
|
||||
|
||||
export const DEFAULT_SQL_TABLE_COMPLETION_SCHEMA_QUALIFICATION: SqlTableCompletionSchemaQualification = "collision";
|
||||
|
||||
export function normalizeSqlTableCompletionSchemaQualification(value: unknown): SqlTableCompletionSchemaQualification {
|
||||
if (value === "never" || value === "collision" || value === "always") return value;
|
||||
return DEFAULT_SQL_TABLE_COMPLETION_SCHEMA_QUALIFICATION;
|
||||
}
|
||||
@@ -224,6 +224,13 @@ describe("normalizeEditorSettings", () => {
|
||||
expect(normalizeEditorSettings({ autoAliasTables: false }).autoAliasTables).toBe(false);
|
||||
});
|
||||
|
||||
it("defaults table completion schema qualification to collision and preserves valid modes", () => {
|
||||
expect(normalizeEditorSettings({}).tableCompletionSchemaQualification).toBe("collision");
|
||||
expect(normalizeEditorSettings({ tableCompletionSchemaQualification: "never" }).tableCompletionSchemaQualification).toBe("never");
|
||||
expect(normalizeEditorSettings({ tableCompletionSchemaQualification: "always" }).tableCompletionSchemaQualification).toBe("always");
|
||||
expect(normalizeEditorSettings({ tableCompletionSchemaQualification: "invalid" } as any).tableCompletionSchemaQualification).toBe("collision");
|
||||
});
|
||||
|
||||
it("enables a trailing space after completion by default and preserves the opt-out", () => {
|
||||
expect(normalizeEditorSettings({}).insertSpaceAfterCompletion).toBe(true);
|
||||
expect(normalizeEditorSettings({ insertSpaceAfterCompletion: false }).insertSpaceAfterCompletion).toBe(false);
|
||||
@@ -1134,6 +1141,22 @@ describe("settingsStore persisted settings initialization", () => {
|
||||
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ dataGridKeepFilterEditorExpanded: false }));
|
||||
});
|
||||
|
||||
it("persists table completion schema qualification updates", async () => {
|
||||
const loadEditorSettings = vi.fn().mockResolvedValue({});
|
||||
const saveEditorSettings = vi.fn().mockResolvedValue(undefined);
|
||||
vi.doMock("@/lib/backend/api", () => ({ loadEditorSettings, saveEditorSettings }));
|
||||
|
||||
const { useSettingsStore } = await import("@/stores/settingsStore");
|
||||
const store = useSettingsStore();
|
||||
await store.initEditorSettings();
|
||||
saveEditorSettings.mockClear();
|
||||
|
||||
await store.updateEditorSettingsAndPersist({ tableCompletionSchemaQualification: "always" });
|
||||
|
||||
expect(store.editorSettings.tableCompletionSchemaQualification).toBe("always");
|
||||
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ tableCompletionSchemaQualification: "always" }));
|
||||
});
|
||||
|
||||
it("loads and persists the substitution switch without discarding syntax overrides", async () => {
|
||||
const loadEditorSettings = vi.fn().mockResolvedValue({
|
||||
sqlVariableSubstitutionEnabled: false,
|
||||
|
||||
@@ -33,12 +33,31 @@ import { DEFAULT_TABLE_COLUMN_TEMPLATE_FIELDS, normalizeTableColumnTemplateField
|
||||
import { type DataTabReuseMode, DEFAULT_DATA_TAB_REUSE_MODE, normalizeDataTabReuseMode } from "@/lib/tabs/dataTabReuseMode";
|
||||
import { normalizeTableHoverLookupMode, type TableHoverLookupMode } from "@/lib/editor/hoverTableLookup";
|
||||
import { normalizeCompletionTriggerMode, type SqlCompletionTriggerMode } from "@/lib/sql/sqlCompletionTriggerPolicy";
|
||||
import { DEFAULT_SQL_TABLE_COMPLETION_SCHEMA_QUALIFICATION, normalizeSqlTableCompletionSchemaQualification, type SqlTableCompletionSchemaQualification } from "@/lib/sql/sqlCompletionSchemaQualification";
|
||||
import { DEFAULT_CSV_QUOTE_MODE, normalizeCsvQuoteMode, type CsvQuoteMode } from "@/lib/export/csvQuoteMode";
|
||||
import { configureMetadataRuntimeCache, METADATA_CACHE_DEFAULT_MEMORY_MB, normalizeMetadataCacheMemoryMb } from "@/lib/metadata/metadataRuntimeCache";
|
||||
import type { AiApiStyle, AiAssistantMode, AiAuthMethod, AiChatSelectionState, AiConfig, AiConfigItem, AiConfiguredModel, AiEffortLevel, AiEffortSelection, AiModelEffortPreference, AiProvider, AiReasoningLevel, AiTestConnectionResult } from "@/types/ai";
|
||||
import type { SqlShortcutAction, SqlSnippet, TableInfoTab } from "@/types/database";
|
||||
|
||||
export type { AiApiStyle, AiAuthMethod, AiChatSelectionState, AiConfig, AiConfigItem, AiConfiguredModel, AiEffortLevel, AiEffortSelection, AiProvider, AiReasoningLevel, AiTestConnectionResult, CsvQuoteMode, DataTabReuseMode, SavedSqlOpenTargetMode, SqlCompletionTriggerMode, TableHoverLookupMode };
|
||||
export type {
|
||||
AiApiStyle,
|
||||
AiAuthMethod,
|
||||
AiChatSelectionState,
|
||||
AiConfig,
|
||||
AiConfigItem,
|
||||
AiConfiguredModel,
|
||||
AiEffortLevel,
|
||||
AiEffortSelection,
|
||||
AiProvider,
|
||||
AiReasoningLevel,
|
||||
AiTestConnectionResult,
|
||||
CsvQuoteMode,
|
||||
DataTabReuseMode,
|
||||
SavedSqlOpenTargetMode,
|
||||
SqlCompletionTriggerMode,
|
||||
SqlTableCompletionSchemaQualification,
|
||||
TableHoverLookupMode,
|
||||
};
|
||||
|
||||
export interface DesktopSettings {
|
||||
show_tray_icon: boolean;
|
||||
@@ -830,6 +849,7 @@ export interface EditorSettings {
|
||||
showCurrentStatementFrame: boolean;
|
||||
showInsertValueHints: boolean;
|
||||
autoAliasTables: boolean;
|
||||
tableCompletionSchemaQualification: SqlTableCompletionSchemaQualification;
|
||||
insertSpaceAfterCompletion: boolean;
|
||||
sqlServerSpaceConfirmsCompletion: boolean;
|
||||
sortCompletionColumnsAlphabetically: boolean;
|
||||
@@ -1114,6 +1134,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
showCurrentStatementFrame: true,
|
||||
showInsertValueHints: true,
|
||||
autoAliasTables: true,
|
||||
tableCompletionSchemaQualification: DEFAULT_SQL_TABLE_COMPLETION_SCHEMA_QUALIFICATION,
|
||||
insertSpaceAfterCompletion: true,
|
||||
sqlServerSpaceConfirmsCompletion: false,
|
||||
sortCompletionColumnsAlphabetically: true,
|
||||
@@ -1676,6 +1697,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
||||
showCurrentStatementFrame: typeof settings.showCurrentStatementFrame === "boolean" ? settings.showCurrentStatementFrame : DEFAULT_EDITOR_SETTINGS.showCurrentStatementFrame,
|
||||
showInsertValueHints: typeof settings.showInsertValueHints === "boolean" ? settings.showInsertValueHints : DEFAULT_EDITOR_SETTINGS.showInsertValueHints,
|
||||
autoAliasTables: settings.autoAliasTables ?? DEFAULT_EDITOR_SETTINGS.autoAliasTables,
|
||||
tableCompletionSchemaQualification: normalizeSqlTableCompletionSchemaQualification(settings.tableCompletionSchemaQualification),
|
||||
insertSpaceAfterCompletion: typeof settings.insertSpaceAfterCompletion === "boolean" ? settings.insertSpaceAfterCompletion : DEFAULT_EDITOR_SETTINGS.insertSpaceAfterCompletion,
|
||||
sqlServerSpaceConfirmsCompletion: typeof settings.sqlServerSpaceConfirmsCompletion === "boolean" ? settings.sqlServerSpaceConfirmsCompletion : DEFAULT_EDITOR_SETTINGS.sqlServerSpaceConfirmsCompletion,
|
||||
sortCompletionColumnsAlphabetically: typeof settings.sortCompletionColumnsAlphabetically === "boolean" ? settings.sortCompletionColumnsAlphabetically : DEFAULT_EDITOR_SETTINGS.sortCompletionColumnsAlphabetically,
|
||||
@@ -2505,6 +2527,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
if (partial.showCurrentStatementFrame !== undefined) editorSettings.value.showCurrentStatementFrame = partial.showCurrentStatementFrame === true;
|
||||
if (partial.showInsertValueHints !== undefined) editorSettings.value.showInsertValueHints = partial.showInsertValueHints === true;
|
||||
if (partial.autoAliasTables !== undefined) editorSettings.value.autoAliasTables = partial.autoAliasTables;
|
||||
if (partial.tableCompletionSchemaQualification !== undefined) editorSettings.value.tableCompletionSchemaQualification = normalizeSqlTableCompletionSchemaQualification(partial.tableCompletionSchemaQualification);
|
||||
if (partial.insertSpaceAfterCompletion !== undefined) editorSettings.value.insertSpaceAfterCompletion = partial.insertSpaceAfterCompletion === true;
|
||||
if (partial.sqlServerSpaceConfirmsCompletion !== undefined) editorSettings.value.sqlServerSpaceConfirmsCompletion = partial.sqlServerSpaceConfirmsCompletion === true;
|
||||
if (partial.sortCompletionColumnsAlphabetically !== undefined) editorSettings.value.sortCompletionColumnsAlphabetically = partial.sortCompletionColumnsAlphabetically === true;
|
||||
|
||||
Reference in New Issue
Block a user