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"
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]

View File

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

View File

@ -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<Option<Card>> {
// 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(())
},
)
}
}

View File

@ -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<Connection> {
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<SchedTimingToday>,
// cards
pub(super) get_card_stmt: Option<rusqlite::CachedStatement<'a>>,
pub(super) update_card_stmt: Option<rusqlite::CachedStatement<'a>>,
cached_statements: Vec<Option<rusqlite::CachedStatement<'a>>>,
}
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<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
//////////////////////////////////////