feat(sql): support absolute paths and basedirectory for sqlite

pull/279/head
FabianLars 2 years ago
parent 92d9f2754c
commit 9c7d233556
No known key found for this signature in database
GPG Key ID: 3B12BC1DEBF61125

@ -1,4 +1,5 @@
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from "@tauri-apps/api/tauri";
import { BaseDirectory } from "@tauri-apps/api/path"
export interface QueryResult { export interface QueryResult {
/** The number of rows affected by the query. */ /** The number of rows affected by the query. */
@ -34,16 +35,20 @@ export default class Database {
* *
* # Sqlite * # Sqlite
* *
* The path is relative to `tauri::api::path::BaseDirectory::App` and must start with `sqlite:`. * The path must be in one of the following formats:
* - `sqlite::memory:`: Open an in-memory database.
* - `sqlite:///data.db`: Open the file `data.db` from the root directory. `sqlite://\\?\` will be automatically converted to `sqlite:///`
* - `sqlite://data.db`: Open the file `data.db` relative to the `dir` argument, or BaseDirectory.App if not provided
* *
* @example * @example
* ```ts * ```ts
* const db = await Database.load("sqlite:test.db"); * const db = await Database.load("sqlite:test.db");
* ``` * ```
*/ */
static async load(path: string): Promise<Database> { static async load(path: string, dir?: BaseDirectory): Promise<Database> {
const _path = await invoke<string>("plugin:sql|load", { const _path = await invoke<string>("plugin:sql|load", {
db: path, db: path,
dir
}); });
return new Database(_path); return new Database(_path);
@ -58,7 +63,10 @@ export default class Database {
* *
* # Sqlite * # Sqlite
* *
* The path is relative to `tauri::api::path::BaseDirectory::App` and must start with `sqlite:`. * The path must be in one of the following formats:
* - `sqlite::memory:`: Open an in-memory database.
* - `sqlite:///data.db`: Open the file `data.db` from the root directory. `sqlite://\\?\` will be automatically converted to `sqlite:///`
* - `sqlite://data.db`: Open the file `data.db` relative to the `dir` argument, or BaseDirectory.App if not provided
* *
* @example * @example
* ```ts * ```ts

@ -22,7 +22,9 @@ use tokio::sync::Mutex;
use std::collections::HashMap; use std::collections::HashMap;
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
use std::{fs::create_dir_all, path::PathBuf}; use std::fs::create_dir_all;
#[cfg(feature = "sqlite")]
use tauri::api::path::BaseDirectory;
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
type Db = sqlx::sqlite::Sqlite; type Db = sqlx::sqlite::Sqlite;
@ -46,6 +48,13 @@ pub enum Error {
DatabaseNotLoaded(String), DatabaseNotLoaded(String),
#[error("unsupported datatype: {0}")] #[error("unsupported datatype: {0}")]
UnsupportedDatatype(String), UnsupportedDatatype(String),
#[cfg(feature = "sqlite")]
#[error(transparent)]
Io(#[from] std::io::Error),
#[cfg(feature = "sqlite")]
#[error(transparent)]
Tauri(#[from] tauri::api::Error),
} }
impl Serialize for Error { impl Serialize for Error {
@ -59,33 +68,48 @@ impl Serialize for Error {
type Result<T> = std::result::Result<T, Error>; type Result<T> = std::result::Result<T, Error>;
#[cfg(feature = "sqlite")]
/// Resolves the App's **file path** from the `AppHandle` context
/// object
fn app_path<R: Runtime>(app: &AppHandle<R>) -> PathBuf {
#[allow(deprecated)] // FIXME: Change to non-deprecated function in Tauri v2
app.path_resolver()
.app_dir()
.expect("No App path was found!")
}
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
/// Maps the user supplied DB connection string to a connection string /// Maps the user supplied DB connection string to a connection string
/// with a fully qualified file path to the App's designed "app_path" /// with a fully qualified file path to the App's designed "app_path"
fn path_mapper(mut app_path: PathBuf, connection_string: &str) -> String { fn path_mapper<R: Runtime>(
app_path.push( app: &AppHandle<R>,
connection_string: &str,
dir: Option<BaseDirectory>,
) -> Result<String> {
use tauri::api::path::resolve_path;
if connection_string.starts_with("sqlite::memory:") {
return Ok(connection_string.to_string());
}
if connection_string.starts_with("sqlite:///")
|| connection_string.starts_with(r"sqlite://\\?\")
{
create_dir_all(
connection_string connection_string
.split_once(':') .replace("sqlite:///", "/")
.expect("Couldn't parse the connection string for DB!") .replace(r"sqlite://\\?\", "")
.1, .replace("?mode=ro", ""),
); )?;
return Ok(connection_string.replace(r"\\?\", "/").replace('\\', "/"));
format!( }
"sqlite:{}",
app_path let connection_string = connection_string
.to_str() .replace("sqlite://", "")
.expect("Problem creating fully qualified path to Database file!") .replace("sqlite:", "");
)
let path = resolve_path(
&app.config(),
app.package_info(),
&app.env(),
connection_string,
#[allow(deprecated)] // FIXME: Use non deprecated variant in tauri v2
dir.or(Some(BaseDirectory::App)),
)?;
Ok(format!(
"sqlite://{}",
path.display().to_string().replace(r"\\?\", "/")
))
} }
#[derive(Default)] #[derive(Default)]
@ -151,15 +175,13 @@ async fn load<R: Runtime>(
db_instances: State<'_, DbInstances>, db_instances: State<'_, DbInstances>,
migrations: State<'_, Migrations>, migrations: State<'_, Migrations>,
db: String, db: String,
dir: Option<BaseDirectory>,
) -> Result<String> { ) -> Result<String> {
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
let fqdb = path_mapper(app_path(&app), &db); let fqdb = path_mapper(&app, &db, dir)?;
#[cfg(not(feature = "sqlite"))] #[cfg(not(feature = "sqlite"))]
let fqdb = db.clone(); let fqdb = db.clone();
#[cfg(feature = "sqlite")]
create_dir_all(app_path(&app)).expect("Problem creating App directory!");
if !Db::database_exists(&fqdb).await.unwrap_or(false) { if !Db::database_exists(&fqdb).await.unwrap_or(false) {
Db::create_database(&fqdb).await?; Db::create_database(&fqdb).await?;
} }
@ -334,15 +356,12 @@ impl Builder {
.setup_with_config(|app, config: Option<PluginConfig>| { .setup_with_config(|app, config: Option<PluginConfig>| {
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
#[cfg(feature = "sqlite")]
create_dir_all(app_path(app)).expect("problems creating App directory!");
tauri::async_runtime::block_on(async move { tauri::async_runtime::block_on(async move {
let instances = DbInstances::default(); let instances = DbInstances::default();
let mut lock = instances.0.lock().await; let mut lock = instances.0.lock().await;
for db in config.preload { for db in config.preload {
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
let fqdb = path_mapper(app_path(app), &db); let fqdb = path_mapper(app, &db, None)?;
#[cfg(not(feature = "sqlite"))] #[cfg(not(feature = "sqlite"))]
let fqdb = db.clone(); let fqdb = db.clone();

Loading…
Cancel
Save