handle duplicate keys in schema 11 deckconf
This commit is contained in:
parent
40093f813f
commit
59ec485852
@ -34,7 +34,8 @@ pub struct DeckConfSchema11 {
|
|||||||
dynamic: bool,
|
dynamic: bool,
|
||||||
|
|
||||||
// 2021 scheduler options: these were not in schema 11, but we need to persist them
|
// 2021 scheduler options: these were not in schema 11, but we need to persist them
|
||||||
// so the settings are not lost on upgrade/downgrade
|
// so the settings are not lost on upgrade/downgrade.
|
||||||
|
// NOTE: if adding new ones, make sure to update clear_other_duplicates()
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
new_mix: i32,
|
new_mix: i32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -278,7 +279,7 @@ impl From<DeckConfSchema11> for DeckConf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// schema 15 -> schema 11
|
// latest schema -> schema 11
|
||||||
impl From<DeckConf> for DeckConfSchema11 {
|
impl From<DeckConf> for DeckConfSchema11 {
|
||||||
fn from(c: DeckConf) -> DeckConfSchema11 {
|
fn from(c: DeckConf) -> DeckConfSchema11 {
|
||||||
// split extra json up
|
// split extra json up
|
||||||
@ -290,6 +291,7 @@ impl From<DeckConf> for DeckConfSchema11 {
|
|||||||
top_other = Default::default();
|
top_other = Default::default();
|
||||||
} else {
|
} else {
|
||||||
top_other = serde_json::from_slice(&c.inner.other).unwrap_or_default();
|
top_other = serde_json::from_slice(&c.inner.other).unwrap_or_default();
|
||||||
|
clear_other_duplicates(&mut top_other);
|
||||||
if let Some(new) = top_other.remove("new") {
|
if let Some(new) = top_other.remove("new") {
|
||||||
let val: HashMap<String, Value> = serde_json::from_value(new).unwrap_or_default();
|
let val: HashMap<String, Value> = serde_json::from_value(new).unwrap_or_default();
|
||||||
new_other = val;
|
new_other = val;
|
||||||
@ -359,3 +361,21 @@ impl From<DeckConf> for DeckConfSchema11 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_other_duplicates(top_other: &mut HashMap<String, Value>) {
|
||||||
|
// Older clients may have received keys from a newer client when
|
||||||
|
// syncing, which get bundled into `other`. If they then upgrade, then
|
||||||
|
// downgrade their collection to schema11, serde will serialize the
|
||||||
|
// new default keys, but then add them again from `other`, leading
|
||||||
|
// to the keys being duplicated in the resulting json - which older
|
||||||
|
// clients then can't read. So we need to strip out any new keys we
|
||||||
|
// add.
|
||||||
|
for key in &[
|
||||||
|
"newMix",
|
||||||
|
"newPerDayMinimum",
|
||||||
|
"interdayLearningMix",
|
||||||
|
"reviewOrder",
|
||||||
|
] {
|
||||||
|
top_other.remove(*key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use rusqlite::{params, Row, NO_PARAMS};
|
use rusqlite::{params, Row, NO_PARAMS};
|
||||||
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
fn row_to_deckconf(row: &Row) -> Result<DeckConf> {
|
fn row_to_deckconf(row: &Row) -> Result<DeckConf> {
|
||||||
@ -139,12 +140,19 @@ impl SqliteStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn upgrade_deck_conf_to_schema14(&self) -> Result<()> {
|
pub(super) fn upgrade_deck_conf_to_schema14(&self) -> Result<()> {
|
||||||
let conf = self
|
let conf: HashMap<DeckConfID, DeckConfSchema11> =
|
||||||
.db
|
self.db
|
||||||
.query_row_and_then("select dconf from col", NO_PARAMS, |row| {
|
.query_row_and_then("select dconf from col", NO_PARAMS, |row| -> Result<_> {
|
||||||
let conf: Result<HashMap<DeckConfID, DeckConfSchema11>> =
|
let text = row.get_raw(0).as_str()?;
|
||||||
serde_json::from_str(row.get_raw(0).as_str()?).map_err(Into::into);
|
// try direct parse
|
||||||
conf
|
serde_json::from_str(text)
|
||||||
|
.or_else(|_| {
|
||||||
|
// failed, and could be caused by duplicate keys. Serialize into
|
||||||
|
// a value first to discard them, then try again
|
||||||
|
let conf: Value = serde_json::from_str(text)?;
|
||||||
|
serde_json::from_value(conf)
|
||||||
|
})
|
||||||
|
.map_err(Into::into)
|
||||||
})?;
|
})?;
|
||||||
for (_, mut conf) in conf.into_iter() {
|
for (_, mut conf) in conf.into_iter() {
|
||||||
self.add_deck_conf_schema14(&mut conf)?;
|
self.add_deck_conf_schema14(&mut conf)?;
|
||||||
|
Loading…
Reference in New Issue
Block a user