|
@@ -8,19 +8,18 @@ use crate::{
|
|
|
value::{bytes_to_number, cursor::Cursor, typ::Typ, VDebug, Value},
|
|
|
};
|
|
|
use bytes::{BufMut, Bytes, BytesMut};
|
|
|
-
|
|
|
-use entry::{new_version, Entry};
|
|
|
+use entry::{unique_id, Entry};
|
|
|
use expiration::ExpirationDb;
|
|
|
-
|
|
|
use glob::Pattern;
|
|
|
use log::trace;
|
|
|
use num_traits::CheckedAdd;
|
|
|
-use parking_lot::{Mutex, RwLock};
|
|
|
+use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|
|
use rand::{prelude::SliceRandom, Rng};
|
|
|
use seahash::hash;
|
|
|
use std::{
|
|
|
collections::{HashMap, VecDeque},
|
|
|
convert::{TryFrom, TryInto},
|
|
|
+ ops::{Deref, DerefMut},
|
|
|
str::FromStr,
|
|
|
sync::Arc,
|
|
|
thread,
|
|
@@ -36,6 +35,80 @@ pub mod pool;
|
|
|
pub mod scan;
|
|
|
pub(crate) mod utils;
|
|
|
|
|
|
+/// Read only reference
|
|
|
+pub struct RefValue<'a> {
|
|
|
+ key: &'a Bytes,
|
|
|
+ slot: RwLockReadGuard<'a, HashMap<Bytes, Entry>>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a> RefValue<'a> {
|
|
|
+ /// test
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn into_inner(self) -> Value {
|
|
|
+ self.slot
|
|
|
+ .get(self.key)
|
|
|
+ .filter(|x| x.is_valid())
|
|
|
+ .map(|x| {
|
|
|
+ if x.is_scalar() {
|
|
|
+ x.get().clone()
|
|
|
+ } else {
|
|
|
+ Error::WrongType.into()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .unwrap_or_default()
|
|
|
+ }
|
|
|
+
|
|
|
+ /// test
|
|
|
+ pub fn inner(&self) -> Option<RwLockReadGuard<'_, Value>> {
|
|
|
+ self.slot
|
|
|
+ .get(self.key)
|
|
|
+ .filter(|x| x.is_valid())
|
|
|
+ .map(|x| x.get())
|
|
|
+ }
|
|
|
+
|
|
|
+ /// test
|
|
|
+ pub fn inner_mut(&self) -> Option<RwLockWriteGuard<'_, Value>> {
|
|
|
+ self.slot
|
|
|
+ .get(self.key)
|
|
|
+ .filter(|x| x.is_valid())
|
|
|
+ .map(|x| x.get_mut())
|
|
|
+ }
|
|
|
+
|
|
|
+ /// map_mut
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn map<T, F>(self, f: F) -> Option<T>
|
|
|
+ where
|
|
|
+ F: FnOnce(&Value) -> T,
|
|
|
+ {
|
|
|
+ self.slot.get(self.key).filter(|x| x.is_valid()).map(|x| {
|
|
|
+ let value = x.get();
|
|
|
+ f(value.deref())
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /// map_mut
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn map_mut<T, F>(self, f: F) -> Option<T>
|
|
|
+ where
|
|
|
+ F: FnOnce(&mut Value) -> T,
|
|
|
+ {
|
|
|
+ self.slot.get(self.key).filter(|x| x.is_valid()).map(|x| {
|
|
|
+ let mut value = x.get_mut();
|
|
|
+ f(value.deref_mut())
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the version of a given key
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn version(&self) -> usize {
|
|
|
+ self.slot
|
|
|
+ .get(self.key)
|
|
|
+ .filter(|x| x.is_valid())
|
|
|
+ .map(|x| x.version())
|
|
|
+ .unwrap_or_default()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/// Database structure
|
|
|
///
|
|
|
/// Each connection has their own clone of the database and the conn_id is stored in each instance.
|
|
@@ -77,7 +150,7 @@ pub struct Db {
|
|
|
|
|
|
/// Databases unique ID. This is an internal identifier to avoid deadlocks
|
|
|
/// when copying and moving data between databases.
|
|
|
- pub db_id: u128,
|
|
|
+ pub db_id: usize,
|
|
|
|
|
|
/// Current connection ID
|
|
|
///
|
|
@@ -106,7 +179,7 @@ impl Db {
|
|
|
expirations: Arc::new(Mutex::new(ExpirationDb::new())),
|
|
|
change_subscriptions: Arc::new(RwLock::new(HashMap::new())),
|
|
|
conn_id: 0,
|
|
|
- db_id: new_version(),
|
|
|
+ db_id: unique_id(),
|
|
|
tx_key_locks: Arc::new(RwLock::new(HashMap::new())),
|
|
|
number_of_slots,
|
|
|
}
|
|
@@ -211,12 +284,10 @@ impl Db {
|
|
|
/// Return debug info for a key
|
|
|
pub fn debug(&self, key: &Bytes) -> Result<VDebug, Error> {
|
|
|
let slot = self.slots[self.get_slot(key)].read();
|
|
|
- Ok(slot
|
|
|
- .get(key)
|
|
|
+ slot.get(key)
|
|
|
.filter(|x| x.is_valid())
|
|
|
- .ok_or(Error::NotFound)?
|
|
|
- .value
|
|
|
- .debug())
|
|
|
+ .map(|x| x.get().debug())
|
|
|
+ .ok_or(Error::NotFound)
|
|
|
}
|
|
|
|
|
|
/// Return the digest for each key. This used for testing only
|
|
@@ -228,7 +299,7 @@ impl Db {
|
|
|
Value::new(
|
|
|
slot.get(key)
|
|
|
.filter(|v| v.is_valid())
|
|
|
- .map(|v| hex::encode(v.value.digest()))
|
|
|
+ .map(|v| hex::encode(v.digest()))
|
|
|
.unwrap_or("00000".into())
|
|
|
.as_bytes(),
|
|
|
)
|
|
@@ -315,35 +386,45 @@ impl Db {
|
|
|
+ Into<Value>
|
|
|
+ Copy,
|
|
|
{
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
+ let slot_id = self.get_slot(key);
|
|
|
+ let slot = self.slots[slot_id].read();
|
|
|
let mut incr_by: T =
|
|
|
bytes_to_number(incr_by).map_err(|_| Error::NotANumberType(typ.to_owned()))?;
|
|
|
- match slot.get_mut(key).filter(|x| x.is_valid()).map(|x| x.get()) {
|
|
|
- Some(Value::Hash(h)) => {
|
|
|
- let mut h = h.write();
|
|
|
- if let Some(n) = h.get(sub_key) {
|
|
|
- incr_by = incr_by
|
|
|
- .checked_add(
|
|
|
- &bytes_to_number(n)
|
|
|
- .map_err(|_| Error::NotANumberType(typ.to_owned()))?,
|
|
|
- )
|
|
|
- .ok_or(Error::Overflow)?;
|
|
|
- }
|
|
|
- let incr_by_bytes = Self::round_numbers(incr_by);
|
|
|
- h.insert(sub_key.clone(), incr_by_bytes.clone());
|
|
|
|
|
|
- Self::number_to_value(&incr_by_bytes)
|
|
|
- }
|
|
|
- None => {
|
|
|
- #[allow(clippy::mutable_key_type)]
|
|
|
- let mut h = HashMap::new();
|
|
|
- let incr_by_bytes = Self::round_numbers(incr_by);
|
|
|
- h.insert(sub_key.clone(), incr_by_bytes.clone());
|
|
|
- let _ = slot.insert(key.clone(), Entry::new(h.into(), None));
|
|
|
- Self::number_to_value(&incr_by_bytes)
|
|
|
- }
|
|
|
- _ => Err(Error::WrongType),
|
|
|
+ if let Some(x) = slot
|
|
|
+ .get(key)
|
|
|
+ .filter(|x| x.is_valid())
|
|
|
+ .map(|x| x.get_mut())
|
|
|
+ .map(|mut x| match x.deref_mut() {
|
|
|
+ Value::Hash(ref mut h) => {
|
|
|
+ if let Some(n) = h.get(sub_key) {
|
|
|
+ incr_by = incr_by
|
|
|
+ .checked_add(
|
|
|
+ &bytes_to_number(n)
|
|
|
+ .map_err(|_| Error::NotANumberType(typ.to_owned()))?,
|
|
|
+ )
|
|
|
+ .ok_or(Error::Overflow)?;
|
|
|
+ }
|
|
|
+ let incr_by_bytes = Self::round_numbers(incr_by);
|
|
|
+ h.insert(sub_key.clone(), incr_by_bytes.clone());
|
|
|
+
|
|
|
+ Self::number_to_value(&incr_by_bytes)
|
|
|
+ }
|
|
|
+ _ => Err(Error::WrongType),
|
|
|
+ })
|
|
|
+ {
|
|
|
+ return x;
|
|
|
}
|
|
|
+
|
|
|
+ drop(slot);
|
|
|
+ #[allow(clippy::mutable_key_type)]
|
|
|
+ let mut h = HashMap::new();
|
|
|
+ let incr_by_bytes = Self::round_numbers(incr_by);
|
|
|
+ h.insert(sub_key.clone(), incr_by_bytes.clone());
|
|
|
+ let _ = self.slots[slot_id]
|
|
|
+ .write()
|
|
|
+ .insert(key.clone(), Entry::new(h.into(), None));
|
|
|
+ Self::number_to_value(&incr_by_bytes)
|
|
|
}
|
|
|
|
|
|
/// Increments a key's value by a given number
|
|
@@ -354,35 +435,34 @@ impl Db {
|
|
|
where
|
|
|
T: ToString + CheckedAdd + for<'a> TryFrom<&'a Value, Error = Error> + Into<Value> + Copy,
|
|
|
{
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
- match slot.get_mut(key).filter(|x| x.is_valid()) {
|
|
|
- Some(x) => {
|
|
|
- if !x.is_clonable() {
|
|
|
- return Err(Error::WrongType);
|
|
|
- }
|
|
|
- let value = x.get();
|
|
|
- let mut number: T = value.try_into()?;
|
|
|
-
|
|
|
- number = incr_by.checked_add(&number).ok_or(Error::Overflow)?;
|
|
|
-
|
|
|
- x.change_value(Value::Blob(Self::round_numbers(number)));
|
|
|
+ let slot_id = self.get_slot(key);
|
|
|
+ let slot = self.slots[slot_id].read();
|
|
|
|
|
|
- Ok(number)
|
|
|
- }
|
|
|
- None => {
|
|
|
- slot.insert(
|
|
|
- key.clone(),
|
|
|
- Entry::new(Value::Blob(Self::round_numbers(incr_by)), None),
|
|
|
- );
|
|
|
- Ok(incr_by)
|
|
|
+ if let Some(entry) = slot.get(key).filter(|x| x.is_valid()) {
|
|
|
+ if !entry.is_scalar() {
|
|
|
+ return Err(Error::WrongType);
|
|
|
}
|
|
|
+ let mut value = entry.get_mut();
|
|
|
+ let mut number: T = (&*value).try_into()?;
|
|
|
+
|
|
|
+ number = incr_by.checked_add(&number).ok_or(Error::Overflow)?;
|
|
|
+ *value = Value::Blob(Self::round_numbers(number));
|
|
|
+ entry.bump_version();
|
|
|
+ Ok(number)
|
|
|
+ } else {
|
|
|
+ drop(slot);
|
|
|
+ self.slots[slot_id].write().insert(
|
|
|
+ key.clone(),
|
|
|
+ Entry::new(Value::Blob(Self::round_numbers(incr_by)), None),
|
|
|
+ );
|
|
|
+ Ok(incr_by)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Removes any expiration associated with a given key
|
|
|
pub fn persist(&self, key: &Bytes) -> Value {
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
- slot.get_mut(key)
|
|
|
+ let slot = self.slots[self.get_slot(key)].read();
|
|
|
+ slot.get(key)
|
|
|
.filter(|x| x.is_valid())
|
|
|
.map_or(0.into(), |x| {
|
|
|
if x.has_ttl() {
|
|
@@ -410,13 +490,13 @@ impl Db {
|
|
|
return Err(Error::OptsNotCompatible("GT and LT".to_owned()));
|
|
|
}
|
|
|
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
+ let slot = self.slots[self.get_slot(key)].read();
|
|
|
let expires_at = Instant::now()
|
|
|
.checked_add(expires_in)
|
|
|
.unwrap_or_else(far_future);
|
|
|
|
|
|
Ok(slot
|
|
|
- .get_mut(key)
|
|
|
+ .get(key)
|
|
|
.filter(|x| x.is_valid())
|
|
|
.map_or(0.into(), |x| {
|
|
|
let current_expire = x.get_ttl();
|
|
@@ -457,22 +537,20 @@ impl Db {
|
|
|
/// command will make sure it holds a string large enough to be able to set
|
|
|
/// value at offset.
|
|
|
pub fn set_range(&self, key: &Bytes, offset: i128, data: &[u8]) -> Result<Value, Error> {
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
-
|
|
|
- if let Some(entry) = slot.get_mut(key).filter(|x| x.is_valid()) {
|
|
|
- if let Value::Blob(data) = entry.get() {
|
|
|
- let rw_data = BytesMut::from(&data[..]);
|
|
|
- entry.change_value(Value::BlobRw(rw_data));
|
|
|
- }
|
|
|
- }
|
|
|
+ let slot_id = self.get_slot(key);
|
|
|
+ let slot = self.slots[slot_id].read();
|
|
|
|
|
|
- let value = slot.get_mut(key).map(|value| {
|
|
|
- if !value.is_valid() {
|
|
|
- self.expirations.lock().remove(key);
|
|
|
- value.persist();
|
|
|
- }
|
|
|
- value.get_mut()
|
|
|
- });
|
|
|
+ let mut value = slot
|
|
|
+ .get(key)
|
|
|
+ .map(|value| {
|
|
|
+ value.ensure_blob_is_mutable()?;
|
|
|
+ if !value.is_valid() {
|
|
|
+ self.expirations.lock().remove(key);
|
|
|
+ value.persist();
|
|
|
+ }
|
|
|
+ Ok::<_, Error>(value.get_mut())
|
|
|
+ })
|
|
|
+ .transpose()?;
|
|
|
|
|
|
if offset < 0 {
|
|
|
return Err(Error::OutOfRange);
|
|
@@ -483,27 +561,32 @@ impl Db {
|
|
|
}
|
|
|
|
|
|
let length = offset as usize + data.len();
|
|
|
- match value {
|
|
|
- Some(Value::BlobRw(bytes)) => {
|
|
|
- if bytes.capacity() < length {
|
|
|
- bytes.resize(length, 0);
|
|
|
+ if let Some(value) = value.as_mut() {
|
|
|
+ match value.deref_mut() {
|
|
|
+ Value::BlobRw(ref mut bytes) => {
|
|
|
+ if bytes.capacity() < length {
|
|
|
+ bytes.resize(length, 0);
|
|
|
+ }
|
|
|
+ let writer = &mut bytes[offset as usize..length];
|
|
|
+ writer.copy_from_slice(data);
|
|
|
+ Ok(bytes.len().into())
|
|
|
}
|
|
|
- let writer = &mut bytes[offset as usize..length];
|
|
|
- writer.copy_from_slice(data);
|
|
|
- Ok(bytes.len().into())
|
|
|
+ _ => Err(Error::WrongType),
|
|
|
}
|
|
|
- None => {
|
|
|
- if data.is_empty() {
|
|
|
- return Ok(0.into());
|
|
|
- }
|
|
|
- let mut bytes = BytesMut::new();
|
|
|
- bytes.resize(length, 0);
|
|
|
- let writer = &mut bytes[offset as usize..];
|
|
|
- writer.copy_from_slice(data);
|
|
|
- slot.insert(key.clone(), Entry::new(Value::new(&bytes), None));
|
|
|
- Ok(bytes.len().into())
|
|
|
+ } else {
|
|
|
+ drop(value);
|
|
|
+ drop(slot);
|
|
|
+ if data.is_empty() {
|
|
|
+ return Ok(0.into());
|
|
|
}
|
|
|
- _ => Err(Error::WrongType),
|
|
|
+ let mut bytes = BytesMut::new();
|
|
|
+ bytes.resize(length, 0);
|
|
|
+ let writer = &mut bytes[offset as usize..];
|
|
|
+ writer.copy_from_slice(data);
|
|
|
+ self.slots[slot_id]
|
|
|
+ .write()
|
|
|
+ .insert(key.clone(), Entry::new(Value::new(&bytes), None));
|
|
|
+ Ok(bytes.len().into())
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -530,14 +613,9 @@ impl Db {
|
|
|
if replace == Override::No && db.exists(&[target.clone()]) > 0 {
|
|
|
return Ok(false);
|
|
|
}
|
|
|
- let _ = db.set_advanced(
|
|
|
- target,
|
|
|
- value.value.clone(),
|
|
|
- value.get_ttl().map(|v| v - Instant::now()),
|
|
|
- replace,
|
|
|
- false,
|
|
|
- false,
|
|
|
- );
|
|
|
+
|
|
|
+ let ttl = value.get_ttl().map(|v| v - Instant::now());
|
|
|
+ let _ = db.set_advanced(target, value.take_value(), ttl, replace, false, false);
|
|
|
Ok(true)
|
|
|
} else {
|
|
|
if source == target {
|
|
@@ -563,7 +641,7 @@ impl Db {
|
|
|
let (expires_in, value) = if let Some(value) = slot.get(&source).filter(|v| v.is_valid()) {
|
|
|
(
|
|
|
value.get_ttl().map(|t| t - Instant::now()),
|
|
|
- value.value.clone(),
|
|
|
+ value.get().clone(),
|
|
|
)
|
|
|
} else {
|
|
|
return Ok(false);
|
|
@@ -702,39 +780,11 @@ impl Db {
|
|
|
matches
|
|
|
}
|
|
|
|
|
|
- /// get_map_or
|
|
|
- ///
|
|
|
- /// Instead of returning an entry of the database, to avoid cloning, this function will
|
|
|
- /// execute a callback function with the entry as a parameter. If no record is found another
|
|
|
- /// callback function is going to be executed, dropping the lock before doing so.
|
|
|
- ///
|
|
|
- /// If an entry is found, the lock is not dropped before doing the callback. Avoid inserting
|
|
|
- /// new entries. In this case the value is passed by reference, so it is possible to modify the
|
|
|
- /// entry itself.
|
|
|
- ///
|
|
|
- /// This function is useful to read non-scalar values from the database. Non-scalar values are
|
|
|
- /// forbidden to clone, attempting cloning will end-up in an error (Error::WrongType)
|
|
|
- pub fn get_map<F1>(&self, key: &Bytes, found: F1) -> Result<Value, Error>
|
|
|
- where
|
|
|
- F1: FnOnce(Option<&Value>) -> Result<Value, Error>,
|
|
|
- {
|
|
|
- let slot = self.slots[self.get_slot(key)].read();
|
|
|
- let entry = slot.get(key).filter(|x| x.is_valid()).map(|e| e.get());
|
|
|
-
|
|
|
- if let Some(entry) = entry {
|
|
|
- found(Some(entry))
|
|
|
- } else {
|
|
|
- // drop lock
|
|
|
- drop(slot);
|
|
|
- found(None)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/// Updates the entry version of a given key
|
|
|
pub fn bump_version(&self, key: &Bytes) -> bool {
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
+ let slot = self.slots[self.get_slot(key)].read();
|
|
|
let to_return = slot
|
|
|
- .get_mut(key)
|
|
|
+ .get(key)
|
|
|
.filter(|x| x.is_valid())
|
|
|
.map(|entry| {
|
|
|
entry.bump_version();
|
|
@@ -773,38 +823,28 @@ impl Db {
|
|
|
.collect()
|
|
|
}
|
|
|
|
|
|
- /// Returns the version of a given key
|
|
|
- #[inline]
|
|
|
- pub fn get_version(&self, key: &Bytes) -> u128 {
|
|
|
- let slot = self.slots[self.get_slot(key)].read();
|
|
|
- slot.get(key)
|
|
|
- .filter(|x| x.is_valid())
|
|
|
- .map(|entry| entry.version())
|
|
|
- .unwrap_or_default()
|
|
|
- }
|
|
|
-
|
|
|
/// Returns the name of the value type
|
|
|
pub fn get_data_type(&self, key: &Bytes) -> String {
|
|
|
let slot = self.slots[self.get_slot(key)].read();
|
|
|
slot.get(key)
|
|
|
.filter(|x| x.is_valid())
|
|
|
.map_or("none".to_owned(), |x| {
|
|
|
- Typ::get_type(x.get()).to_string().to_lowercase()
|
|
|
+ Typ::get_type(&x.get()).to_string().to_lowercase()
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- /// Get a copy of an entry
|
|
|
- pub fn get(&self, key: &Bytes) -> Value {
|
|
|
- let slot = self.slots[self.get_slot(key)].read();
|
|
|
- slot.get(key)
|
|
|
- .filter(|x| x.is_valid())
|
|
|
- .map_or(Value::Null, |x| x.clone_value())
|
|
|
+ /// Get a ref value
|
|
|
+ pub fn get<'a>(&'a self, key: &'a Bytes) -> RefValue<'a> {
|
|
|
+ RefValue {
|
|
|
+ slot: self.slots[self.get_slot(key)].read(),
|
|
|
+ key,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// Get a copy of an entry and modifies the expiration of the key
|
|
|
pub fn getex(&self, key: &Bytes, expires_in: Option<Duration>, make_persistent: bool) -> Value {
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
- slot.get_mut(key)
|
|
|
+ let slot = self.slots[self.get_slot(key)].read();
|
|
|
+ slot.get(key)
|
|
|
.filter(|x| x.is_valid())
|
|
|
.map(|value| {
|
|
|
if make_persistent {
|
|
@@ -828,7 +868,7 @@ impl Db {
|
|
|
.map(|key| {
|
|
|
let slot = self.slots[self.get_slot(key)].read();
|
|
|
slot.get(key)
|
|
|
- .filter(|x| x.is_valid() && x.is_clonable())
|
|
|
+ .filter(|x| x.is_valid() && x.is_scalar())
|
|
|
.map_or(Value::Null, |x| x.clone_value())
|
|
|
})
|
|
|
.collect::<Vec<Value>>()
|
|
@@ -855,21 +895,20 @@ impl Db {
|
|
|
|
|
|
/// Set a key, value with an optional expiration time
|
|
|
pub fn append(&self, key: &Bytes, value_to_append: &Bytes) -> Result<Value, Error> {
|
|
|
- let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
+ let slot = self.slots[self.get_slot(key)].read();
|
|
|
|
|
|
- if let Some(entry) = slot.get_mut(key).filter(|x| x.is_valid()) {
|
|
|
- if let Value::Blob(data) = entry.get() {
|
|
|
- let rw_data = BytesMut::from(&data[..]);
|
|
|
- entry.change_value(Value::BlobRw(rw_data));
|
|
|
- }
|
|
|
- match entry.get_mut() {
|
|
|
- Value::BlobRw(value) => {
|
|
|
+ if let Some(entry) = slot.get(key).filter(|x| x.is_valid()) {
|
|
|
+ entry.ensure_blob_is_mutable()?;
|
|
|
+ match *entry.get_mut() {
|
|
|
+ Value::BlobRw(ref mut value) => {
|
|
|
value.put(value_to_append.as_ref());
|
|
|
Ok(value.len().into())
|
|
|
}
|
|
|
_ => Err(Error::WrongType),
|
|
|
}
|
|
|
} else {
|
|
|
+ drop(slot);
|
|
|
+ let mut slot = self.slots[self.get_slot(key)].write();
|
|
|
slot.insert(key.clone(), Entry::new(Value::new(value_to_append), None));
|
|
|
Ok(value_to_append.len().into())
|
|
|
}
|
|
@@ -1105,7 +1144,7 @@ impl scan::Scan for Db {
|
|
|
}
|
|
|
}
|
|
|
if let Some(typ) = &typ {
|
|
|
- if !typ.is_value_type(value.get()) {
|
|
|
+ if !typ.is_value_type(&value.get()) {
|
|
|
last_pos += 1;
|
|
|
continue;
|
|
|
}
|
|
@@ -1147,7 +1186,10 @@ mod test {
|
|
|
|
|
|
assert!(r.is_err());
|
|
|
assert_eq!(Error::NotANumber, r.expect_err("should fail"));
|
|
|
- assert_eq!(Value::Blob(bytes!("some string")), db.get(&bytes!("num")));
|
|
|
+ assert_eq!(
|
|
|
+ Value::Blob(bytes!("some string")),
|
|
|
+ db.get(&bytes!("num")).into_inner()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -1156,7 +1198,10 @@ mod test {
|
|
|
db.set(bytes!(b"num"), Value::Blob(bytes!("1.1")), None);
|
|
|
|
|
|
assert_eq!(Ok(2.2.into()), db.incr::<Float>(&bytes!("num"), 1.1.into()));
|
|
|
- assert_eq!(Value::Blob(bytes!("2.2")), db.get(&bytes!("num")));
|
|
|
+ assert_eq!(
|
|
|
+ Value::Blob(bytes!("2.2")),
|
|
|
+ db.get(&bytes!("num")).into_inner()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -1165,7 +1210,10 @@ mod test {
|
|
|
db.set(bytes!(b"num"), Value::Blob(bytes!("1")), None);
|
|
|
|
|
|
assert_eq!(Ok(2.1.into()), db.incr::<Float>(&bytes!("num"), 1.1.into()));
|
|
|
- assert_eq!(Value::Blob(bytes!("2.1")), db.get(&bytes!("num")));
|
|
|
+ assert_eq!(
|
|
|
+ Value::Blob(bytes!("2.1")),
|
|
|
+ db.get(&bytes!("num")).into_inner()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -1174,21 +1222,30 @@ mod test {
|
|
|
db.set(bytes!(b"num"), Value::Blob(bytes!("1")), None);
|
|
|
|
|
|
assert_eq!(Ok(2), db.incr(&bytes!("num"), 1));
|
|
|
- assert_eq!(Value::Blob(bytes!("2")), db.get(&bytes!("num")));
|
|
|
+ assert_eq!(
|
|
|
+ Value::Blob(bytes!("2")),
|
|
|
+ db.get(&bytes!("num")).into_inner()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn incr_blob_int_set() {
|
|
|
let db = Db::new(100);
|
|
|
assert_eq!(Ok(1), db.incr(&bytes!("num"), 1));
|
|
|
- assert_eq!(Value::Blob(bytes!("1")), db.get(&bytes!("num")));
|
|
|
+ assert_eq!(
|
|
|
+ Value::Blob(bytes!("1")),
|
|
|
+ db.get(&bytes!("num")).into_inner()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn incr_blob_float_set() {
|
|
|
let db = Db::new(100);
|
|
|
assert_eq!(Ok(1.1.into()), db.incr::<Float>(&bytes!("num"), 1.1.into()));
|
|
|
- assert_eq!(Value::Blob(bytes!("1.1")), db.get(&bytes!("num")));
|
|
|
+ assert_eq!(
|
|
|
+ Value::Blob(bytes!("1.1")),
|
|
|
+ db.get(&bytes!("num")).into_inner()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -1226,7 +1283,7 @@ mod test {
|
|
|
fn persist_bug() {
|
|
|
let db = Db::new(100);
|
|
|
db.set(bytes!(b"one"), Value::Ok, Some(Duration::from_secs(1)));
|
|
|
- assert_eq!(Value::Ok, db.get(&bytes!(b"one")));
|
|
|
+ assert_eq!(Value::Ok, db.get(&bytes!(b"one")).into_inner());
|
|
|
assert!(db.is_key_in_expiration_list(&bytes!(b"one")));
|
|
|
db.persist(&bytes!(b"one"));
|
|
|
assert!(!db.is_key_in_expiration_list(&bytes!(b"one")));
|
|
@@ -1238,13 +1295,13 @@ mod test {
|
|
|
db.set(bytes!(b"one"), Value::Ok, Some(Duration::from_secs(0)));
|
|
|
// Expired keys should not be returned, even if they are not yet
|
|
|
// removed by the purge process.
|
|
|
- assert_eq!(Value::Null, db.get(&bytes!(b"one")));
|
|
|
+ assert_eq!(Value::Null, db.get(&bytes!(b"one")).into_inner());
|
|
|
|
|
|
// Purge twice
|
|
|
assert_eq!(1, db.purge());
|
|
|
assert_eq!(0, db.purge());
|
|
|
|
|
|
- assert_eq!(Value::Null, db.get(&bytes!(b"one")));
|
|
|
+ assert_eq!(Value::Null, db.get(&bytes!(b"one")).into_inner());
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -1253,10 +1310,10 @@ mod test {
|
|
|
db.set(bytes!(b"one"), Value::Ok, Some(Duration::from_secs(0)));
|
|
|
// Expired keys should not be returned, even if they are not yet
|
|
|
// removed by the purge process.
|
|
|
- assert_eq!(Value::Null, db.get(&bytes!(b"one")));
|
|
|
+ assert_eq!(Value::Null, db.get(&bytes!(b"one")).into_inner());
|
|
|
|
|
|
db.set(bytes!(b"one"), Value::Ok, Some(Duration::from_secs(5)));
|
|
|
- assert_eq!(Value::Ok, db.get(&bytes!(b"one")));
|
|
|
+ assert_eq!(Value::Ok, db.get(&bytes!(b"one")).into_inner());
|
|
|
|
|
|
// Purge should return 0 as the expired key has been removed already
|
|
|
assert_eq!(0, db.purge());
|