store cached statements in a vec instead of separate optionals

This commit is contained in:
Damien Elmes 2020-03-27 08:49:05 +10:00
parent 369d2d89d9
commit 2f4e35d566
4 changed files with 87 additions and 75 deletions

View file

@ -39,6 +39,7 @@ slog-envlogger = "2.2.0"
serde_repr = "0.1.5" serde_repr = "0.1.5"
num_enum = "0.4.2" num_enum = "0.4.2"
unicase = "2.6.0" unicase = "2.6.0"
variant_count = "=1.0.0"
# pinned until rusqlite 0.22 comes out # pinned until rusqlite 0.22 comes out
[target.'cfg(target_vendor="apple")'.dependencies.rusqlite] [target.'cfg(target_vendor="apple")'.dependencies.rusqlite]

View file

@ -52,7 +52,7 @@ pub struct Collection {
} }
pub(crate) enum CollectionOp { pub(crate) enum CollectionOp {
UpdateCard UpdateCard,
} }
pub(crate) struct RequestContext<'a> { pub(crate) struct RequestContext<'a> {

View file

@ -1,7 +1,7 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // 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::card::{Card, CardID, CardQueue, CardType};
use crate::err::Result; use crate::err::Result;
use rusqlite::params; use rusqlite::params;
@ -35,15 +35,13 @@ impl super::StorageContext<'_> {
pub fn get_card(&mut self, cid: CardID) -> Result<Option<Card>> { pub fn get_card(&mut self, cid: CardID) -> Result<Option<Card>> {
// the casts are required as Anki didn't prevent add-ons from // the casts are required as Anki didn't prevent add-ons from
// storing strings or floats in columns before // storing strings or floats in columns before
let stmt = cached_sql!( self.with_cached_stmt(
self.get_card_stmt, CachedStatementKind::GetCard,
self.db,
" "
select nid, did, ord, cast(mod as integer), usn, type, queue, due, select nid, did, ord, cast(mod as integer), usn, type, queue, due,
cast(ivl as integer), factor, reps, lapses, left, odue, odid, cast(ivl as integer), factor, reps, lapses, left, odue, odid,
flags, data from cards where id=?" flags, data from cards where id=?",
); |stmt| {
stmt.query_row(params![cid], |row| { stmt.query_row(params![cid], |row| {
Ok(Card { Ok(Card {
id: cid, id: cid,
@ -68,21 +66,21 @@ flags, data from cards where id=?"
}) })
.optional() .optional()
.map_err(Into::into) .map_err(Into::into)
},
)
} }
pub(crate) fn flush_card(&mut self, card: &Card) -> Result<()> { pub(crate) fn flush_card(&mut self, card: &Card) -> Result<()> {
let stmt = cached_sql!( self.with_cached_stmt(
self.update_card_stmt, CachedStatementKind::FlushCard,
self.db,
" "
insert or replace into cards insert or replace into cards
(id, nid, did, ord, mod, usn, type, queue, due, ivl, factor, (id, nid, did, ord, mod, usn, type, queue, due, ivl, factor,
reps, lapses, left, odue, odid, flags, data) reps, lapses, left, odue, odid, flags, data)
values values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
" ",
); |stmt| {
stmt.execute(params![ stmt.execute(params![
card.id, card.id,
card.nid, card.nid,
@ -103,7 +101,8 @@ values
card.flags, card.flags,
card.data, card.data,
])?; ])?;
Ok(()) Ok(())
},
)
} }
} }

View file

@ -24,6 +24,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use unicase::UniCase; use unicase::UniCase;
use variant_count::VariantCount;
const SCHEMA_MIN_VERSION: u8 = 11; const SCHEMA_MIN_VERSION: u8 = 11;
const SCHEMA_MAX_VERSION: u8 = 11; const SCHEMA_MAX_VERSION: u8 = 11;
@ -42,16 +43,6 @@ pub struct SqliteStorage {
path: PathBuf, 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<Connection> { fn open_or_create_collection_db(path: &Path) -> Result<Connection> {
let mut db = Connection::open(path)?; 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) struct StorageContext<'a> {
pub(crate) db: &'a Connection, pub(crate) db: &'a Connection,
server: bool, server: bool,
@ -220,23 +217,38 @@ pub(crate) struct StorageContext<'a> {
timing_today: Option<SchedTimingToday>, timing_today: Option<SchedTimingToday>,
// cards cached_statements: Vec<Option<rusqlite::CachedStatement<'a>>>,
pub(super) get_card_stmt: Option<rusqlite::CachedStatement<'a>>,
pub(super) update_card_stmt: Option<rusqlite::CachedStatement<'a>>,
} }
impl StorageContext<'_> { impl StorageContext<'_> {
fn new(db: &Connection, server: bool) -> 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 { StorageContext {
db, db,
server, server,
usn: None, usn: None,
timing_today: None, timing_today: None,
get_card_stmt: None, cached_statements: statements,
update_card_stmt: None,
} }
} }
pub(super) fn with_cached_stmt<F, T>(
&mut self,
kind: CachedStatementKind,
sql: &str,
func: F,
) -> Result<T>
where
F: FnOnce(&mut rusqlite::CachedStatement) -> Result<T>,
{
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 // Standard transaction start/stop
////////////////////////////////////// //////////////////////////////////////