use std::{
cell::RefCell,
collections::{BTreeMap, HashMap},
fmt, iter,
iter::{Iterator as StdIterator, Peekable},
marker::PhantomData,
mem,
ops::{Bound, Deref, DerefMut},
rc::Rc,
result::Result as StdResult,
};
use crate::{
validation::assert_valid_name_component,
views::{AsReadonly, ChangesIter, IndexesPool, RawAccess, ResolvedAddress, View},
Error, Result,
};
#[derive(Debug, Default, Clone)]
pub struct ViewChanges {
pub(super) data: BTreeMap<Vec<u8>, Change>,
is_cleared: bool,
}
impl ViewChanges {
fn new() -> Self {
Self::default()
}
pub fn is_cleared(&self) -> bool {
self.is_cleared
}
pub fn clear(&mut self) {
self.data.clear();
self.is_cleared = true;
}
pub(crate) fn into_data(self) -> BTreeMap<Vec<u8>, Change> {
self.data
}
pub fn get(&self, key: &[u8]) -> StdResult<Option<Vec<u8>>, ()> {
if let Some(change) = self.data.get(key) {
return Ok(match *change {
Change::Put(ref v) => Some(v.clone()),
Change::Delete => None,
});
}
if self.is_cleared() {
return Ok(None);
}
Err(())
}
pub fn contains(&self, key: &[u8]) -> StdResult<bool, ()> {
if let Some(change) = self.data.get(key) {
return Ok(match *change {
Change::Put(..) => true,
Change::Delete => false,
});
}
if self.is_cleared() {
return Ok(false);
}
Err(())
}
}
type ChangesCell = Option<Rc<ViewChanges>>;
#[derive(Debug, Default)]
struct WorkingPatch {
changes: RefCell<HashMap<ResolvedAddress, ChangesCell>>,
}
#[derive(Debug)]
enum WorkingPatchRef<'a> {
Borrowed(&'a WorkingPatch),
Owned(Rc<Fork>),
}
impl WorkingPatchRef<'_> {
fn patch(&self) -> &WorkingPatch {
match self {
WorkingPatchRef::Borrowed(patch) => patch,
WorkingPatchRef::Owned(ref fork) => &fork.working_patch,
}
}
}
#[derive(Debug)]
pub struct ChangesRef<'a> {
inner: Rc<ViewChanges>,
_lifetime: PhantomData<&'a ()>,
}
impl Drop for ChangesRef<'_> {
fn drop(&mut self) {
}
}
impl Deref for ChangesRef<'_> {
type Target = ViewChanges;
fn deref(&self) -> &ViewChanges {
&*self.inner
}
}
#[derive(Debug)]
pub struct ChangesMut<'a> {
parent: WorkingPatchRef<'a>,
key: ResolvedAddress,
changes: Option<Rc<ViewChanges>>,
}
impl Deref for ChangesMut<'_> {
type Target = ViewChanges;
fn deref(&self) -> &ViewChanges {
self.changes.as_ref().unwrap()
}
}
impl DerefMut for ChangesMut<'_> {
fn deref_mut(&mut self) -> &mut ViewChanges {
Rc::get_mut(self.changes.as_mut().unwrap()).unwrap()
}
}
impl Drop for ChangesMut<'_> {
fn drop(&mut self) {
let mut change_map = self.parent.patch().changes.borrow_mut();
let changes = change_map.get_mut(&self.key).unwrap_or_else(|| {
panic!("insertion point for changes disappeared at {:?}", self.key);
});
debug_assert!(changes.is_none(), "edit conflict at {:?}", self.key);
*changes = self.changes.take();
}
}
impl WorkingPatch {
fn new() -> Self {
Self {
changes: RefCell::new(HashMap::new()),
}
}
fn take_view_changes(&self, address: &ResolvedAddress) -> ChangesCell {
let view_changes = {
let mut changes = self.changes.borrow_mut();
let view_changes = changes.get_mut(address).map(Option::take);
view_changes.unwrap_or_else(|| {
changes
.entry(address.clone())
.or_insert_with(|| Some(Rc::new(ViewChanges::new())))
.take()
})
};
if let Some(ref view_changes) = view_changes {
assert!(
Rc::strong_count(view_changes) == 1,
"Attempting to borrow {:?} mutably while it's borrowed immutably",
address
);
} else {
panic!("Multiple mutable borrows of an index at {:?}", address);
}
view_changes
}
fn clone_view_changes(&self, address: &ResolvedAddress) -> Rc<ViewChanges> {
let mut changes = self.changes.borrow_mut();
let changes: &ChangesCell = changes
.entry(address.clone())
.or_insert_with(|| Some(Rc::new(ViewChanges::new())));
changes
.as_ref()
.unwrap_or_else(|| {
panic!(
"Attempting to borrow {:?} immutably while it's borrowed mutably",
address
);
})
.clone()
}
fn merge_into(self, patch: &mut Patch) {
for (address, changes) in self.changes.into_inner() {
let changes = changes.unwrap_or_else(|| {
panic!(
"changes are still mutably borrowed at address {:?}",
address
);
});
let changes = Rc::try_unwrap(changes).unwrap_or_else(|_| {
panic!(
"changes are still immutably borrowed at address {:?}",
address
);
});
let patch_changes = patch
.changes
.entry(address)
.or_insert_with(ViewChanges::new);
if changes.is_cleared() {
*patch_changes = changes;
} else {
patch_changes.data.extend(changes.data);
}
}
}
}
pub type Iter<'a> = Box<dyn Iterator + 'a>;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(test, derive(Eq, Hash))] pub enum Change {
Put(Vec<u8>),
Delete,
}
#[derive(Debug)]
pub struct Fork {
patch: Patch,
working_patch: WorkingPatch,
}
#[derive(Debug)]
pub struct Patch {
snapshot: Box<dyn Snapshot>,
changes: HashMap<ResolvedAddress, ViewChanges>,
}
pub(super) struct ForkIter<'a, T: StdIterator> {
snapshot: Iter<'a>,
changes: Option<Peekable<T>>,
}
#[derive(Debug, PartialEq, Eq)]
enum NextIterValue {
Stored,
Replaced,
Inserted,
Deleted,
MissDeleted,
Finished,
}
pub trait Database: Send + Sync + 'static {
fn snapshot(&self) -> Box<dyn Snapshot>;
fn fork(&self) -> Fork {
Fork {
patch: Patch {
snapshot: self.snapshot(),
changes: HashMap::new(),
},
working_patch: WorkingPatch::new(),
}
}
fn merge(&self, patch: Patch) -> Result<()>;
fn merge_sync(&self, patch: Patch) -> Result<()>;
}
pub trait DatabaseExt: Database {
fn merge_with_backup(&self, patch: Patch) -> Result<Patch> {
let snapshot = self.snapshot();
let mut rev_changes = HashMap::with_capacity(patch.changes.len());
for (name, changes) in &patch.changes {
let mut view_changes = changes.data.clone();
for (key, change) in &mut view_changes {
*change = snapshot.get(name, key).map_or(Change::Delete, Change::Put);
}
if changes.is_cleared() {
let mut iter = snapshot.iter(name, &[]);
while let Some((key, value)) = iter.next() {
view_changes.insert(key.to_vec(), Change::Put(value.to_vec()));
}
}
rev_changes.insert(
name.clone(),
ViewChanges {
data: view_changes,
is_cleared: false,
},
);
}
self.merge(patch)?;
Ok(Patch {
snapshot: self.snapshot(),
changes: rev_changes,
})
}
}
impl<T: Database> DatabaseExt for T {}
pub trait Snapshot: Send + Sync + 'static {
fn get(&self, name: &ResolvedAddress, key: &[u8]) -> Option<Vec<u8>>;
fn multi_get<'a>(
&self,
name: &ResolvedAddress,
keys: &'a mut dyn iter::Iterator<Item = &'a [u8]>,
) -> Vec<Option<Vec<u8>>>;
fn contains(&self, name: &ResolvedAddress, key: &[u8]) -> bool {
self.get(name, key).is_some()
}
fn iter(&self, name: &ResolvedAddress, from: &[u8]) -> Iter<'_>;
}
pub trait Iterator {
fn next(&mut self) -> Option<(&[u8], &[u8])>;
fn peek(&mut self) -> Option<(&[u8], &[u8])>;
}
impl Patch {
pub(crate) fn into_changes(self) -> HashMap<ResolvedAddress, ViewChanges> {
self.changes
}
}
impl Snapshot for Patch {
fn get(&self, name: &ResolvedAddress, key: &[u8]) -> Option<Vec<u8>> {
self.changes
.get(name)
.map_or(Err(()), |changes| changes.get(key))
.unwrap_or_else(|()| self.snapshot.get(name, key))
}
fn multi_get<'a>(
&self,
name: &ResolvedAddress,
keys: &'a mut dyn iter::Iterator<Item = &'a [u8]>,
) -> Vec<Option<Vec<u8>>> {
let changes = self.changes.get(name);
let size = {
let (min, max) = keys.size_hint();
max.unwrap_or(min)
};
let (mut res, db_keys) = keys.into_iter().enumerate().fold(
(Vec::with_capacity(size), Vec::with_capacity(size)),
|(mut res, mut db_keys), (idx, key)| {
if let Some(Ok(item)) = changes.map(|changes| changes.get(key)) {
res.push(item);
} else {
res.push(None);
db_keys.push((idx, key));
}
(res, db_keys)
},
);
let db_res = self
.snapshot
.multi_get(name, &mut db_keys.iter().map(|(_, key)| *key));
for ((idx, _), item) in db_keys.into_iter().zip(db_res) {
res[idx] = item;
}
res
}
fn contains(&self, name: &ResolvedAddress, key: &[u8]) -> bool {
self.changes
.get(name)
.map_or(Err(()), |changes| changes.contains(key))
.unwrap_or_else(|()| self.snapshot.contains(name, key))
}
fn iter(&self, name: &ResolvedAddress, from: &[u8]) -> Iter<'_> {
let maybe_changes = self.changes.get(name);
let changes_iter = maybe_changes.map(|changes| {
changes
.data
.range::<[u8], _>((Bound::Included(from), Bound::Unbounded))
});
let is_cleared = maybe_changes.map_or(false, ViewChanges::is_cleared);
if is_cleared {
Box::new(ChangesIter::new(changes_iter.unwrap()))
} else {
Box::new(ForkIter::new(self.snapshot.iter(name, from), changes_iter))
}
}
}
impl RawAccess for &'_ Patch {
type Changes = ();
fn snapshot(&self) -> &dyn Snapshot {
*self as &dyn Snapshot
}
fn changes(&self, _address: &ResolvedAddress) -> Self::Changes {}
}
impl AsReadonly for &'_ Patch {
type Readonly = Self;
fn as_readonly(&self) -> Self::Readonly {
self
}
}
impl Fork {
pub fn flush(&mut self) {
let working_patch = mem::replace(&mut self.working_patch, WorkingPatch::new());
working_patch.merge_into(&mut self.patch);
}
pub(crate) fn flush_migration(&mut self, prefix: &str) {
assert_valid_name_component(prefix);
self.flush();
let removed_addrs = IndexesPool::new(&*self).flush_migration(prefix);
for addr in removed_addrs {
self.patch.changes.entry(addr).or_default().clear();
}
}
pub fn rollback(&mut self) {
self.working_patch = WorkingPatch::new();
}
pub(crate) fn rollback_migration(&mut self, prefix: &str) {
assert_valid_name_component(prefix);
self.flush();
let removed_addrs = IndexesPool::new(&*self).rollback_migration(prefix);
for addr in &removed_addrs {
self.patch.changes.remove(addr);
}
}
pub fn into_patch(mut self) -> Patch {
self.flush();
self.patch
}
pub fn readonly(&self) -> ReadonlyFork<'_> {
ReadonlyFork(self)
}
}
impl From<Patch> for Fork {
fn from(patch: Patch) -> Self {
Self {
patch,
working_patch: WorkingPatch::new(),
}
}
}
impl<'a> RawAccess for &'a Fork {
type Changes = ChangesMut<'a>;
fn snapshot(&self) -> &dyn Snapshot {
&self.patch
}
fn changes(&self, address: &ResolvedAddress) -> Self::Changes {
let changes = self.working_patch.take_view_changes(address);
ChangesMut {
changes,
key: address.clone(),
parent: WorkingPatchRef::Borrowed(&self.working_patch),
}
}
}
impl RawAccess for Rc<Fork> {
type Changes = ChangesMut<'static>;
fn snapshot(&self) -> &dyn Snapshot {
&self.patch
}
fn changes(&self, address: &ResolvedAddress) -> Self::Changes {
let changes = self.working_patch.take_view_changes(address);
ChangesMut {
changes,
key: address.clone(),
parent: WorkingPatchRef::Owned(Self::clone(self)),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ReadonlyFork<'a>(&'a Fork);
impl<'a> AsReadonly for ReadonlyFork<'a> {
type Readonly = Self;
fn as_readonly(&self) -> Self::Readonly {
*self
}
}
impl<'a> AsReadonly for &'a Fork {
type Readonly = ReadonlyFork<'a>;
fn as_readonly(&self) -> Self::Readonly {
ReadonlyFork(*self)
}
}
impl<'a> RawAccess for ReadonlyFork<'a> {
type Changes = ChangesRef<'a>;
fn snapshot(&self) -> &dyn Snapshot {
&self.0.patch
}
fn changes(&self, address: &ResolvedAddress) -> Self::Changes {
ChangesRef {
inner: self.0.working_patch.clone_view_changes(address),
_lifetime: PhantomData,
}
}
}
#[derive(Debug, Clone)]
pub struct OwnedReadonlyFork(Rc<Fork>);
impl RawAccess for OwnedReadonlyFork {
type Changes = ChangesRef<'static>;
fn snapshot(&self) -> &dyn Snapshot {
&self.0.patch
}
fn changes(&self, address: &ResolvedAddress) -> Self::Changes {
ChangesRef {
inner: self.0.working_patch.clone_view_changes(address),
_lifetime: PhantomData,
}
}
}
impl AsReadonly for OwnedReadonlyFork {
type Readonly = Self;
fn as_readonly(&self) -> Self::Readonly {
self.clone()
}
}
impl AsReadonly for Rc<Fork> {
type Readonly = OwnedReadonlyFork;
fn as_readonly(&self) -> Self::Readonly {
OwnedReadonlyFork(self.clone())
}
}
impl AsRef<dyn Snapshot> for dyn Snapshot {
fn as_ref(&self) -> &dyn Snapshot {
self
}
}
impl Snapshot for Box<dyn Snapshot> {
fn get(&self, name: &ResolvedAddress, key: &[u8]) -> Option<Vec<u8>> {
self.as_ref().get(name, key)
}
fn multi_get<'a>(
&self,
name: &ResolvedAddress,
keys: &'a mut dyn iter::Iterator<Item = &'a [u8]>,
) -> Vec<Option<Vec<u8>>> {
self.as_ref().multi_get(name, keys)
}
fn contains(&self, name: &ResolvedAddress, key: &[u8]) -> bool {
self.as_ref().contains(name, key)
}
fn iter(&self, name: &ResolvedAddress, from: &[u8]) -> Iter<'_> {
self.as_ref().iter(name, from)
}
}
impl<'a, T> ForkIter<'a, T>
where
T: StdIterator<Item = (&'a Vec<u8>, &'a Change)>,
{
pub fn new(snapshot: Iter<'a>, changes: Option<T>) -> Self {
ForkIter {
snapshot,
changes: changes.map(StdIterator::peekable),
}
}
#[allow(clippy::option_if_let_else)]
fn step(&mut self) -> NextIterValue {
use std::cmp::Ordering::{Equal, Greater, Less};
if let Some(ref mut changes) = self.changes {
match changes.peek() {
Some(&(k, change)) => match self.snapshot.peek() {
Some((key, ..)) => match *change {
Change::Put(..) => match k[..].cmp(key) {
Equal => NextIterValue::Replaced,
Less => NextIterValue::Inserted,
Greater => NextIterValue::Stored,
},
Change::Delete => match k[..].cmp(key) {
Equal => NextIterValue::Deleted,
Less => NextIterValue::MissDeleted,
Greater => NextIterValue::Stored,
},
},
None => match *change {
Change::Put(..) => NextIterValue::Inserted,
Change::Delete => NextIterValue::MissDeleted,
},
},
None => match self.snapshot.peek() {
Some(..) => NextIterValue::Stored,
None => NextIterValue::Finished,
},
}
} else {
match self.snapshot.peek() {
Some(..) => NextIterValue::Stored,
None => NextIterValue::Finished,
}
}
}
}
impl<'a, T> Iterator for ForkIter<'a, T>
where
T: StdIterator<Item = (&'a Vec<u8>, &'a Change)>,
{
fn next(&mut self) -> Option<(&[u8], &[u8])> {
loop {
match self.step() {
NextIterValue::Stored => return self.snapshot.next(),
NextIterValue::Replaced => {
self.snapshot.next();
return self.changes.as_mut().unwrap().next().map(|(key, change)| {
(
key.as_slice(),
match *change {
Change::Put(ref value) => value.as_slice(),
Change::Delete => unreachable!(),
},
)
});
}
NextIterValue::Inserted => {
return self.changes.as_mut().unwrap().next().map(|(key, change)| {
(
key.as_slice(),
match *change {
Change::Put(ref value) => value.as_slice(),
Change::Delete => unreachable!(),
},
)
});
}
NextIterValue::Deleted => {
self.changes.as_mut().unwrap().next();
self.snapshot.next();
}
NextIterValue::MissDeleted => {
self.changes.as_mut().unwrap().next();
}
NextIterValue::Finished => return None,
}
}
}
fn peek(&mut self) -> Option<(&[u8], &[u8])> {
loop {
match self.step() {
NextIterValue::Stored => return self.snapshot.peek(),
NextIterValue::Replaced | NextIterValue::Inserted => {
return self.changes.as_mut().unwrap().peek().map(|&(key, change)| {
(
key.as_slice(),
match *change {
Change::Put(ref value) => value.as_slice(),
Change::Delete => unreachable!(),
},
)
});
}
NextIterValue::Deleted => {
self.changes.as_mut().unwrap().next();
self.snapshot.next();
}
NextIterValue::MissDeleted => {
self.changes.as_mut().unwrap().next();
}
NextIterValue::Finished => return None,
}
}
}
}
impl fmt::Debug for dyn Database {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Database").finish()
}
}
impl fmt::Debug for dyn Snapshot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Snapshot").finish()
}
}
impl fmt::Debug for dyn Iterator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Iterator").finish()
}
}
pub const DB_VERSION: u8 = 0;
pub const DB_METADATA: &str = "__DB_METADATA__";
pub const VERSION_NAME: &str = "version";
pub fn check_database(db: &mut dyn Database) -> Result<()> {
let fork = db.fork();
{
let addr = ResolvedAddress::system(DB_METADATA);
let mut view = View::new(&fork, addr);
if let Some(saved_version) = view.get::<_, u8>(VERSION_NAME) {
if saved_version != DB_VERSION {
return Err(Error::new(format!(
"Database version doesn't match: actual {}, expected {}",
saved_version, DB_VERSION
)));
}
return Ok(());
}
view.put(VERSION_NAME, DB_VERSION);
}
db.merge(fork.into_patch())
}
#[cfg(test)]
mod tests {
use super::{
AsReadonly, Change, Database, DatabaseExt, Fork, OwnedReadonlyFork, Patch, Rc,
ResolvedAddress, Snapshot, StdIterator, View,
};
use crate::{access::CopyAccessExt, TemporaryDB};
use std::{collections::HashSet, iter};
#[test]
fn readonly_indexes_are_timely_dropped() {
let db = TemporaryDB::new();
let fork = db.fork();
fork.get_list("list").push(1_u64);
{
let _list = fork.readonly().get_list::<_, u64>("list");
}
fork.into_patch();
}
fn check_patch<'a, I>(patch: &Patch, changes: I)
where
I: IntoIterator<Item = (&'a str, &'a [u8], Change)>,
{
let mut patch_set: HashSet<_> = HashSet::new();
for (name, changes) in &patch.changes {
for (key, value) in &changes.data {
patch_set.insert((name.clone(), key.as_slice(), value.clone()));
}
}
let expected_set: HashSet<_> = changes
.into_iter()
.map(|(name, key, change)| (ResolvedAddress::system(name), key, change))
.collect();
assert_eq!(patch_set, expected_set);
}
#[test]
fn backup_data_is_correct() {
let db = TemporaryDB::new();
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.put(&vec![], vec![2]);
}
let backup = db.merge_with_backup(fork.into_patch()).unwrap();
check_patch(&backup, vec![("foo", &[] as &[u8], Change::Delete)]);
let snapshot = db.snapshot();
assert_eq!(snapshot.get(&"foo".into(), &[]), Some(vec![2]));
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.put(&vec![], vec![3]);
let mut view = View::new(&fork, "bar");
view.put(&vec![1], vec![4]);
let mut view = View::new(&fork, "bar2");
view.put(&vec![5], vec![6]);
}
let backup = db.merge_with_backup(fork.into_patch()).unwrap();
check_patch(
&backup,
vec![
("bar2", &[5_u8] as &[u8], Change::Delete),
("bar", &[1], Change::Delete),
("foo", &[], Change::Put(vec![2])),
],
);
assert_eq!(snapshot.get(&"foo".into(), &[]), Some(vec![2]));
let snapshot = db.snapshot();
assert_eq!(snapshot.get(&"foo".into(), &[]), Some(vec![3]));
}
#[test]
fn rollback_via_backup_patches() {
let db = TemporaryDB::new();
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.put(&vec![], vec![2]);
}
db.merge(fork.into_patch()).unwrap();
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.put(&vec![], vec![3]);
let mut view = View::new(&fork, "bar");
view.put(&vec![1], vec![4]);
}
let backup = db.merge_with_backup(fork.into_patch()).unwrap();
let snapshot = db.snapshot();
assert_eq!(snapshot.get(&"foo".into(), &[]), Some(vec![3]));
assert_eq!(backup.get(&"foo".into(), &[]), Some(vec![2]));
assert_eq!(backup.get(&"bar".into(), &[1]), None);
assert_eq!(snapshot.get(&"bar".into(), &[1]), Some(vec![4]));
assert_eq!(
snapshot.multi_get(&"foo".into(), &mut iter::once(&[] as &[u8])),
vec![Some(vec![3])]
);
assert_eq!(
backup.multi_get(&"foo".into(), &mut iter::once(&[] as &[u8])),
vec![Some(vec![2])]
);
assert_eq!(
backup.multi_get(&"bar".into(), &mut iter::once(&[1u8] as &[u8])),
vec![None]
);
assert_eq!(
snapshot.multi_get(&"bar".into(), &mut iter::once(&[1u8] as &[u8])),
vec![Some(vec![4])]
);
db.merge(backup).unwrap();
let snapshot = db.snapshot();
assert_eq!(snapshot.get(&"foo".into(), &[]), Some(vec![2]));
assert_eq!(snapshot.get(&"bar".into(), &[1]), None);
assert_eq!(
snapshot.multi_get(&"foo".into(), &mut iter::once(&[] as &[u8])),
vec![Some(vec![2])]
);
assert_eq!(
snapshot.multi_get(&"bar".into(), &mut iter::once(&[1u8] as &[u8])),
vec![None]
);
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.put(&vec![], vec![4]);
view.put(&vec![0, 0], vec![255]);
let mut view = View::new(&fork, "bar");
view.put(&vec![1], vec![253]);
}
let backup1 = db.merge_with_backup(fork.into_patch()).unwrap();
let snapshot = db.snapshot();
assert_eq!(
snapshot.multi_get(
&"foo".into(),
&mut vec![&[] as &[u8], &[0u8, 0]].into_iter()
),
vec![Some(vec![4]), Some(vec![255])]
);
let fork = db.fork();
{
let mut view = View::new(&fork, "bar");
view.put(&vec![1], vec![254]);
}
let backup2 = db.merge_with_backup(fork.into_patch()).unwrap();
let snapshot = db.snapshot();
assert_eq!(
snapshot.multi_get(
&"foo".into(),
&mut vec![&[] as &[u8], &[0u8, 0]].into_iter()
),
vec![Some(vec![4]), Some(vec![255])]
);
assert_eq!(snapshot.get(&"bar".into(), &[1]), Some(vec![254]));
assert_eq!(backup1.get(&"bar".into(), &[1]), None);
assert_eq!(backup2.get(&"bar".into(), &[1]), Some(vec![253]));
assert_eq!(backup1.get(&"foo".into(), &[]), Some(vec![2]));
assert_eq!(backup2.get(&"foo".into(), &[]), Some(vec![4]));
db.merge(backup2).unwrap();
db.merge(backup1).unwrap();
let snapshot = db.snapshot();
assert_eq!(
snapshot.multi_get(&"foo".into(), &mut vec![&[] as &[u8], &[0, 0]].into_iter()),
vec![Some(vec![2]), None]
);
assert_eq!(snapshot.get(&"bar".into(), &[1]), None);
}
#[test]
fn backup_after_clearing_view() {
let db = TemporaryDB::new();
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.put(&vec![], vec![1]);
view.put(&vec![1], vec![2]);
}
db.merge(fork.into_patch()).unwrap();
let fork = db.fork();
{
let mut view = View::new(&fork, "foo");
view.clear();
view.put(&vec![1], vec![3]);
view.put(&vec![2], vec![4]);
}
let backup = db.merge_with_backup(fork.into_patch()).unwrap();
assert_eq!(
backup.multi_get(
&"foo".into(),
&mut vec![&[] as &[u8], &[1], &[2]].into_iter()
),
vec![Some(vec![1]), Some(vec![2]), None]
);
db.merge(backup).unwrap();
let snapshot = db.snapshot();
assert_eq!(
snapshot.multi_get(
&"foo".into(),
&mut vec![&[] as &[u8], &[1], &[2]].into_iter()
),
vec![Some(vec![1]), Some(vec![2]), None]
);
}
#[test]
fn backup_reverting_index_creation() {
let db = TemporaryDB::new();
let fork = db.fork();
fork.get_entry("foo").set(1_u32);
db.merge(fork.into_patch()).unwrap();
let fork = db.fork();
fork.get_entry(("foo", &1_u8)).set(2_u32);
let backup = db.merge_with_backup(fork.into_patch()).unwrap();
assert!(backup.index_type(("foo", &1_u8)).is_none());
assert!(backup.get_list::<_, u32>(("foo", &1_u8)).is_empty());
}
#[test]
fn borrows_from_owned_forks() {
use crate::{access::AccessExt, Entry};
let db = TemporaryDB::new();
let fork = Rc::new(db.fork());
let readonly: OwnedReadonlyFork = fork.as_readonly();
fork.get_list("list").extend(vec![1_i64, 2, 3]);
let mut entry: Entry<Rc<Fork>, _> = fork.get_entry("entry");
let list = readonly.get_list::<_, i64>("list");
assert_eq!(list.len(), 3);
assert_eq!(list.get(1), Some(2));
assert_eq!(list.iter_from(1).collect::<Vec<_>>(), vec![2, 3]);
entry.set("!".to_owned());
drop(entry);
let entry = readonly.get_entry::<_, String>("entry");
let other_readonly = readonly;
let other_entry = other_readonly.get_entry::<_, String>("entry");
assert_eq!(entry.get().unwrap(), "!");
assert_eq!(other_entry.get().unwrap(), "!");
}
#[test]
fn concurrent_borrow_from_fork_and_readonly_fork() {
let db = TemporaryDB::new();
let fork = db.fork();
let _readonly_entry = fork.readonly().get_entry::<_, u32>(("entry", &1_u8));
let _entry = fork.get_entry::<_, u32>("entry");
}
#[test]
fn stale_read_from_phantom_index() {
let db = TemporaryDB::new();
let fork = db.fork();
let phantom_entry = fork.readonly().get_entry::<_, u32>("entry");
let mut entry = fork.get_entry::<_, u32>("entry");
entry.set(1);
assert_eq!(phantom_entry.get(), None);
}
#[test]
#[should_panic(expected = "immutably while it's borrowed mutably")]
fn borrow_from_readonly_fork_after_index_is_created() {
let db = TemporaryDB::new();
let fork = db.fork();
let _entry = fork.get_entry::<_, u32>("entry");
let _readonly_entry = fork.readonly().get_entry::<_, u32>("entry");
}
}