migrate the schema11 usages to new structs/sql queries

This commit is contained in:
Damien Elmes 2020-04-12 21:45:37 +10:00
parent e2b978e7cb
commit 32bc1e88eb
8 changed files with 88 additions and 83 deletions

View file

@ -380,7 +380,7 @@ where
renamed: &HashMap<String, String>, renamed: &HashMap<String, String>,
) -> Result<HashSet<String>> { ) -> Result<HashSet<String>> {
let mut referenced_files = HashSet::new(); let mut referenced_files = HashSet::new();
let note_types = self.ctx.storage.get_all_notetypes_as_schema11()?; let note_types = self.ctx.storage.get_all_notetype_core()?;
let mut collection_modified = false; let mut collection_modified = false;
for_every_note(&self.ctx.storage.db, |note| { for_every_note(&self.ctx.storage.db, |note| {
@ -401,7 +401,7 @@ where
&self.mgr.media_folder, &self.mgr.media_folder,
)? { )? {
// note was modified, needs saving // note was modified, needs saving
set_note(&self.ctx.storage.db, note, nt)?; set_note(&self.ctx.storage.db, note, nt.sort_field_idx())?;
collection_modified = true; collection_modified = true;
} }

View file

@ -7,7 +7,7 @@ use crate::err::{AnkiError, DBErrorKind, Result};
use crate::notetype::NoteTypeID; use crate::notetype::NoteTypeID;
use crate::text::strip_html_preserving_image_filenames; use crate::text::strip_html_preserving_image_filenames;
use crate::timestamp::TimestampSecs; use crate::timestamp::TimestampSecs;
use crate::{define_newtype, notetype::NoteTypeSchema11, types::Usn}; use crate::{define_newtype, types::Usn};
use rusqlite::{params, Connection, Row, NO_PARAMS}; use rusqlite::{params, Connection, Row, NO_PARAMS};
use std::convert::TryInto; use std::convert::TryInto;
@ -84,27 +84,21 @@ fn row_to_note(row: &Row) -> Result<Note> {
}) })
} }
pub(super) fn set_note( pub(super) fn set_note(db: &Connection, note: &mut Note, sort_field_idx: usize) -> Result<()> {
db: &Connection,
note: &mut Note,
note_type: &NoteTypeSchema11,
) -> Result<()> {
note.mtime = TimestampSecs::now(); note.mtime = TimestampSecs::now();
// hard-coded for now // hard-coded for now
note.usn = Usn(-1); note.usn = Usn(-1);
let field1_nohtml = strip_html_preserving_image_filenames(&note.fields()[0]); let field1_nohtml = strip_html_preserving_image_filenames(&note.fields()[0]);
let csum = field_checksum(field1_nohtml.as_ref()); let csum = field_checksum(field1_nohtml.as_ref());
let sort_field = if note_type.sortf == 0 { let sort_field = if sort_field_idx == 0 {
field1_nohtml field1_nohtml
} else { } else {
strip_html_preserving_image_filenames( strip_html_preserving_image_filenames(note.fields().get(sort_field_idx).ok_or_else(
note.fields() || AnkiError::DBError {
.get(note_type.sortf as usize) info: "sort field out of range".to_string(),
.ok_or_else(|| AnkiError::DBError { kind: DBErrorKind::MissingEntity,
info: "sort field out of range".to_string(), },
kind: DBErrorKind::MissingEntity, )?)
})?,
)
}; };
let mut stmt = let mut stmt =

View file

@ -37,6 +37,7 @@ pub(crate) const DEFAULT_LATEX_HEADER: &str = r#"\documentclass[12pt]{article}
pub(crate) const DEFAULT_LATEX_FOOTER: &str = r#"\end{document}"#; pub(crate) const DEFAULT_LATEX_FOOTER: &str = r#"\end{document}"#;
// other: vec![], // fixme: ensure empty map converted to empty bytes // other: vec![], // fixme: ensure empty map converted to empty bytes
// fixme: rollback savepoint when tags not changed
impl NoteType { impl NoteType {
pub fn new() -> Self { pub fn new() -> Self {
@ -53,6 +54,14 @@ impl NoteType {
NoteTypeID(self.id) NoteTypeID(self.id)
} }
pub fn sort_field_idx(&self) -> usize {
self.config.as_ref().unwrap().sort_field_idx as usize
}
pub fn latex_uses_svg(&self) -> bool {
self.config.as_ref().unwrap().latex_svg
}
pub(crate) fn ensure_names_unique(&mut self) { pub(crate) fn ensure_names_unique(&mut self) {
let mut names = HashSet::new(); let mut names = HashSet::new();
for t in &mut self.templates { for t in &mut self.templates {

View file

@ -267,27 +267,16 @@ impl SqlWriter<'_> {
write!(self.sql, "c.ord = {}", n).unwrap(); write!(self.sql, "c.ord = {}", n).unwrap();
} }
TemplateKind::Name(name) => { TemplateKind::Name(name) => {
let note_types = self.col.storage.get_all_notetypes_as_schema11()?; if let Some(glob) = glob_to_re(name) {
let mut id_ords = vec![]; self.sql.push_str(
for nt in note_types.values() { "(n.mid,c.ord) in (select ntid,ord from templates where name regexp ?)",
for tmpl in &nt.tmpls { );
if matches_wildcard(&tmpl.name, name) { self.args.push(glob);
id_ords.push((nt.id, tmpl.ord));
}
}
}
// sort for the benefit of unit tests
id_ords.sort();
if id_ords.is_empty() {
self.sql.push_str("false");
} else { } else {
let v: Vec<_> = id_ords self.sql.push_str(
.iter() "(n.mid,c.ord) in (select ntid,ord from templates where name = ?)",
.map(|(ntid, ord)| format!("(n.mid = {} and c.ord = {})", ntid, ord)) );
.collect(); self.args.push(name.to_string());
write!(self.sql, "({})", v.join(" or ")).unwrap();
} }
} }
}; };
@ -295,29 +284,27 @@ impl SqlWriter<'_> {
} }
fn write_note_type(&mut self, nt_name: &str) -> Result<()> { fn write_note_type(&mut self, nt_name: &str) -> Result<()> {
let mut ntids: Vec<_> = self if let Some(glob) = glob_to_re(nt_name) {
.col self.sql
.storage .push_str("n.mid in (select id from notetypes where name regexp ?)");
.get_all_notetypes_as_schema11()? self.args.push(glob);
.values() } else {
.filter(|nt| matches_wildcard(&nt.name, nt_name)) self.sql
.map(|nt| nt.id) .push_str("n.mid in (select id from notetypes where name = ?)");
.collect(); self.args.push(nt_name.to_string());
self.sql.push_str("n.mid in "); }
// sort for the benefit of unit tests
ntids.sort();
ids_to_string(&mut self.sql, &ntids);
Ok(()) Ok(())
} }
fn write_single_field(&mut self, field_name: &str, val: &str, is_re: bool) -> Result<()> { fn write_single_field(&mut self, field_name: &str, val: &str, is_re: bool) -> Result<()> {
let note_types = self.col.storage.get_all_notetypes_as_schema11()?; let note_types = self.col.storage.get_all_notetype_core()?;
let mut field_map = vec![]; let mut field_map = vec![];
for nt in note_types.values() { for nt in note_types.values() {
for field in &nt.flds { let fields = self.col.storage.get_notetype_fields(nt.id())?;
for field in &fields {
if matches_wildcard(&field.name, field_name) { if matches_wildcard(&field.name, field_name) {
field_map.push((nt.id, field.ord)); field_map.push((nt.id(), field.ord));
} }
} }
} }
@ -539,18 +526,11 @@ mod test {
assert_eq!(s(ctx, "deck:filtered"), ("(c.odid > 0)".into(), vec![],)); assert_eq!(s(ctx, "deck:filtered"), ("(c.odid > 0)".into(), vec![],));
// card // card
assert_eq!(s(ctx, "card:front"), ("(false)".into(), vec![],));
assert_eq!( assert_eq!(
s(ctx, r#""card:card 1""#), s(ctx, r#""card:card 1""#),
( (
concat!( "((n.mid,c.ord) in (select ntid,ord from templates where name = ?))".into(),
"(((n.mid = 1581236385344 and c.ord = 0) or ", vec!["card 1".into()]
"(n.mid = 1581236385345 and c.ord = 0) or ",
"(n.mid = 1581236385346 and c.ord = 0) or ",
"(n.mid = 1581236385347 and c.ord = 0)))"
)
.into(),
vec![],
) )
); );
@ -633,10 +613,19 @@ mod test {
); );
// note types by name // note types by name
assert_eq!(&s(ctx, "note:basic").0, "(n.mid in (1581236385347))");
assert_eq!( assert_eq!(
&s(ctx, "note:basic*").0, s(ctx, "note:basic"),
"(n.mid in (1581236385345,1581236385346,1581236385347,1581236385344))" (
"(n.mid in (select id from notetypes where name = ?))".into(),
vec!["basic".into()]
)
);
assert_eq!(
s(ctx, "note:basic*"),
(
"(n.mid in (select id from notetypes where name regexp ?))".into(),
vec!["basic.*".into()]
)
); );
// regex // regex

View file

@ -5,3 +5,5 @@ select
from fields from fields
where where
ntid = ? ntid = ?
order by
ord

View file

@ -1,8 +1,7 @@
select select
id,
name, name,
mtime_secs, mtime_secs,
usn, usn,
config config
from notetypes from notetypes
where
id = ?

View file

@ -7,3 +7,5 @@ select
from templates from templates
where where
ntid = ? ntid = ?
order by
ord

View file

@ -10,31 +10,41 @@ use crate::{
notetype::{NoteTypeID, NoteTypeSchema11}, notetype::{NoteTypeID, NoteTypeSchema11},
}; };
use prost::Message; use prost::Message;
use rusqlite::{params, NO_PARAMS}; use rusqlite::{params, Row, NO_PARAMS};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use unicase::UniCase; use unicase::UniCase;
fn row_to_notetype_core(row: &Row) -> Result<NoteType> {
let config = NoteTypeConfig::decode(row.get_raw(4).as_blob()?)?;
Ok(NoteType {
id: row.get(0)?,
name: row.get(1)?,
mtime_secs: row.get(2)?,
usn: row.get(3)?,
config: Some(config),
fields: vec![],
templates: vec![],
})
}
impl SqliteStorage { impl SqliteStorage {
fn get_notetype_core(&self, ntid: NoteTypeID) -> Result<Option<NoteType>> { fn get_notetype_core(&self, ntid: NoteTypeID) -> Result<Option<NoteType>> {
self.db self.db
.prepare_cached(include_str!("get_notetype.sql"))? .prepare_cached(concat!(include_str!("get_notetype.sql"), " where id = ?"))?
.query_and_then(&[ntid], |row| { .query_and_then(&[ntid], row_to_notetype_core)?
let config = NoteTypeConfig::decode(row.get_raw(3).as_blob()?)?;
Ok(NoteType {
id: ntid.0,
name: row.get(0)?,
mtime_secs: row.get(1)?,
usn: row.get(2)?,
config: Some(config),
fields: vec![],
templates: vec![],
})
})?
.next() .next()
.transpose() .transpose()
} }
fn get_notetype_fields(&self, ntid: NoteTypeID) -> Result<Vec<NoteField>> { pub(crate) fn get_all_notetype_core(&self) -> Result<HashMap<NoteTypeID, NoteType>> {
self.db
.prepare_cached(include_str!("get_notetype.sql"))?
.query_and_then(NO_PARAMS, row_to_notetype_core)?
.map(|ntres| ntres.map(|nt| (nt.id(), nt)))
.collect()
}
pub(crate) fn get_notetype_fields(&self, ntid: NoteTypeID) -> Result<Vec<NoteField>> {
self.db self.db
.prepare_cached(include_str!("get_fields.sql"))? .prepare_cached(include_str!("get_fields.sql"))?
.query_and_then(&[ntid], |row| { .query_and_then(&[ntid], |row| {