Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,35 +571,50 @@ tx.commit().await?;

### Cross-Database Operations

Attach other databases for cross-database queries:
Attach other databases for cross-database queries. For Rust API usage, you need to load
both databases first, then create `AttachedSpec` instances using their inner database
references:

```rust
use tauri_plugin_sqlite::AttachedDatabaseSpec;
use tauri_plugin_sqlite::{DatabaseWrapper, AttachedSpec, AttachedMode};
use std::sync::Arc;

// Load both databases
let main_db = DatabaseWrapper::load("/path/to/main.db".into(), None).await?;
let stats_db = DatabaseWrapper::load("/path/to/stats.db".into(), None).await?;

// Create attached spec using the inner database reference
let stats_spec = AttachedSpec {
database: Arc::clone(stats_db.inner()),
schema_name: "stats".to_string(),
mode: AttachedMode::ReadWrite,
};

// Simple transaction with attached database
let results = db.execute_transaction(vec![
let results = main_db.execute_transaction(vec![
("INSERT INTO main.orders (user_id) VALUES (?)", vec![json!(1)]),
("UPDATE stats.order_count SET count = count + 1", vec![]),
])
.attach(vec![AttachedDatabaseSpec {
database_path: "stats.db".into(),
schema_name: "stats".into(),
mode: tauri_plugin_sqlite::AttachedDatabaseMode::ReadWrite,
}])
.attach(vec![stats_spec])
.await?;

println!("Cross-database transaction completed: {} statements", results.len());

// Interruptible transaction with attached database
// Load the inventory database
let inventory_db = DatabaseWrapper::load("/path/to/inventory.db".into(), None).await?;

// Create spec for inventory database
let inv_spec = AttachedSpec {
database: Arc::clone(inventory_db.inner()),
schema_name: "inv".to_string(),
mode: AttachedMode::ReadWrite,
};

// Assuming product_id is defined in your application context
let product_id = 789;

let _tx = db.begin_interruptible_transaction()
.attach(vec![AttachedDatabaseSpec {
database_path: "inventory.db".into(),
schema_name: "inv".into(),
mode: tauri_plugin_sqlite::AttachedDatabaseMode::ReadWrite,
}])
let _tx = main_db.begin_interruptible_transaction()
.attach(vec![inv_spec])
.execute(vec![
("UPDATE inv.stock SET quantity = quantity - ? WHERE product_id = ?", vec![json!(1), json!(product_id)]),
])
Expand Down
2 changes: 2 additions & 0 deletions crates/sqlx-sqlite-conn-mgr/migrations/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Placeholder migrations directory for doctest compilation
# sqlx::migrate! requires a migrations directory to exist at compile time
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ mod transactions;
mod wrapper;

pub use error::{Error, Result};
pub use sqlx_sqlite_conn_mgr::Migrator as SqliteMigrator;
pub use sqlx_sqlite_conn_mgr::{
AttachedMode, AttachedSpec, Migrator as SqliteMigrator, SqliteDatabaseConfig,
};
pub use transactions::{ActiveInterruptibleTransactions, ActiveRegularTransactions, Statement};
pub use wrapper::{
DatabaseWrapper, InterruptibleTransaction, InterruptibleTransactionBuilder,
Expand Down
27 changes: 24 additions & 3 deletions src/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,28 @@ pub struct DatabaseWrapper {

impl DatabaseWrapper {
/// Get the inner Arc<SqliteDatabase> for advanced usage
pub(crate) fn inner(&self) -> &Arc<SqliteDatabase> {
///
/// This is useful when you need to create `AttachedSpec` instances for cross-database
/// operations with interruptible transactions.
///
/// # Example
///
/// ```no_run
/// # use tauri_plugin_sqlite::{DatabaseWrapper, AttachedSpec, AttachedMode};
/// # use std::sync::Arc;
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # let db1: DatabaseWrapper = todo!();
/// # let db2: DatabaseWrapper = todo!();
/// // Create an attached spec using the inner database reference
/// let spec = AttachedSpec {
/// database: Arc::clone(db2.inner()),
/// schema_name: "other".to_string(),
/// mode: AttachedMode::ReadOnly,
/// };
/// # Ok(())
/// # }
/// ```
pub fn inner(&self) -> &Arc<SqliteDatabase> {
&self.inner
}

Expand All @@ -50,7 +71,7 @@ impl DatabaseWrapper {
/// # Example
///
/// ```no_run
/// # use tauri_plugin_sqlite::DatabaseWrapper;
/// # use tauri_plugin_sqlite::{DatabaseWrapper, Statement};
/// # use serde_json::json;
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # let db: DatabaseWrapper = todo!();
Expand All @@ -64,7 +85,7 @@ impl DatabaseWrapper {
///
/// // Continue with more work
/// let results = tx.continue_with(vec![
/// crate::transactions::Statement {
/// Statement {
/// query: "INSERT INTO items (name) VALUES (?)".to_string(),
/// values: vec![json!("item1")],
/// }
Expand Down