From 7294f33f6b7ceb9dfbae32713ff9acc94209bfcb Mon Sep 17 00:00:00 2001 From: Michael Ionov Date: Thu, 18 Jan 2024 23:23:14 +0200 Subject: [PATCH] feat: add app db migrations --- src-tauri/Cargo.lock | 33 +++++++++ src-tauri/Cargo.toml | 3 + src-tauri/src/bin/main.rs | 4 +- src-tauri/src/database/init.rs | 27 +++++++ .../migrations/01-initial-schema/up.sql | 10 +++ src-tauri/src/database/mod.rs | 1 + src-tauri/src/database/queries.rs | 71 +------------------ src-tauri/src/utils/fs.rs | 7 +- src-tauri/src/utils/init.rs | 3 +- 9 files changed, 85 insertions(+), 74 deletions(-) create mode 100644 src-tauri/src/database/init.rs create mode 100644 src-tauri/src/database/migrations/01-initial-schema/up.sql diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6004fcb..4b9b72e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2211,6 +2211,25 @@ dependencies = [ "tiff", ] +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2895,6 +2914,8 @@ dependencies = [ "deadpool-sqlite", "env_logger", "futures", + "include_dir", + "lazy_static", "log", "magic-crypt", "md-5 0.10.6", @@ -2902,6 +2923,7 @@ dependencies = [ "postgres", "rand 0.8.5", "rusqlite", + "rusqlite_migration", "serde", "serde_json", "sql_lexer", @@ -3890,6 +3912,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "rusqlite_migration" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4116d1697de2912db0b72069473dfb025f6c332b4a085ed041d121e8d745aea" +dependencies = [ + "include_dir", + "log", + "rusqlite", +] + [[package]] name = "rust_decimal" version = "1.33.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0e625d3..0a36271 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -43,6 +43,9 @@ postgres = { version = "0.19.7", features = ["array-impls", "with-serde_json-1", deadpool-postgres = "0.12.1" deadpool-sqlite = "0.7.0" magic-crypt = "3.1.13" +rusqlite_migration = { version= "1.1.0", features = ["from-directory"] } +include_dir = "0.7.3" +lazy_static = "1.4.0" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/src-tauri/src/bin/main.rs b/src-tauri/src/bin/main.rs index d352f4f..8934f28 100644 --- a/src-tauri/src/bin/main.rs +++ b/src-tauri/src/bin/main.rs @@ -2,8 +2,8 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use chrono; -use log::error; use state::AppState; +use tracing::error; use std::path::PathBuf; use std::{fs, panic}; use tauri::{Manager, State}; @@ -12,7 +12,7 @@ use tokio::sync::Mutex; use tracing_subscriber; use noir::{ - database::queries::initialize_database, + database::init::initialize_database, handlers::{connections, queries}, queues::query::{async_process_model, rs2js}, state::{self, AsyncState}, diff --git a/src-tauri/src/database/init.rs b/src-tauri/src/database/init.rs new file mode 100644 index 0000000..4e32cca --- /dev/null +++ b/src-tauri/src/database/init.rs @@ -0,0 +1,27 @@ +use crate::utils::fs::get_db_path; +use deadpool_sqlite::rusqlite::{Connection as AppConnection, Error}; +use include_dir::{include_dir, Dir}; +use lazy_static::lazy_static; +use rusqlite_migration::Migrations; +use tracing::error; + +static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/database/migrations"); + +// Define migrations. These are applied atomically. +lazy_static! { + static ref MIGRATIONS: Migrations<'static> = + Migrations::from_directory(&MIGRATIONS_DIR).unwrap(); +} + +/// Initializes the database connection, creating the .sqlite file if needed, and upgrading the database +/// if it's out of date. +pub fn initialize_database() -> Result { + let db_path = get_db_path(); + let mut db = AppConnection::open(db_path)?; + + let _ = MIGRATIONS.to_latest(&mut db).map_err(|e| { + error!("Error applying migrations: {:?}", e); + }); + + Ok(db) +} diff --git a/src-tauri/src/database/migrations/01-initial-schema/up.sql b/src-tauri/src/database/migrations/01-initial-schema/up.sql new file mode 100644 index 0000000..4f31b2d --- /dev/null +++ b/src-tauri/src/database/migrations/01-initial-schema/up.sql @@ -0,0 +1,10 @@ +CREATE TABLE connections + ( + id TEXT NOT NULL, + dialect VARCHAR(255) NOT NULL, + mode VARCHAR(255) NOT NULL, + credentials VARCHAR(255) NOT NULL, + SCHEMA VARCHAR(255) NOT NULL, + NAME VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL + ) diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index 84c032e..a27db05 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -1 +1,2 @@ +pub mod init; pub mod queries; diff --git a/src-tauri/src/database/queries.rs b/src-tauri/src/database/queries.rs index b9128bd..3fb1c1f 100644 --- a/src-tauri/src/database/queries.rs +++ b/src-tauri/src/database/queries.rs @@ -1,77 +1,10 @@ -use std::path::PathBuf; - -use crate::utils::{ - crypto::{decrypt_data, encrypt_data, get_app_key}, - fs::get_app_path, -}; +use crate::utils::crypto::{decrypt_data, encrypt_data, get_app_key}; use anyhow::Result; -use deadpool_sqlite::rusqlite::{named_params, Connection as AppConnection, Error}; -use tracing::{error, info}; +use deadpool_sqlite::rusqlite::{named_params, Connection as AppConnection}; use uuid::Uuid; use crate::engine::types::config::{ConnectionConfig, Credentials, Dialect, Mode}; -const CURRENT_DB_VERSION: u32 = 1; - -/// Initializes the database connection, creating the .sqlite file if needed, and upgrading the database -/// if it's out of date. -pub fn initialize_database() -> Result { - let db_path = get_db_path(); - let mut db = AppConnection::open(db_path)?; - - let mut user_pragma = db.prepare("PRAGMA user_version")?; - let existing_user_version: u32 = user_pragma.query_row([], |row| row.get(0))?; - drop(user_pragma); - - upgrade_database_if_needed(&mut db, existing_user_version)?; - - Ok(db) -} - -/// Upgrades the database to the current version. -pub fn upgrade_database_if_needed( - db: &mut AppConnection, - existing_version: u32, -) -> Result<(), Error> { - if existing_version < CURRENT_DB_VERSION { - db.pragma_update(None, "journal_mode", "WAL")?; - let tx = db.transaction()?; - tx.pragma_update(None, "user_version", CURRENT_DB_VERSION)?; - tx.commit()?; - } - Ok(()) -} - -pub fn get_db_path() -> PathBuf { - let app_path = get_app_path(); - PathBuf::from(format!("{}/.app.db", app_path.to_str().unwrap())) -} - -pub fn create_app_db() -> Result<()> { - let db_path = get_db_path(); - info!("Creating app database at {}", db_path.to_str().unwrap()); - let db_path = get_db_path(); - let db = AppConnection::open(db_path)?; - - db.execute( - "create table `connections` ( - `id` TEXT not null, - `dialect` varchar(255) not null, - `mode` varchar(255) not null, - `credentials` varchar(255) not null, - `schema` varchar(255) not null, - `name` varchar(255) not null, - `color` varchar(255) not null - )", - [], - )?; - match db.close() { - Ok(_) => info!("Successfully created app database"), - Err(e) => error!("Failed to create app database: {:?}", e), - } - Ok(()) -} - pub fn add_connection(db: &AppConnection, conn: &ConnectionConfig) -> Result<()> { let mut statement = db.prepare( "INSERT INTO connections ( diff --git a/src-tauri/src/utils/fs.rs b/src-tauri/src/utils/fs.rs index aedb126..2e86c16 100644 --- a/src-tauri/src/utils/fs.rs +++ b/src-tauri/src/utils/fs.rs @@ -1,4 +1,4 @@ -use crate::{database::queries::get_db_path, engine::types::result::ResultSet}; +use crate::engine::types::result::ResultSet; use anyhow::Result; use fs::metadata; use serde_json::json; @@ -8,6 +8,11 @@ use tracing::error; use rand::{distributions::Alphanumeric, Rng}; +pub fn get_db_path() -> PathBuf { + let app_path = get_app_path(); + PathBuf::from(format!("{}/.app.db", app_path.to_str().unwrap())) +} + fn get_key_path() -> PathBuf { PathBuf::from(format!("{}/._", get_app_path().to_str().unwrap())) } diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index a87ce81..41a7bde 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -1,7 +1,7 @@ use anyhow::Result; use tracing::debug; -use crate::{database::queries::create_app_db, utils::fs::create_app_key}; +use crate::utils::fs::create_app_key; use super::fs::{create_app_dir, is_appdir_populated}; @@ -10,7 +10,6 @@ pub fn init_app() -> Result<()> { debug!("appdir is not populated"); create_app_dir()?; create_app_key()?; - create_app_db()?; } Ok(()) }