From 2f4e35d566c2f865277d5bac4be293f9b81b4403 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 27 Mar 2020 08:49:05 +1000 Subject: [PATCH] store cached statements in a vec instead of separate optionals --- rslib/Cargo.toml | 1 + rslib/src/collection.rs | 2 +- rslib/src/storage/card.rs | 117 ++++++++++++++++++------------------ rslib/src/storage/sqlite.rs | 42 ++++++++----- 4 files changed, 87 insertions(+), 75 deletions(-) diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 906fb1692..4cdf2491a 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -39,6 +39,7 @@ slog-envlogger = "2.2.0" serde_repr = "0.1.5" num_enum = "0.4.2" unicase = "2.6.0" +variant_count = "=1.0.0" # pinned until rusqlite 0.22 comes out [target.'cfg(target_vendor="apple")'.dependencies.rusqlite] diff --git a/rslib/src/collection.rs b/rslib/src/collection.rs index 0e183bec6..627a779be 100644 --- a/rslib/src/collection.rs +++ b/rslib/src/collection.rs @@ -52,7 +52,7 @@ pub struct Collection { } pub(crate) enum CollectionOp { - UpdateCard + UpdateCard, } pub(crate) struct RequestContext<'a> { diff --git a/rslib/src/storage/card.rs b/rslib/src/storage/card.rs index 66adec7ef..fd3a40e68 100644 --- a/rslib/src/storage/card.rs +++ b/rslib/src/storage/card.rs @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use crate::cached_sql; +use super::sqlite::CachedStatementKind; use crate::card::{Card, CardID, CardQueue, CardType}; use crate::err::Result; use rusqlite::params; @@ -35,75 +35,74 @@ impl super::StorageContext<'_> { pub fn get_card(&mut self, cid: CardID) -> Result> { // the casts are required as Anki didn't prevent add-ons from // storing strings or floats in columns before - let stmt = cached_sql!( - self.get_card_stmt, - self.db, + self.with_cached_stmt( + CachedStatementKind::GetCard, " select nid, did, ord, cast(mod as integer), usn, type, queue, due, cast(ivl as integer), factor, reps, lapses, left, odue, odid, -flags, data from cards where id=?" - ); - - stmt.query_row(params![cid], |row| { - Ok(Card { - id: cid, - nid: row.get(0)?, - did: row.get(1)?, - ord: row.get(2)?, - mtime: row.get(3)?, - usn: row.get(4)?, - ctype: row.get(5)?, - queue: row.get(6)?, - due: row.get(7)?, - ivl: row.get(8)?, - factor: row.get(9)?, - reps: row.get(10)?, - lapses: row.get(11)?, - left: row.get(12)?, - odue: row.get(13)?, - odid: row.get(14)?, - flags: row.get(15)?, - data: row.get(16)?, - }) - }) - .optional() - .map_err(Into::into) +flags, data from cards where id=?", + |stmt| { + stmt.query_row(params![cid], |row| { + Ok(Card { + id: cid, + nid: row.get(0)?, + did: row.get(1)?, + ord: row.get(2)?, + mtime: row.get(3)?, + usn: row.get(4)?, + ctype: row.get(5)?, + queue: row.get(6)?, + due: row.get(7)?, + ivl: row.get(8)?, + factor: row.get(9)?, + reps: row.get(10)?, + lapses: row.get(11)?, + left: row.get(12)?, + odue: row.get(13)?, + odid: row.get(14)?, + flags: row.get(15)?, + data: row.get(16)?, + }) + }) + .optional() + .map_err(Into::into) + }, + ) } pub(crate) fn flush_card(&mut self, card: &Card) -> Result<()> { - let stmt = cached_sql!( - self.update_card_stmt, - self.db, + self.with_cached_stmt( + CachedStatementKind::FlushCard, " insert or replace into cards (id, nid, did, ord, mod, usn, type, queue, due, ivl, factor, reps, lapses, left, odue, odid, flags, data) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -" - ); - - stmt.execute(params![ - card.id, - card.nid, - card.did, - card.ord, - card.mtime, - card.usn, - card.ctype as u8, - card.queue as i8, - card.due, - card.ivl, - card.factor, - card.reps, - card.lapses, - card.left, - card.odue, - card.odid, - card.flags, - card.data, - ])?; - - Ok(()) +", + |stmt| { + stmt.execute(params![ + card.id, + card.nid, + card.did, + card.ord, + card.mtime, + card.usn, + card.ctype as u8, + card.queue as i8, + card.due, + card.ivl, + card.factor, + card.reps, + card.lapses, + card.left, + card.odue, + card.odid, + card.flags, + card.data, + ])?; + Ok(()) + }, + ) } } diff --git a/rslib/src/storage/sqlite.rs b/rslib/src/storage/sqlite.rs index 76aa9e1b4..e71f74cb1 100644 --- a/rslib/src/storage/sqlite.rs +++ b/rslib/src/storage/sqlite.rs @@ -24,6 +24,7 @@ use std::{ path::{Path, PathBuf}, }; use unicase::UniCase; +use variant_count::VariantCount; const SCHEMA_MIN_VERSION: u8 = 11; const SCHEMA_MAX_VERSION: u8 = 11; @@ -42,16 +43,6 @@ pub struct SqliteStorage { path: PathBuf, } -#[macro_export] -macro_rules! cached_sql { - ( $label:expr, $db:expr, $sql:expr ) => {{ - if $label.is_none() { - $label = Some($db.prepare_cached($sql)?); - } - $label.as_mut().unwrap() - }}; -} - fn open_or_create_collection_db(path: &Path) -> Result { let mut db = Connection::open(path)?; @@ -213,6 +204,12 @@ impl SqliteStorage { } } +#[derive(Clone, Copy, VariantCount)] +pub(super) enum CachedStatementKind { + GetCard, + FlushCard, +} + pub(crate) struct StorageContext<'a> { pub(crate) db: &'a Connection, server: bool, @@ -220,23 +217,38 @@ pub(crate) struct StorageContext<'a> { timing_today: Option, - // cards - pub(super) get_card_stmt: Option>, - pub(super) update_card_stmt: Option>, + cached_statements: Vec>>, } impl StorageContext<'_> { fn new(db: &Connection, server: bool) -> StorageContext { + let stmt_len = CachedStatementKind::VARIANT_COUNT; + let mut statements = Vec::with_capacity(stmt_len); + statements.resize_with(stmt_len, Default::default); StorageContext { db, server, usn: None, timing_today: None, - get_card_stmt: None, - update_card_stmt: None, + cached_statements: statements, } } + pub(super) fn with_cached_stmt( + &mut self, + kind: CachedStatementKind, + sql: &str, + func: F, + ) -> Result + where + F: FnOnce(&mut rusqlite::CachedStatement) -> Result, + { + if self.cached_statements[kind as usize].is_none() { + self.cached_statements[kind as usize] = Some(self.db.prepare_cached(sql)?); + } + func(self.cached_statements[kind as usize].as_mut().unwrap()) + } + // Standard transaction start/stop //////////////////////////////////////