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>,
) -> Result<HashSet<String>> {
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;
for_every_note(&self.ctx.storage.db, |note| {
@ -401,7 +401,7 @@ where
&self.mgr.media_folder,
)? {
// 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;
}

View file

@ -7,7 +7,7 @@ use crate::err::{AnkiError, DBErrorKind, Result};
use crate::notetype::NoteTypeID;
use crate::text::strip_html_preserving_image_filenames;
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 std::convert::TryInto;
@ -84,27 +84,21 @@ fn row_to_note(row: &Row) -> Result<Note> {
})
}
pub(super) fn set_note(
db: &Connection,
note: &mut Note,
note_type: &NoteTypeSchema11,
) -> Result<()> {
pub(super) fn set_note(db: &Connection, note: &mut Note, sort_field_idx: usize) -> Result<()> {
note.mtime = TimestampSecs::now();
// hard-coded for now
note.usn = Usn(-1);
let field1_nohtml = strip_html_preserving_image_filenames(&note.fields()[0]);
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
} else {
strip_html_preserving_image_filenames(
note.fields()
.get(note_type.sortf as usize)
.ok_or_else(|| AnkiError::DBError {
info: "sort field out of range".to_string(),
kind: DBErrorKind::MissingEntity,
})?,
)
strip_html_preserving_image_filenames(note.fields().get(sort_field_idx).ok_or_else(
|| AnkiError::DBError {
info: "sort field out of range".to_string(),
kind: DBErrorKind::MissingEntity,
},
)?)
};
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}"#;
// other: vec![], // fixme: ensure empty map converted to empty bytes
// fixme: rollback savepoint when tags not changed
impl NoteType {
pub fn new() -> Self {
@ -53,6 +54,14 @@ impl NoteType {
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) {
let mut names = HashSet::new();
for t in &mut self.templates {

View file

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

View file

@ -4,4 +4,6 @@ select
config
from fields
where
ntid = ?
ntid = ?
order by
ord

View file

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

View file

@ -6,4 +6,6 @@ select
config
from templates
where
ntid = ?
ntid = ?
order by
ord

View file

@ -10,31 +10,41 @@ use crate::{
notetype::{NoteTypeID, NoteTypeSchema11},
};
use prost::Message;
use rusqlite::{params, NO_PARAMS};
use rusqlite::{params, Row, NO_PARAMS};
use std::collections::{HashMap, HashSet};
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 {
fn get_notetype_core(&self, ntid: NoteTypeID) -> Result<Option<NoteType>> {
self.db
.prepare_cached(include_str!("get_notetype.sql"))?
.query_and_then(&[ntid], |row| {
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![],
})
})?
.prepare_cached(concat!(include_str!("get_notetype.sql"), " where id = ?"))?
.query_and_then(&[ntid], row_to_notetype_core)?
.next()
.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
.prepare_cached(include_str!("get_fields.sql"))?
.query_and_then(&[ntid], |row| {