feat(gbase8s): pick locale and charset when creating database

This commit is contained in:
弈秋 authored and GitHub committed 2026-09-16 21:37:34 +08:00
1 parent ea1de14517
commit 1d19c8b273
8 files changed
+190 -9

No files matched your search

@@ -538,7 +538,9 @@ watch(
</template>
</SearchableSelect>
</div>
<div class="grid gap-1.5">
<!-- The gbase8s locale path has no collations; only show the picker when the selected
charset actually has collation options (MySQL always does). -->
<div v-if="createDatabaseCollationOptionsForCharset(createDatabaseCharset, createDatabaseCollationsByCharset).length > 0" class="grid gap-1.5">
<label class="text-xs font-medium text-muted-foreground">{{ t("contextMenu.createDatabaseCollation") }}</label>
<SearchableSelect
v-model="createDatabaseCollation"
@@ -127,7 +127,7 @@ import { dataTabOpenModeFromTreeClick, type DataTabOpenMode } from "@/lib/sideba
import { isCopySidebarSelectionShortcut, isEditSidebarConnectionShortcut, isModRShortcut, isPasteSidebarSelectionShortcut } from "@/lib/editor/keyboardShortcuts";
import { handleSidebarTreeDeleteShortcut } from "@/lib/sidebar/sidebarTreeDeleteShortcut";
import { dataTableDoubleClickAction } from "@/lib/tabs/dataTabActivation";
import { attachedDatabaseNameFromPath, buildCreateDatabaseSql, buildDuckDbAttachDatabaseSql, buildSqliteAttachDatabaseSql, supportsCreateDatabaseCharset, uniqueAttachedDatabaseName } from "@/lib/database/createDatabaseSql";
import { attachedDatabaseNameFromPath, buildCreateDatabaseSql, buildDuckDbAttachDatabaseSql, buildSqliteAttachDatabaseSql, supportsCreateDatabaseCharset, supportsCreateDatabaseLocale, uniqueAttachedDatabaseName } from "@/lib/database/createDatabaseSql";
import { appendCreateDatabaseErrorHint } from "@/lib/database/createDatabaseErrorHints";
import { SQLITE_DATABASE_FILE_EXTENSIONS } from "@/lib/database/databaseFileDetection";
import {
@@ -202,7 +202,7 @@ import { savedSqlClipboardFileIds, savedSqlPasteTargetForNode } from "@/lib/save
import { exportSavedSqlFileContent } from "@/lib/savedSql/savedSqlExport";
import { isSqlServerLinkedNode } from "@/lib/database/sqlServerLinkedServers";
import { flattenTree } from "@/composables/useFlatTree";
import { createDatabaseCollationOptionsForCharset, nextCreateDatabaseCollation, normalizeCreateDatabaseCharset, parseCreateDatabaseCharsetMetadata } from "@/lib/database/createDatabaseCharsetOptions";
import { createDatabaseCollationOptionsForCharset, DEFAULT_GBASE8S_DATABASE_LOCALE, GBASE8S_DATABASE_LOCALES, nextCreateDatabaseCollation, normalizeCreateDatabaseCharset, parseCreateDatabaseCharsetMetadata } from "@/lib/database/createDatabaseCharsetOptions";
import { executeWithProductionContextGuard, executeWithProductionSqlGuard } from "@/lib/database/productionExecutionGuard";
import { connectionIsEffectivelyReadOnly } from "@/lib/database/readOnlyWriteAccess";
import { buildXuguCompileSql } from "@/lib/database/xuguCompileSql";
@@ -3499,6 +3499,11 @@ const canSetCreateDatabaseCharset = computed(() => {
return connectionNamespaceCreationTarget(config) === "database" && supportsCreateDatabaseCharset(config?.db_type, config?.driver_profile);
});
const canSetCreateDatabaseLocale = computed(() => {
const config = activeNode.value.connectionId ? connectionStore.getConfig(activeNode.value.connectionId) : undefined;
return connectionNamespaceCreationTarget(config) === "database" && supportsCreateDatabaseLocale(config?.db_type, config?.driver_profile);
});
const canDropDatabase = computed(() => {
const config = activeNode.value.connectionId ? connectionStore.getConfig(activeNode.value.connectionId) : undefined;
return activeNode.value.type === "database" && !isSqlServerLinkedNode(activeNode.value) && supportsDatabaseCreation(config?.db_type);
@@ -3805,7 +3810,15 @@ function openCreateDatabaseDialog() {
createDatabaseCharsetOptions.value = fallbackCreateDatabaseCharset.charsets;
createDatabaseCollationsByCharset.value = fallbackCreateDatabaseCharset.collationsByCharset;
showCreateDatabaseDialog.value = true;
if (canSetCreateDatabaseCharset.value) {
if (canSetCreateDatabaseLocale.value) {
// GBase 8s / Informix: the "charset" control selects the new database's DB_LOCALE, which the
// agent applies by opening the CREATE DATABASE session with it. Default to UTF-8 so new
// databases can store Chinese; no server charset catalog query is needed.
createDatabaseCharsetOptions.value = [...GBASE8S_DATABASE_LOCALES];
createDatabaseCollationsByCharset.value = {};
createDatabaseCharset.value = DEFAULT_GBASE8S_DATABASE_LOCALE;
createDatabaseCollation.value = "";
} else if (canSetCreateDatabaseCharset.value) {
void loadCreateDatabaseCharsetMetadata();
}
void loadCreateDatabaseUsers();
@@ -5130,7 +5143,7 @@ function databaseDialogCapabilities() {
return {
showCreateDatabaseDialog,
createDatabaseName,
canSetCreateDatabaseCharset: canSetCreateDatabaseCharset.value,
canSetCreateDatabaseCharset: canSetCreateDatabaseCharset.value || canSetCreateDatabaseLocale.value,
createDatabaseCharset,
createDatabaseCharsetOptions,
createDatabaseCharsetLoading,
@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import { supportsCreateDatabaseCharset, supportsCreateDatabaseLocale } from "@/lib/database/createDatabaseSql";
import { DEFAULT_GBASE8S_DATABASE_LOCALE, GBASE8S_DATABASE_LOCALES } from "@/lib/database/createDatabaseCharsetOptions";
describe("GBase 8s / Informix create-database locale support", () => {
it("offers a locale picker only for the gbase8s profile", () => {
expect(supportsCreateDatabaseLocale("gbase", "gbase8s")).toBe(true);
// Plain Informix's agent has no directive handling, so the locale picker must stay hidden
// there until InformixAgent implements it; showing it would silently ignore the choice.
expect(supportsCreateDatabaseLocale("informix", undefined)).toBe(false);
});
it("does not treat GBase 8a or MySQL as locale-picking", () => {
expect(supportsCreateDatabaseLocale("gbase", "gbase8a")).toBe(false);
expect(supportsCreateDatabaseLocale("mysql", "mysql")).toBe(false);
// The MySQL charset path stays independent of the Informix locale path.
expect(supportsCreateDatabaseCharset("mysql", "mysql")).toBe(true);
expect(supportsCreateDatabaseCharset("gbase", "gbase8s")).toBe(false);
});
it("defaults to a Chinese-capable UTF-8 locale", () => {
expect(GBASE8S_DATABASE_LOCALES).toContain("zh_CN.utf8");
expect(DEFAULT_GBASE8S_DATABASE_LOCALE).toBe("zh_CN.utf8");
});
});
@@ -10,6 +10,12 @@ export interface CreateDatabaseCharsetMetadata {
export const CREATE_DATABASE_CHARSET_OPTIONS = ["utf8mb4", "utf8", "gbk", "latin1", "ascii", "big5", "gb2312", "utf16", "utf32"] as const;
// Common GBase 8s / Informix database locales (language_territory.codeset). The default is a
// UTF-8 locale so newly created databases can store Chinese; en_US.819 is a typical Informix
// instance default and cannot hold CJK characters.
export const GBASE8S_DATABASE_LOCALES = ["zh_CN.utf8", "en_US.utf8", "ja_JP.utf8", "ko_KR.utf8", "zh_CN.gbk", "zh_CN.gb18030", "en_US.819"] as const;
export const DEFAULT_GBASE8S_DATABASE_LOCALE = "zh_CN.utf8";
const FALLBACK_COLLATION_OPTIONS: Record<string, string[]> = {
ascii: ["ascii_general_ci", "ascii_bin"],
big5: ["big5_chinese_ci", "big5_bin"],
@@ -18,6 +18,14 @@ export function supportsCreateDatabaseCharset(databaseType?: DatabaseType, drive
return MYSQL_COMPATIBLE_TYPES.has(databaseType as DatabaseType) || (!!driverProfile && MYSQL_COMPATIBLE_PROFILES.has(driverProfile.toLowerCase()));
}
// GBase 8s creates a database whose codeset follows the creating session's DB_LOCALE; there is
// no charset clause in the SQL. The create dialog still offers a locale picker, and the chosen
// value is applied by the gbase8s agent opening the CREATE DATABASE session with that DB_LOCALE.
// Plain Informix is excluded until InformixAgent learns to honor the locale directive.
export function supportsCreateDatabaseLocale(databaseType?: DatabaseType, driverProfile?: string | null): boolean {
return databaseType === "gbase" && driverProfile === "gbase8s";
}
export function buildCreateDatabaseSql(options: CreateDatabaseSqlOptions): Promise<string> {
return api.buildCreateDatabaseSql(options);
}