diff --git a/rslib/src/media/check.rs b/rslib/src/media/check.rs index 1d80d315f..bc4b2f12c 100644 --- a/rslib/src/media/check.rs +++ b/rslib/src/media/check.rs @@ -380,7 +380,7 @@ where renamed: &HashMap, ) -> Result> { 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; } diff --git a/rslib/src/notes.rs b/rslib/src/notes.rs index dce91f8b5..f6ded9a95 100644 --- a/rslib/src/notes.rs +++ b/rslib/src/notes.rs @@ -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 { }) } -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(¬e.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 = diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 39b7ecb0f..3767bdedc 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -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 { diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index e8b224d2d..9e2150476 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -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 diff --git a/rslib/src/storage/notetype/get_fields.sql b/rslib/src/storage/notetype/get_fields.sql index d48da014e..aef718770 100644 --- a/rslib/src/storage/notetype/get_fields.sql +++ b/rslib/src/storage/notetype/get_fields.sql @@ -4,4 +4,6 @@ select config from fields where - ntid = ? \ No newline at end of file + ntid = ? +order by + ord \ No newline at end of file diff --git a/rslib/src/storage/notetype/get_notetype.sql b/rslib/src/storage/notetype/get_notetype.sql index ddd43a022..f405ed76d 100644 --- a/rslib/src/storage/notetype/get_notetype.sql +++ b/rslib/src/storage/notetype/get_notetype.sql @@ -1,8 +1,7 @@ select + id, name, mtime_secs, usn, config from notetypes -where - id = ? \ No newline at end of file diff --git a/rslib/src/storage/notetype/get_templates.sql b/rslib/src/storage/notetype/get_templates.sql index e5ff38728..a6fcadb10 100644 --- a/rslib/src/storage/notetype/get_templates.sql +++ b/rslib/src/storage/notetype/get_templates.sql @@ -6,4 +6,6 @@ select config from templates where - ntid = ? \ No newline at end of file + ntid = ? +order by + ord \ No newline at end of file diff --git a/rslib/src/storage/notetype/mod.rs b/rslib/src/storage/notetype/mod.rs index bccf1cd0e..b7b6fdd0b 100644 --- a/rslib/src/storage/notetype/mod.rs +++ b/rslib/src/storage/notetype/mod.rs @@ -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 { + 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> { 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> { + pub(crate) fn get_all_notetype_core(&self) -> Result> { + 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> { self.db .prepare_cached(include_str!("get_fields.sql"))? .query_and_then(&[ntid], |row| {