use r2d2 pool, update rusqlite version

This commit is contained in:
yggverse 2025-03-14 12:27:09 +02:00
parent c3f63dfbdc
commit 33369e31ea
17 changed files with 171 additions and 197 deletions

View file

@ -22,7 +22,7 @@ features = ["gnome_46"]
[dependencies.sqlite] [dependencies.sqlite]
package = "rusqlite" package = "rusqlite"
version = "0.32.1" version = "0.34.0"
[dependencies.sourceview] [dependencies.sourceview]
package = "sourceview5" package = "sourceview5"
@ -38,6 +38,8 @@ itertools = "0.14.0"
libspelling = "0.3.0" libspelling = "0.3.0"
openssl = "0.10.70" openssl = "0.10.70"
plurify = "0.2.0" plurify = "0.2.0"
r2d2 = "0.8.10"
r2d2_sqlite = "0.27.0"
syntect = "5.2.0" syntect = "5.2.0"
# development # development

View file

@ -42,7 +42,7 @@ impl App {
let profile = profile.clone(); let profile = profile.clone();
move |this| { move |this| {
// Init readable connection // Init readable connection
match profile.database.connection.read() { match profile.database.pool.get() {
Ok(connection) => { Ok(connection) => {
// Create transaction // Create transaction
match connection.unchecked_transaction() { match connection.unchecked_transaction() {
@ -77,7 +77,7 @@ impl App {
let profile = profile.clone(); let profile = profile.clone();
move |_| { move |_| {
match profile.save() { match profile.save() {
Ok(_) => match profile.database.connection.write() { Ok(_) => match profile.database.pool.get() {
Ok(mut connection) => { Ok(mut connection) => {
// Create transaction // Create transaction
match connection.transaction() { match connection.transaction() {
@ -266,7 +266,7 @@ impl App {
pub fn run(&self) -> Result<ExitCode> { pub fn run(&self) -> Result<ExitCode> {
// Begin database migration @TODO // Begin database migration @TODO
{ {
let mut connection = self.profile.database.connection.write().unwrap(); let mut connection = self.profile.database.pool.get()?;
let transaction = connection.transaction()?; let transaction = connection.transaction()?;
migrate(&transaction)?; migrate(&transaction)?;
transaction.commit()?; transaction.commit()?;

View file

@ -46,7 +46,7 @@ impl Save {
button.set_sensitive(false); button.set_sensitive(false);
// Create PEM file based on option ID selected // Create PEM file based on option ID selected
match Certificate::new(profile.clone(), profile_identity_id) { match Certificate::build(profile.clone(), profile_identity_id) {
Ok(certificate) => { Ok(certificate) => {
// Init file filters related with PEM extension // Init file filters related with PEM extension
let filters = ListStore::new::<FileFilter>(); let filters = ListStore::new::<FileFilter>();

View file

@ -1,5 +1,4 @@
mod error; use anyhow::{bail, Result};
pub use error::Error;
use crate::profile::Profile; use crate::profile::Profile;
use gtk::{gio::TlsCertificate, prelude::TlsCertificateExt}; use gtk::{gio::TlsCertificate, prelude::TlsCertificateExt};
@ -15,19 +14,17 @@ impl Certificate {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn new(profile: Rc<Profile>, profile_identity_id: i64) -> Result<Self, Error> { pub fn build(profile: Rc<Profile>, profile_identity_id: i64) -> Result<Self> {
match profile.identity.database.record(profile_identity_id) { let record = profile.identity.database.record(profile_identity_id)?;
Ok(record) => match record { match record {
Some(identity) => match TlsCertificate::from_pem(&identity.pem) { Some(identity) => Ok(Self {
Ok(certificate) => Ok(Self { name: TlsCertificate::from_pem(&identity.pem)?
data: identity.pem, .subject_name()
name: certificate.subject_name().unwrap().replace("CN=", ""), .unwrap_or_default()
}), .replace("CN=", ""),
Err(e) => Err(Error::TlsCertificate(e)), data: identity.pem,
}, }),
None => Err(Error::NotFound(profile_identity_id)), None => bail!("Identity not found!"),
},
Err(e) => Err(Error::Database(e)),
} }
} }
} }

View file

@ -1,25 +0,0 @@
use gtk::glib;
use std::fmt::{Display, Formatter, Result};
#[derive(Debug)]
pub enum Error {
Database(sqlite::Error),
NotFound(i64),
TlsCertificate(glib::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
Self::Database(e) => {
write!(f, "Database error: {e}")
}
Self::NotFound(profile_identity_id) => {
write!(f, "Record for `{profile_identity_id}` not found")
}
Self::TlsCertificate(e) => {
write!(f, "TLS certificate error: {e}")
}
}
}
}

View file

@ -4,16 +4,17 @@ mod history;
mod identity; mod identity;
mod search; mod search;
use anyhow::Result;
use bookmark::Bookmark; use bookmark::Bookmark;
use database::Database; use database::Database;
use gtk::glib::{user_config_dir, DateTime};
use history::History; use history::History;
use identity::Identity; use identity::Identity;
use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
use search::Search; use search::Search;
use sqlite::Transaction;
use anyhow::Result; use std::{fs::create_dir_all, path::PathBuf};
use gtk::glib::{user_config_dir, DateTime};
use sqlite::{Connection, Transaction};
use std::{fs::create_dir_all, path::PathBuf, rc::Rc, sync::RwLock};
const VENDOR: &str = "YGGverse"; const VENDOR: &str = "YGGverse";
const APP_ID: &str = "Yoda"; const APP_ID: &str = "Yoda";
@ -53,23 +54,24 @@ impl Profile {
database_path.push(DB_NAME); database_path.push(DB_NAME);
// Init database connection // Init database connection
let connection = Rc::new(RwLock::new(Connection::open(database_path.as_path())?)); let database_pool =
Pool::new(SqliteConnectionManager::file(database_path.as_path())).unwrap();
// Init profile components // Init profile components
{ {
// Init writable connection // Init writable connection
let mut connection = connection.write().unwrap(); // @TODO handle let mut connection = database_pool.get()?;
// Init new transaction // Init new transaction
let transaction = connection.transaction()?; let tx = connection.transaction()?;
// Begin migration // Begin migration
migrate(&transaction)?; migrate(&tx)?;
transaction.commit()?; tx.commit()?;
} // unlock database } // unlock database
// Init model // Init model
let database = Database::build(&connection); let database = Database::build(&database_pool);
// Get active profile or create new one // Get active profile or create new one
let profile_id = match database.active()? { let profile_id = match database.active()? {
@ -78,10 +80,10 @@ impl Profile {
}; };
// Init components // Init components
let bookmark = Bookmark::build(&connection, profile_id)?; let bookmark = Bookmark::build(&database_pool, profile_id)?;
let history = History::build(&connection, profile_id)?; let history = History::build(&database_pool, profile_id)?;
let search = Search::build(&connection, profile_id)?; let search = Search::build(&database_pool, profile_id)?;
let identity = Identity::build(&connection, profile_id)?; let identity = Identity::build(&database_pool, profile_id)?;
// Result // Result
Ok(Self { Ok(Self {

View file

@ -7,11 +7,10 @@ use database::Database;
use gtk::glib::DateTime; use gtk::glib::DateTime;
use item::Item; use item::Item;
use memory::Memory; use memory::Memory;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{ use r2d2_sqlite::SqliteConnectionManager;
rc::Rc, use sqlite::Transaction;
sync::{Arc, RwLock}, use std::sync::{Arc, RwLock};
};
pub struct Bookmark { pub struct Bookmark {
database: Database, // permanent storage database: Database, // permanent storage
@ -22,9 +21,9 @@ impl Bookmark {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Result<Self> { pub fn build(database_pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Result<Self> {
// Init children components // Init children components
let database = Database::new(connection, profile_id); let database = Database::new(database_pool, profile_id);
let memory = Arc::new(RwLock::new(Memory::new())); let memory = Arc::new(RwLock::new(Memory::new()));
// Build initial index // Build initial index

View file

@ -1,11 +1,12 @@
use super::Item; use super::Item;
use anyhow::Result; use anyhow::Result;
use gtk::glib::DateTime; use gtk::glib::DateTime;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
pub struct Database { pub struct Database {
connection: Rc<RwLock<Connection>>, pool: Pool<SqliteConnectionManager>,
profile_id: i64, profile_id: i64,
} }
@ -13,9 +14,9 @@ impl Database {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn new(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Self { pub fn new(pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Self {
Self { Self {
connection: connection.clone(), pool: pool.clone(),
profile_id, profile_id,
} }
} }
@ -24,9 +25,12 @@ impl Database {
/// Get bookmark records from database with optional filter by `request` /// Get bookmark records from database with optional filter by `request`
pub fn records(&self, request: Option<&str>, title: Option<&str>) -> Result<Vec<Item>> { pub fn records(&self, request: Option<&str>, title: Option<&str>) -> Result<Vec<Item>> {
let readable = self.connection.read().unwrap(); // @TODO select(
let tx = readable.unchecked_transaction()?; &self.pool.get()?.unchecked_transaction()?,
select(&tx, self.profile_id, request, title) self.profile_id,
request,
title,
)
} }
// Setters // Setters
@ -34,8 +38,8 @@ impl Database {
/// Create new bookmark record in database /// Create new bookmark record in database
/// * return last insert ID on success /// * return last insert ID on success
pub fn add(&self, time: DateTime, request: &str, title: Option<&str>) -> Result<i64> { pub fn add(&self, time: DateTime, request: &str, title: Option<&str>) -> Result<i64> {
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
let id = insert(&tx, self.profile_id, time, request, title)?; let id = insert(&tx, self.profile_id, time, request, title)?;
tx.commit()?; tx.commit()?;
Ok(id) Ok(id)
@ -43,8 +47,8 @@ impl Database {
/// Delete bookmark record from database /// Delete bookmark record from database
pub fn delete(&self, id: i64) -> Result<usize> { pub fn delete(&self, id: i64) -> Result<usize> {
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
let usize = delete(&tx, id)?; let usize = delete(&tx, id)?;
tx.commit()?; tx.commit()?;
Ok(usize) Ok(usize)

View file

@ -1,7 +1,8 @@
use anyhow::Result; use anyhow::Result;
use gtk::glib::DateTime; use gtk::glib::DateTime;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
pub struct Table { pub struct Table {
pub id: i64, pub id: i64,
@ -11,26 +12,22 @@ pub struct Table {
} }
pub struct Database { pub struct Database {
pub connection: Rc<RwLock<Connection>>, pub pool: Pool<SqliteConnectionManager>,
} }
impl Database { impl Database {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>) -> Self { pub fn build(pool: &Pool<SqliteConnectionManager>) -> Self {
Self { Self { pool: pool.clone() }
connection: connection.clone(),
}
} }
// Getters // Getters
/// Get all records /// Get all records
pub fn records(&self) -> Result<Vec<Table>> { pub fn records(&self) -> Result<Vec<Table>> {
let readable = self.connection.read().unwrap(); select(&self.pool.get()?.unchecked_transaction()?)
let tx = readable.unchecked_transaction()?;
select(&tx)
} }
/// Get active profile record if exist /// Get active profile record if exist
@ -43,8 +40,8 @@ impl Database {
/// Create new record in `Self` database connected /// Create new record in `Self` database connected
pub fn add(&self, is_active: bool, time: DateTime, name: Option<String>) -> Result<i64> { pub fn add(&self, is_active: bool, time: DateTime, name: Option<String>) -> Result<i64> {
let mut writable = self.connection.write().unwrap(); let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
if is_active { if is_active {
for record in select(&tx)? { for record in select(&tx)? {
update(&tx, record.id, false, record.time, record.name)?; update(&tx, record.id, false, record.time, record.name)?;

View file

@ -7,11 +7,10 @@ use database::Database;
use gtk::glib::GString; use gtk::glib::GString;
use item::{Event, Item}; use item::{Event, Item};
use memory::Memory; use memory::Memory;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{ use r2d2_sqlite::SqliteConnectionManager;
rc::Rc, use sqlite::Transaction;
sync::{Arc, RwLock}, use std::sync::{Arc, RwLock};
};
pub struct History { pub struct History {
database: Database, // permanent storage database: Database, // permanent storage
@ -22,9 +21,9 @@ impl History {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Result<Self> { pub fn build(database_pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Result<Self> {
// Init children components // Init children components
let database = Database::build(connection, profile_id); let database = Database::build(database_pool, profile_id);
let memory = Arc::new(RwLock::new(Memory::new())); let memory = Arc::new(RwLock::new(Memory::new()));
for item in database.records(None, None)? { for item in database.records(None, None)? {

View file

@ -1,11 +1,12 @@
use super::{item::Event, Item}; use super::{item::Event, Item};
use anyhow::Result; use anyhow::Result;
use gtk::glib::DateTime; use gtk::glib::DateTime;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
pub struct Database { pub struct Database {
connection: Rc<RwLock<Connection>>, pool: Pool<SqliteConnectionManager>,
profile_id: i64, profile_id: i64,
} }
@ -13,9 +14,9 @@ impl Database {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Self { pub fn build(pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Self {
Self { Self {
connection: connection.clone(), pool: pool.clone(),
profile_id, profile_id,
} }
} }
@ -24,9 +25,12 @@ impl Database {
/// Get history records from database with optional filter by `request` /// Get history records from database with optional filter by `request`
pub fn records(&self, request: Option<&str>, title: Option<&str>) -> Result<Vec<Item>> { pub fn records(&self, request: Option<&str>, title: Option<&str>) -> Result<Vec<Item>> {
let readable = self.connection.read().unwrap(); // @TODO select(
let tx = readable.unchecked_transaction()?; &self.pool.get()?.unchecked_transaction()?,
select(&tx, self.profile_id, request, title) self.profile_id,
request,
title,
)
} }
// Actions // Actions
@ -34,16 +38,16 @@ impl Database {
/// Create new history record in database /// Create new history record in database
/// * return last insert ID on success /// * return last insert ID on success
pub fn add(&self, item: &Item) -> Result<i64> { pub fn add(&self, item: &Item) -> Result<i64> {
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
let id = insert(&tx, self.profile_id, item)?; let id = insert(&tx, self.profile_id, item)?;
tx.commit()?; tx.commit()?;
Ok(id) Ok(id)
} }
pub fn update(&self, item: &Item) -> Result<usize> { pub fn update(&self, item: &Item) -> Result<usize> {
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
let affected = update(&tx, self.profile_id, item)?; let affected = update(&tx, self.profile_id, item)?;
tx.commit()?; tx.commit()?;
Ok(affected) Ok(affected)

View file

@ -10,8 +10,9 @@ use database::Database;
use gtk::glib::DateTime; use gtk::glib::DateTime;
use item::Item; use item::Item;
use memory::Memory; use memory::Memory;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
/// Authorization wrapper for Gemini protocol /// Authorization wrapper for Gemini protocol
/// ///
@ -26,10 +27,13 @@ impl Identity {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>, profile_identity_id: i64) -> Result<Self> { pub fn build(
database_pool: &Pool<SqliteConnectionManager>,
profile_identity_id: i64,
) -> Result<Self> {
// Init components // Init components
let auth = Auth::build(connection)?; let auth = Auth::build(database_pool)?;
let database = Database::build(connection, profile_identity_id); let database = Database::build(database_pool, profile_identity_id);
let memory = Memory::new(); let memory = Memory::new();
// Init `Self` // Init `Self`

View file

@ -6,24 +6,25 @@ mod memory;
use anyhow::Result; use anyhow::Result;
use database::Database; use database::Database;
use memory::Memory; use memory::Memory;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
/// Auth pair operations /// Auth pair operations
pub struct Auth { pub struct Auth {
database: Rc<Database>, database: Database,
memory: Rc<Memory>, memory: Memory,
} }
impl Auth { impl Auth {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>) -> Result<Self> { pub fn build(database_pool: &Pool<SqliteConnectionManager>) -> Result<Self> {
// Init `Self` // Init `Self`
let this = Self { let this = Self {
database: Rc::new(Database::build(connection)), database: Database::build(database_pool),
memory: Rc::new(Memory::new()), memory: Memory::new(),
}; };
// Build initial index // Build initial index

View file

@ -1,6 +1,7 @@
use anyhow::Result; use anyhow::Result;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
pub struct Table { pub struct Table {
pub id: i64, pub id: i64,
@ -10,25 +11,23 @@ pub struct Table {
/// Storage for `profile_identity_id` + `scope` auth pairs /// Storage for `profile_identity_id` + `scope` auth pairs
pub struct Database { pub struct Database {
connection: Rc<RwLock<Connection>>, pool: Pool<SqliteConnectionManager>,
} }
impl Database { impl Database {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>) -> Self { pub fn build(pool: &Pool<SqliteConnectionManager>) -> Self {
Self { Self { pool: pool.clone() }
connection: connection.clone(),
}
} }
// Actions // Actions
/// Create new record in database /// Create new record in database
pub fn add(&self, profile_identity_id: i64, scope: &str) -> Result<i64> { pub fn add(&self, profile_identity_id: i64, scope: &str) -> Result<i64> {
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
let id = insert(&tx, profile_identity_id, scope)?; let id = insert(&tx, profile_identity_id, scope)?;
tx.commit()?; tx.commit()?;
Ok(id) Ok(id)
@ -36,8 +35,8 @@ impl Database {
/// Delete record with given `id` from database /// Delete record with given `id` from database
pub fn delete(&self, id: i64) -> Result<()> { pub fn delete(&self, id: i64) -> Result<()> {
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
delete(&tx, id)?; delete(&tx, id)?;
tx.commit()?; tx.commit()?;
Ok(()) Ok(())
@ -47,16 +46,15 @@ impl Database {
/// Get records from database match current `profile_id` optionally filtered by `scope` /// Get records from database match current `profile_id` optionally filtered by `scope`
pub fn records_scope(&self, scope: Option<&str>) -> Result<Vec<Table>> { pub fn records_scope(&self, scope: Option<&str>) -> Result<Vec<Table>> {
let readable = self.connection.read().unwrap(); // @TODO select_scope(&self.pool.get()?.unchecked_transaction()?, scope)
let tx = readable.unchecked_transaction()?;
select_scope(&tx, scope)
} }
/// Get records from database match current `profile_id` optionally filtered by `scope` /// Get records from database match current `profile_id` optionally filtered by `scope`
pub fn records_ref(&self, profile_identity_id: i64) -> Result<Vec<Table>> { pub fn records_ref(&self, profile_identity_id: i64) -> Result<Vec<Table>> {
let readable = self.connection.read().unwrap(); // @TODO select_ref(
let tx = readable.unchecked_transaction()?; &self.pool.get()?.unchecked_transaction()?,
select_ref(&tx, profile_identity_id) profile_identity_id,
)
} }
} }

View file

@ -1,5 +1,7 @@
use sqlite::{Connection, Error, Transaction}; use anyhow::Result;
use std::{rc::Rc, sync::RwLock}; use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
pub struct Table { pub struct Table {
pub id: i64, pub id: i64,
@ -9,7 +11,7 @@ pub struct Table {
/// Storage for Gemini auth certificates /// Storage for Gemini auth certificates
pub struct Database { pub struct Database {
connection: Rc<RwLock<Connection>>, pool: Pool<SqliteConnectionManager>,
profile_id: i64, profile_id: i64,
} }
@ -17,9 +19,9 @@ impl Database {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Self { pub fn build(pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Self {
Self { Self {
connection: connection.clone(), pool: pool.clone(),
profile_id, profile_id,
} }
} }
@ -27,10 +29,10 @@ impl Database {
// Actions // Actions
/// Create new record in database /// Create new record in database
pub fn add(&self, pem: &str) -> Result<i64, Error> { pub fn add(&self, pem: &str) -> Result<i64> {
// Begin new transaction // Begin new transaction
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
// Create new record // Create new record
insert(&tx, self.profile_id, pem)?; insert(&tx, self.profile_id, pem)?;
@ -39,55 +41,45 @@ impl Database {
let id = last_insert_id(&tx); let id = last_insert_id(&tx);
// Done // Done
match tx.commit() { tx.commit()?;
Ok(_) => Ok(id), Ok(id)
Err(e) => Err(e),
}
} }
/// Delete record with given `id` from database /// Delete record with given `id` from database
pub fn delete(&self, id: i64) -> Result<(), Error> { pub fn delete(&self, id: i64) -> Result<()> {
// Begin new transaction // Begin new transaction
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
// Create new record // Create new record
delete(&tx, id)?; delete(&tx, id)?;
// Done // Done
match tx.commit() { tx.commit()?;
Ok(_) => Ok(()), Ok(())
Err(e) => Err(e),
}
} }
/// Get single record match `id` /// Get single record match `id`
pub fn record(&self, id: i64) -> Result<Option<Table>, Error> { pub fn record(&self, id: i64) -> Result<Option<Table>> {
let readable = self.connection.read().unwrap(); let records = select(&self.pool.get()?.unchecked_transaction()?, self.profile_id)?; // @TODO single record query
let tx = readable.unchecked_transaction()?;
let records = select(&tx, self.profile_id)?; // @TODO single record query
for record in records { for record in records {
if record.id == id { if record.id == id {
return Ok(Some(record)); return Ok(Some(record));
} }
} }
Ok(None) Ok(None)
} }
/// Get all records match current `profile_id` /// Get all records match current `profile_id`
pub fn records(&self) -> Result<Vec<Table>, Error> { pub fn records(&self) -> Result<Vec<Table>> {
let readable = self.connection.read().unwrap(); // @TODO select(&self.pool.get()?.unchecked_transaction()?, self.profile_id)
let tx = readable.unchecked_transaction()?;
select(&tx, self.profile_id)
} }
} }
// Low-level DB API // Low-level DB API
pub fn init(tx: &Transaction) -> Result<usize, Error> { pub fn init(tx: &Transaction) -> Result<usize> {
tx.execute( Ok(tx.execute(
"CREATE TABLE IF NOT EXISTS `profile_identity` "CREATE TABLE IF NOT EXISTS `profile_identity`
( (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
@ -97,24 +89,24 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
FOREIGN KEY (`profile_id`) REFERENCES `profile`(`id`) FOREIGN KEY (`profile_id`) REFERENCES `profile`(`id`)
)", )",
[], [],
) )?)
} }
pub fn insert(tx: &Transaction, profile_id: i64, pem: &str) -> Result<usize, Error> { pub fn insert(tx: &Transaction, profile_id: i64, pem: &str) -> Result<usize> {
tx.execute( Ok(tx.execute(
"INSERT INTO `profile_identity` ( "INSERT INTO `profile_identity` (
`profile_id`, `profile_id`,
`pem` `pem`
) VALUES (?, ?)", ) VALUES (?, ?)",
(profile_id, pem), (profile_id, pem),
) )?)
} }
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> { pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
tx.execute("DELETE FROM `profile_identity` WHERE `id` = ?", [id]) Ok(tx.execute("DELETE FROM `profile_identity` WHERE `id` = ?", [id])?)
} }
pub fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Table>, Error> { pub fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Table>> {
let mut stmt = tx.prepare( let mut stmt = tx.prepare(
"SELECT `id`, "SELECT `id`,
`profile_id`, `profile_id`,

View file

@ -5,8 +5,9 @@ use anyhow::Result;
use database::Database; use database::Database;
use gtk::glib::Uri; use gtk::glib::Uri;
use memory::Memory; use memory::Memory;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
pub struct Search { pub struct Search {
database: Database, // permanent storage database: Database, // permanent storage
@ -17,8 +18,8 @@ impl Search {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Result<Self> { pub fn build(database_pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Result<Self> {
let database = Database::init(connection, profile_id)?; let database = Database::init(database_pool, profile_id)?;
// Init fast search index // Init fast search index
let memory = Memory::init(); let memory = Memory::init();
// Build initial index // Build initial index

View file

@ -1,6 +1,7 @@
use anyhow::Result; use anyhow::Result;
use sqlite::{Connection, Transaction}; use r2d2::Pool;
use std::{rc::Rc, sync::RwLock}; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
#[derive(Clone)] #[derive(Clone)]
pub struct Row { pub struct Row {
@ -11,7 +12,7 @@ pub struct Row {
} }
pub struct Database { pub struct Database {
connection: Rc<RwLock<Connection>>, pool: Pool<SqliteConnectionManager>,
profile_id: i64, profile_id: i64,
} }
@ -19,9 +20,9 @@ impl Database {
// Constructors // Constructors
/// Create new `Self` /// Create new `Self`
pub fn init(connection: &Rc<RwLock<Connection>>, profile_id: i64) -> Result<Self> { pub fn init(pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Result<Self> {
let mut writable = connection.write().unwrap(); // @TODO handle let mut connection = pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
let records = select(&tx, profile_id)?; let records = select(&tx, profile_id)?;
@ -31,7 +32,7 @@ impl Database {
} }
Ok(Self { Ok(Self {
connection: connection.clone(), pool: pool.clone(),
profile_id, profile_id,
}) })
} }
@ -40,9 +41,7 @@ impl Database {
/// Get records from database /// Get records from database
pub fn records(&self) -> Result<Vec<Row>> { pub fn records(&self) -> Result<Vec<Row>> {
let readable = self.connection.read().unwrap(); // @TODO handle select(&self.pool.get()?.unchecked_transaction()?, self.profile_id)
let tx = readable.unchecked_transaction()?;
select(&tx, self.profile_id)
} }
// Setters // Setters
@ -50,8 +49,8 @@ impl Database {
/// Create new record in database /// Create new record in database
/// * return last insert ID on success /// * return last insert ID on success
pub fn add(&self, query: String, is_default: bool) -> Result<i64> { pub fn add(&self, query: String, is_default: bool) -> Result<i64> {
let mut writable = self.connection.write().unwrap(); // @TODO handle let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
if is_default { if is_default {
reset(&tx, self.profile_id, !is_default)?; reset(&tx, self.profile_id, !is_default)?;
} }
@ -63,8 +62,8 @@ impl Database {
/// Delete record from database /// Delete record from database
pub fn delete(&self, id: i64) -> Result<()> { pub fn delete(&self, id: i64) -> Result<()> {
// Begin new transaction // Begin new transaction
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
// Delete record by ID // Delete record by ID
delete(&tx, id)?; delete(&tx, id)?;
@ -97,8 +96,8 @@ impl Database {
/// Delete record from database /// Delete record from database
pub fn set_default(&self, id: i64) -> Result<()> { pub fn set_default(&self, id: i64) -> Result<()> {
// Begin new transaction // Begin new transaction
let mut writable = self.connection.write().unwrap(); // @TODO let mut connection = self.pool.get()?;
let tx = writable.transaction()?; let tx = connection.transaction()?;
// Make sure only one default provider in set // Make sure only one default provider in set
reset(&tx, self.profile_id, false)?; reset(&tx, self.profile_id, false)?;