From 558b9301a249abd51cb6d6b985dfc255b59e9613 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 16 Jun 2026 13:55:44 +0800 Subject: [PATCH] feat(sqlite): create empty database file on connect if missing --- .../connection/ConnectionDialog.vue | 26 +++++++++++++++++++ apps/desktop/src/i18n/locales/en.ts | 1 + apps/desktop/src/i18n/locales/es.ts | 1 + apps/desktop/src/i18n/locales/it.ts | 1 + apps/desktop/src/i18n/locales/ja.ts | 1 + apps/desktop/src/i18n/locales/pt-BR.ts | 1 + apps/desktop/src/i18n/locales/zh-CN.ts | 1 + apps/desktop/src/i18n/locales/zh-TW.ts | 1 + crates/dbx-core/src/connection.rs | 6 ++++- crates/dbx-core/src/db/sqlite.rs | 7 +++++ src-tauri/src/commands/connection.rs | 10 +++++-- 11 files changed, 53 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index 732a716bf8..e2939582da 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -2000,6 +2000,24 @@ async function createDuckDbFilePath() { form.value.host = path; } +function ensureSqliteFileExtension(path: string): string { + return /\.(db|sqlite|sqlite3)$/i.test(path) ? path : `${path}.db`; +} + +async function createSqliteFilePath() { + if (!isTauriRuntime()) return; + const { save } = await import("@tauri-apps/plugin-dialog"); + const selected = await save({ + title: t("connection.createSqliteFile"), + defaultPath: "database.db", + filters: [{ name: "SQLite", extensions: ["db", "sqlite", "sqlite3"] }], + }); + if (!selected) return; + + const path = ensureSqliteFileExtension(selected); + form.value.host = path; +} + async function browseJdbcDriverPaths() { if (!isTauriRuntime()) return; const { open } = await import("@tauri-apps/plugin-dialog"); @@ -2371,6 +2389,14 @@ function openExternalUrl(url: string) { {{ t("connection.createDuckDbFile") }} + + + + + {{ t("connection.createSqliteFile") }} +

{{ t("connection.memoryDatabasePathHint") }} diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 463d691f8c..de2a1f6435 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -144,6 +144,7 @@ h2TcpMode: "TCP", h2FilePathRequired: "H2 file path cannot be empty", createDuckDbFile: "Create DuckDB file", + createSqliteFile: "Create SQLite database", memoryDatabasePathHint: "Use :memory: to create an in-memory SQLite or DuckDB database.", sqliteExtensions: "SQLite Extensions", sqliteExtensionsPlaceholder: "/path/to/regexp.dylib\n/path/to/text.dylib|sqlite3_text_init", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index 081c625f83..f476ac7db7 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -142,6 +142,7 @@ h2TcpMode: "TCP", h2FilePathRequired: "La ruta del archivo H2 no puede estar vacía", createDuckDbFile: "Crear archivo DuckDB", + createSqliteFile: "Crear base de datos SQLite", user: "Usuario", password: "Contraseña", database: "Base de datos", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 5997a1b3c0..8d8049deb1 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -142,6 +142,7 @@ h2TcpMode: "TCP", h2FilePathRequired: "Il percorso del file H2 non può essere vuoto", createDuckDbFile: "Crea file DuckDB", + createSqliteFile: "Crea database SQLite", memoryDatabasePathHint: "Usa :memory: per creare un database SQLite o DuckDB in memoria.", sqliteExtensions: "Estensioni SQLite", sqliteExtensionsPlaceholder: "/percorso/per/regexp.dylib\n/percorso/per/text.dylib|sqlite3_text_init", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index e0ad7b6f73..836372a03e 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -143,6 +143,7 @@ export default { h2TcpMode: "TCP", h2FilePathRequired: "H2ファイルパスを指定してください", createDuckDbFile: "DuckDBファイルを作成", + createSqliteFile: "SQLiteデータベースを作成", memoryDatabasePathHint: "インメモリのSQLiteまたはDuckDBデータベースを作成するには :memory: を使用してください。", sqliteExtensions: "SQLite拡張", sqliteExtensionsPlaceholder: "/path/to/regexp.dylib\n/path/to/text.dylib|sqlite3_text_init", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index bf2ccfd199..d30cbe2f6a 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -142,6 +142,7 @@ h2TcpMode: "TCP", h2FilePathRequired: "O caminho do arquivo H2 não pode ficar vazio", createDuckDbFile: "Criar arquivo DuckDB", + createSqliteFile: "Criar banco de dados SQLite", memoryDatabasePathHint: "Use :memory: para criar um banco de dados SQLite ou DuckDB na memória.", sqliteExtensions: "Extensões SQLite", sqliteExtensionsPlaceholder: "/caminho/para/regexp.dylib\n/caminho/para/text.dylib|sqlite3_text_init", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index f3aa9c8f7a..e1e99f5147 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -145,6 +145,7 @@ h2TcpMode: "TCP", h2FilePathRequired: "H2 文件路径不能为空", createDuckDbFile: "新建 DuckDB 文件", + createSqliteFile: "新建 SQLite 数据库", memoryDatabasePathHint: "输入 :memory: 可创建 SQLite 或 DuckDB 内存数据库。", sqliteExtensions: "SQLite 扩展库", sqliteExtensionsPlaceholder: "/path/to/regexp.dylib\n/path/to/text.dylib|sqlite3_text_init", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 48e0afd4c4..d2f322be67 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -142,6 +142,7 @@ h2TcpMode: "TCP", h2FilePathRequired: "H2 檔案路徑不能為空", createDuckDbFile: "建立 DuckDB 檔案", + createSqliteFile: "建立 SQLite 資料庫", memoryDatabasePathHint: "輸入 :memory: 可建立 SQLite 或 DuckDB 記憶體資料庫。", sqliteExtensions: "SQLite 擴充庫", sqliteExtensionsPlaceholder: "/path/to/regexp.dylib\n/path/to/text.dylib|sqlite3_text_init", diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index ec64f8933f..ab97c6d1f3 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -447,7 +447,11 @@ impl AppState { }) .collect(); PoolKind::Sqlite( - db::sqlite::connect_path_with_extensions(&expand_tilde(&db_config.host), extensions).await?, + db::sqlite::connect_path_create_if_missing_with_extensions( + &expand_tilde(&db_config.host), + extensions, + ) + .await?, ) } DatabaseType::Rqlite => { diff --git a/crates/dbx-core/src/db/sqlite.rs b/crates/dbx-core/src/db/sqlite.rs index 6c05283533..500d2e461d 100644 --- a/crates/dbx-core/src/db/sqlite.rs +++ b/crates/dbx-core/src/db/sqlite.rs @@ -46,6 +46,13 @@ pub async fn connect_path_create_if_missing(path: &str) -> Result, +) -> Result { + connect_path_with_options(path, true, extensions).await +} + async fn connect_path_with_options( path: &str, create_if_missing: bool, diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index 6bd5b6efca..ca5ac89522 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -320,7 +320,12 @@ pub async fn test_connection(state: State<'_, Arc>, config: Connection extension }) .collect(); - match db::sqlite::connect_path_with_extensions(&expand_tilde(&config.host), extensions).await { + match db::sqlite::connect_path_create_if_missing_with_extensions( + &expand_tilde(&config.host), + extensions, + ) + .await + { Ok(_) => Ok("Connection successful".to_string()), Err(e) => Err(e), } @@ -539,7 +544,8 @@ pub async fn connect_db(state: State<'_, Arc>, config: ConnectionConfi }) .collect(); PoolKind::Sqlite( - db::sqlite::connect_path_with_extensions(&expand_tilde(&db_config.host), extensions).await?, + db::sqlite::connect_path_create_if_missing_with_extensions(&expand_tilde(&db_config.host), extensions) + .await?, ) } DatabaseType::Redis => {