mirror of https://github.com/tikv/client-rust.git
Add proptest support (#68)
* Add proptest support Signed-off-by: Ana Hobden <operator@hoverbear.org> * fmt Signed-off-by: Ana Hobden <operator@hoverbear.org> * Make proptest feature more consistent Signed-off-by: Ana Hobden <operator@hoverbear.org> * Fixes #69 Signed-off-by: Ana Hobden <operator@hoverbear.org> * fmt Signed-off-by: Ana Hobden <operator@hoverbear.org>
This commit is contained in:
parent
75eae033a7
commit
1815a3bf54
|
@ -43,3 +43,5 @@ clap = "2.32"
|
||||||
tempdir = "0.3"
|
tempdir = "0.3"
|
||||||
runtime = "0.3.0-alpha.3"
|
runtime = "0.3.0-alpha.3"
|
||||||
runtime-tokio = "0.3.0-alpha.3"
|
runtime-tokio = "0.3.0-alpha.3"
|
||||||
|
proptest = "0.9"
|
||||||
|
proptest-derive = "0.1.0"
|
489
src/kv.rs
489
src/kv.rs
|
@ -1,489 +0,0 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
|
||||||
|
|
||||||
use derive_new::new;
|
|
||||||
use std::cmp::{Eq, PartialEq};
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::ops::{Bound, Range, RangeFrom, RangeInclusive};
|
|
||||||
use std::{fmt, str, u8};
|
|
||||||
|
|
||||||
use crate::{Error, Result};
|
|
||||||
|
|
||||||
struct HexRepr<'a>(pub &'a [u8]);
|
|
||||||
|
|
||||||
impl<'a> fmt::Display for HexRepr<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
for byte in self.0 {
|
|
||||||
write!(f, "{:02X}", byte)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The key part of a key/value pair.
|
|
||||||
///
|
|
||||||
/// In TiKV, keys are an ordered sequence of bytes. This has an advantage over choosing `String` as
|
|
||||||
/// valid `UTF-8` is not required. This means that the user is permitted to store any data they wish,
|
|
||||||
/// as long as it can be represented by bytes. (Which is to say, pretty much anything!)
|
|
||||||
///
|
|
||||||
/// This type also implements `From` for many types. With one exception, these are all done without
|
|
||||||
/// reallocation. Using a `&'static str`, like many examples do for simplicity, has an internal
|
|
||||||
/// allocation cost.
|
|
||||||
///
|
|
||||||
/// This type wraps around an owned value, so it should be treated it like `String` or `Vec<u8>`
|
|
||||||
/// over a `&str` or `&[u8]`.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use tikv_client::Key;
|
|
||||||
///
|
|
||||||
/// let static_str: &'static str = "TiKV";
|
|
||||||
/// let from_static_str = Key::from(static_str);
|
|
||||||
///
|
|
||||||
/// let string: String = String::from(static_str);
|
|
||||||
/// let from_string = Key::from(string);
|
|
||||||
/// assert_eq!(from_static_str, from_string);
|
|
||||||
///
|
|
||||||
/// let vec: Vec<u8> = static_str.as_bytes().to_vec();
|
|
||||||
/// let from_vec = Key::from(vec);
|
|
||||||
/// assert_eq!(from_static_str, from_vec);
|
|
||||||
///
|
|
||||||
/// let bytes = static_str.as_bytes().to_vec();
|
|
||||||
/// let from_bytes = Key::from(bytes);
|
|
||||||
/// assert_eq!(from_static_str, from_bytes);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// **But, you should not need to worry about all this:** Many functions which accept a `Key`
|
|
||||||
/// accept an `Into<Key>`, which means all of the above types can be passed directly to those
|
|
||||||
/// functions.
|
|
||||||
#[derive(new, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
||||||
pub struct Key(Vec<u8>);
|
|
||||||
|
|
||||||
impl Key {
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn zero_terminated(&self) -> bool {
|
|
||||||
self.0.last().map(|i| *i == 0).unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn push_zero(&mut self) {
|
|
||||||
self.0.push(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_lower_bound(mut self) -> Bound<Key> {
|
|
||||||
if self.zero_terminated() {
|
|
||||||
self.0.pop().unwrap();
|
|
||||||
Bound::Excluded(self)
|
|
||||||
} else {
|
|
||||||
Bound::Included(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_upper_bound(mut self) -> Bound<Key> {
|
|
||||||
if self.zero_terminated() {
|
|
||||||
self.0.pop().unwrap();
|
|
||||||
Bound::Included(self)
|
|
||||||
} else {
|
|
||||||
Bound::Excluded(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for Key {
|
|
||||||
fn from(v: Vec<u8>) -> Self {
|
|
||||||
Key(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Key {
|
|
||||||
fn from(v: String) -> Key {
|
|
||||||
Key(v.into_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&'static str> for Key {
|
|
||||||
fn from(v: &'static str) -> Key {
|
|
||||||
Key(v.as_bytes().to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Vec<u8>> for Key {
|
|
||||||
fn into(self) -> Vec<u8> {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Into<&'a [u8]> for &'a Key {
|
|
||||||
fn into(self) -> &'a [u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Key {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Key({})", HexRepr(&self.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The value part of a key/value pair.
|
|
||||||
///
|
|
||||||
/// In TiKV, values are an ordered sequence of bytes. This has an advantage over choosing `String`
|
|
||||||
/// as valid `UTF-8` is not required. This means that the user is permitted to store any data they wish,
|
|
||||||
/// as long as it can be represented by bytes. (Which is to say, pretty much anything!)
|
|
||||||
///
|
|
||||||
/// This type also implements `From` for many types. With one exception, these are all done without
|
|
||||||
/// reallocation. Using a `&'static str`, like many examples do for simplicity, has an internal
|
|
||||||
/// allocation cost.
|
|
||||||
///
|
|
||||||
/// This type wraps around an owned value, so it should be treated it like `String` or `Vec<u8>`
|
|
||||||
/// over a `&str` or `&[u8]`.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use tikv_client::Value;
|
|
||||||
///
|
|
||||||
/// let static_str: &'static str = "TiKV";
|
|
||||||
/// let from_static_str = Value::from(static_str);
|
|
||||||
///
|
|
||||||
/// let string: String = String::from(static_str);
|
|
||||||
/// let from_string = Value::from(string);
|
|
||||||
/// assert_eq!(from_static_str, from_string);
|
|
||||||
///
|
|
||||||
/// let vec: Vec<u8> = static_str.as_bytes().to_vec();
|
|
||||||
/// let from_vec = Value::from(vec);
|
|
||||||
/// assert_eq!(from_static_str, from_vec);
|
|
||||||
///
|
|
||||||
/// let bytes = static_str.as_bytes().to_vec();
|
|
||||||
/// let from_bytes = Value::from(bytes);
|
|
||||||
/// assert_eq!(from_static_str, from_bytes);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// **But, you should not need to worry about all this:** Many functions which accept a `Value`
|
|
||||||
/// accept an `Into<Value>`, which means all of the above types can be passed directly to those
|
|
||||||
/// functions.
|
|
||||||
#[derive(new, Default, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct Value(Vec<u8>);
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for Value {
|
|
||||||
fn from(v: Vec<u8>) -> Self {
|
|
||||||
Value(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Value {
|
|
||||||
fn from(v: String) -> Value {
|
|
||||||
Value(v.into_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&'static str> for Value {
|
|
||||||
fn from(v: &'static str) -> Value {
|
|
||||||
Value(v.as_bytes().to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Vec<u8>> for Value {
|
|
||||||
fn into(self) -> Vec<u8> {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Into<&'a [u8]> for &'a Value {
|
|
||||||
fn into(self) -> &'a [u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Value {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match str::from_utf8(&self.0) {
|
|
||||||
Ok(s) => write!(f, "Value({:?})", s),
|
|
||||||
Err(_) => write!(f, "Value({})", HexRepr(&self.0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A key/value pair.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use tikv_client::{Key, Value, KvPair};
|
|
||||||
/// let key = "key";
|
|
||||||
/// let value = "value";
|
|
||||||
/// let constructed = KvPair::new(key, value);
|
|
||||||
/// let from_tuple = KvPair::from((key, value));
|
|
||||||
/// assert_eq!(constructed, from_tuple);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Many functions which accept a `KvPair` accept an `Into<KvPair>`, which means all of the above
|
|
||||||
/// types (Like a `(Key, Value)`) can be passed directly to those functions.
|
|
||||||
#[derive(Default, Clone, Eq, PartialEq)]
|
|
||||||
pub struct KvPair(Key, Value);
|
|
||||||
|
|
||||||
impl KvPair {
|
|
||||||
/// Create a new `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(key: impl Into<Key>, value: impl Into<Value>) -> Self {
|
|
||||||
KvPair(key.into(), value.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Immutably borrow the `Key` part of the `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn key(&self) -> &Key {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Immutably borrow the `Value` part of the `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn value(&self) -> &Value {
|
|
||||||
&self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn into_key(self) -> Key {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn into_value(self) -> Value {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably borrow the `Key` part of the `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn key_mut(&mut self) -> &mut Key {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably borrow the `Value` part of the `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn value_mut(&mut self) -> &mut Value {
|
|
||||||
&mut self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the `Key` part of the `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_key(&mut self, k: impl Into<Key>) {
|
|
||||||
self.0 = k.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the `Value` part of the `KvPair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_value(&mut self, v: impl Into<Value>) {
|
|
||||||
self.1 = v.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> From<(K, V)> for KvPair
|
|
||||||
where
|
|
||||||
K: Into<Key>,
|
|
||||||
V: Into<Value>,
|
|
||||||
{
|
|
||||||
fn from((k, v): (K, V)) -> Self {
|
|
||||||
KvPair(k.into(), v.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<(Key, Value)> for KvPair {
|
|
||||||
fn into(self) -> (Key, Value) {
|
|
||||||
(self.0, self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for KvPair {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let KvPair(key, value) = self;
|
|
||||||
match str::from_utf8(&value.0) {
|
|
||||||
Ok(s) => write!(f, "KvPair({}, {:?})", HexRepr(&key.0), s),
|
|
||||||
Err(_) => write!(f, "KvPair({}, {})", HexRepr(&key.0), HexRepr(&value.0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A struct for expressing ranges. This type is semi-opaque and is not really meant for users to
|
|
||||||
/// deal with directly. Most functions which operate on ranges will accept any types which
|
|
||||||
/// implement `Into<BoundRange>`.
|
|
||||||
///
|
|
||||||
/// In TiKV, keys are an ordered sequence of bytes. This means we can have ranges over those
|
|
||||||
/// bytes. Eg `001` is before `010`.
|
|
||||||
///
|
|
||||||
/// `Into<BoundRange>` has implementations for common range types like `a..b`, `a..=b` where `a` and `b`
|
|
||||||
/// `impl Into<Key>`. You can implement `Into<BoundRange>` for your own types by using `try_from`.
|
|
||||||
///
|
|
||||||
/// Invariant: a range may not be unbounded below.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use tikv_client::{BoundRange, Key};
|
|
||||||
/// use std::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive, RangeFrom, RangeFull, Bound};
|
|
||||||
/// # use std::convert::TryInto;
|
|
||||||
///
|
|
||||||
/// let explict_range: Range<Key> = Range { start: Key::from("Rust"), end: Key::from("TiKV") };
|
|
||||||
/// let from_explict_range: BoundRange = explict_range.into();
|
|
||||||
///
|
|
||||||
/// let range: Range<&str> = "Rust".."TiKV";
|
|
||||||
/// let from_range: BoundRange = range.into();
|
|
||||||
/// assert_eq!(from_explict_range, from_range);
|
|
||||||
///
|
|
||||||
/// let range: RangeInclusive<&str> = "Rust"..="TiKV";
|
|
||||||
/// let from_range: BoundRange = range.into();
|
|
||||||
/// assert_eq!(
|
|
||||||
/// from_range,
|
|
||||||
/// (Bound::Included(Key::from("Rust")), Bound::Included(Key::from("TiKV"))),
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// let range_from: RangeFrom<&str> = "Rust"..;
|
|
||||||
/// let from_range_from: BoundRange = range_from.into();
|
|
||||||
/// assert_eq!(
|
|
||||||
/// from_range_from,
|
|
||||||
/// (Bound::Included(Key::from("Rust")), Bound::Unbounded),
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// **But, you should not need to worry about all this:** Most functions which operate
|
|
||||||
/// on ranges will accept any types which implement `Into<BoundRange>`.
|
|
||||||
/// which means all of the above types can be passed directly to those functions.
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct BoundRange {
|
|
||||||
from: Bound<Key>,
|
|
||||||
to: Bound<Key>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoundRange {
|
|
||||||
/// Create a new BoundRange.
|
|
||||||
///
|
|
||||||
/// The caller must ensure that `from` is not `Unbounded`.
|
|
||||||
fn new(from: Bound<Key>, to: Bound<Key>) -> BoundRange {
|
|
||||||
// Debug assert because this function is private.
|
|
||||||
debug_assert!(from != Bound::Unbounded);
|
|
||||||
BoundRange { from, to }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ranges used in scanning TiKV have a particularity to them.
|
|
||||||
///
|
|
||||||
/// The **start** of a scan is inclusive, unless appended with an '\0', then it is exclusive.
|
|
||||||
///
|
|
||||||
/// The **end** of a scan is exclusive, unless appended with an '\0', then it is inclusive.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use tikv_client::{BoundRange, Key};
|
|
||||||
/// // Exclusive
|
|
||||||
/// let range = "a".."z";
|
|
||||||
/// assert_eq!(
|
|
||||||
/// BoundRange::from(range).into_keys(),
|
|
||||||
/// (Key::from("a"), Some(Key::from("z"))),
|
|
||||||
/// );
|
|
||||||
/// // Inclusive
|
|
||||||
/// let range = "a"..="z";
|
|
||||||
/// assert_eq!(
|
|
||||||
/// BoundRange::from(range).into_keys(),
|
|
||||||
/// (Key::from("a"), Some(Key::from("z\0"))),
|
|
||||||
/// );
|
|
||||||
/// // Open
|
|
||||||
/// let range = "a"..;
|
|
||||||
/// assert_eq!(
|
|
||||||
/// BoundRange::from(range).into_keys(),
|
|
||||||
/// (Key::from("a"), None),
|
|
||||||
/// );
|
|
||||||
// ```
|
|
||||||
pub fn into_keys(self) -> (Key, Option<Key>) {
|
|
||||||
let start = match self.from {
|
|
||||||
Bound::Included(v) => v,
|
|
||||||
Bound::Excluded(mut v) => {
|
|
||||||
v.push_zero();
|
|
||||||
v
|
|
||||||
}
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
let end = match self.to {
|
|
||||||
Bound::Included(mut v) => {
|
|
||||||
v.push_zero();
|
|
||||||
Some(v)
|
|
||||||
}
|
|
||||||
Bound::Excluded(v) => Some(v),
|
|
||||||
Bound::Unbounded => None,
|
|
||||||
};
|
|
||||||
(start, end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key> + Clone> PartialEq<(Bound<T>, Bound<T>)> for BoundRange {
|
|
||||||
fn eq(&self, other: &(Bound<T>, Bound<T>)) -> bool {
|
|
||||||
self.from == convert_to_bound_key(other.0.clone())
|
|
||||||
&& self.to == convert_to_bound_key(other.1.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key>> From<Range<T>> for BoundRange {
|
|
||||||
fn from(other: Range<T>) -> BoundRange {
|
|
||||||
BoundRange::new(
|
|
||||||
Bound::Included(other.start.into()),
|
|
||||||
Bound::Excluded(other.end.into()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key>> From<RangeFrom<T>> for BoundRange {
|
|
||||||
fn from(other: RangeFrom<T>) -> BoundRange {
|
|
||||||
BoundRange::new(Bound::Included(other.start.into()), Bound::Unbounded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key>> From<RangeInclusive<T>> for BoundRange {
|
|
||||||
fn from(other: RangeInclusive<T>) -> BoundRange {
|
|
||||||
let (start, end) = other.into_inner();
|
|
||||||
BoundRange::new(Bound::Included(start.into()), Bound::Included(end.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key>> From<(T, Option<T>)> for BoundRange {
|
|
||||||
fn from(other: (T, Option<T>)) -> BoundRange {
|
|
||||||
let to = match other.1 {
|
|
||||||
None => Bound::Unbounded,
|
|
||||||
Some(to) => to.into().into_upper_bound(),
|
|
||||||
};
|
|
||||||
|
|
||||||
BoundRange::new(other.0.into().into_lower_bound(), to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key>> From<(T, T)> for BoundRange {
|
|
||||||
fn from(other: (T, T)) -> BoundRange {
|
|
||||||
BoundRange::new(
|
|
||||||
other.0.into().into_lower_bound(),
|
|
||||||
other.1.into().into_upper_bound(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Key> + Eq> TryFrom<(Bound<T>, Bound<T>)> for BoundRange {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(bounds: (Bound<T>, Bound<T>)) -> Result<BoundRange> {
|
|
||||||
if bounds.0 == Bound::Unbounded {
|
|
||||||
Err(Error::invalid_key_range())
|
|
||||||
} else {
|
|
||||||
Ok(BoundRange::new(
|
|
||||||
convert_to_bound_key(bounds.0),
|
|
||||||
convert_to_bound_key(bounds.1),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_to_bound_key<K: Into<Key>>(b: Bound<K>) -> Bound<Key> {
|
|
||||||
match b {
|
|
||||||
Bound::Included(k) => Bound::Included(k.into()),
|
|
||||||
Bound::Excluded(k) => Bound::Excluded(k.into()),
|
|
||||||
Bound::Unbounded => Bound::Unbounded,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
|
use super::Key;
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest_derive::Arbitrary;
|
||||||
|
use std::cmp::{Eq, PartialEq};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::ops::{Bound, Range, RangeFrom, RangeInclusive};
|
||||||
|
|
||||||
|
use crate::{Error, Result};
|
||||||
|
|
||||||
|
/// A struct for expressing ranges. This type is semi-opaque and is not really meant for users to
|
||||||
|
/// deal with directly. Most functions which operate on ranges will accept any types which
|
||||||
|
/// implement `Into<BoundRange>`.
|
||||||
|
///
|
||||||
|
/// In TiKV, keys are an ordered sequence of bytes. This means we can have ranges over those
|
||||||
|
/// bytes. Eg `001` is before `010`.
|
||||||
|
///
|
||||||
|
/// `Into<BoundRange>` has implementations for common range types like `a..b`, `a..=b` where `a` and `b`
|
||||||
|
/// `impl Into<Key>`. You can implement `Into<BoundRange>` for your own types by using `try_from`.
|
||||||
|
///
|
||||||
|
/// Invariant: a range may not be unbounded below.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use tikv_client::{BoundRange, Key};
|
||||||
|
/// use std::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive, RangeFrom, RangeFull, Bound};
|
||||||
|
/// # use std::convert::TryInto;
|
||||||
|
///
|
||||||
|
/// let explict_range: Range<Key> = Range { start: Key::from("Rust"), end: Key::from("TiKV") };
|
||||||
|
/// let from_explict_range: BoundRange = explict_range.into();
|
||||||
|
///
|
||||||
|
/// let range: Range<&str> = "Rust".."TiKV";
|
||||||
|
/// let from_range: BoundRange = range.into();
|
||||||
|
/// assert_eq!(from_explict_range, from_range);
|
||||||
|
///
|
||||||
|
/// let range: RangeInclusive<&str> = "Rust"..="TiKV";
|
||||||
|
/// let from_range: BoundRange = range.into();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// from_range,
|
||||||
|
/// (Bound::Included(Key::from("Rust")), Bound::Included(Key::from("TiKV"))),
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let range_from: RangeFrom<&str> = "Rust"..;
|
||||||
|
/// let from_range_from: BoundRange = range_from.into();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// from_range_from,
|
||||||
|
/// (Bound::Included(Key::from("Rust")), Bound::Unbounded),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// **But, you should not need to worry about all this:** Most functions which operate
|
||||||
|
/// on ranges will accept any types which implement `Into<BoundRange>`.
|
||||||
|
/// which means all of the above types can be passed directly to those functions.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
pub struct BoundRange {
|
||||||
|
from: Bound<Key>,
|
||||||
|
to: Bound<Key>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundRange {
|
||||||
|
/// Create a new BoundRange.
|
||||||
|
///
|
||||||
|
/// The caller must ensure that `from` is not `Unbounded`.
|
||||||
|
fn new(from: Bound<Key>, to: Bound<Key>) -> BoundRange {
|
||||||
|
// Debug assert because this function is private.
|
||||||
|
debug_assert!(from != Bound::Unbounded);
|
||||||
|
BoundRange { from, to }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ranges used in scanning TiKV have a particularity to them.
|
||||||
|
///
|
||||||
|
/// The **start** of a scan is inclusive, unless appended with an '\0', then it is exclusive.
|
||||||
|
///
|
||||||
|
/// The **end** of a scan is exclusive, unless appended with an '\0', then it is inclusive.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use tikv_client::{BoundRange, Key};
|
||||||
|
/// // Exclusive
|
||||||
|
/// let range = "a".."z";
|
||||||
|
/// assert_eq!(
|
||||||
|
/// BoundRange::from(range).into_keys(),
|
||||||
|
/// (Key::from("a"), Some(Key::from("z"))),
|
||||||
|
/// );
|
||||||
|
/// // Inclusive
|
||||||
|
/// let range = "a"..="z";
|
||||||
|
/// assert_eq!(
|
||||||
|
/// BoundRange::from(range).into_keys(),
|
||||||
|
/// (Key::from("a"), Some(Key::from("z\0"))),
|
||||||
|
/// );
|
||||||
|
/// // Open
|
||||||
|
/// let range = "a"..;
|
||||||
|
/// assert_eq!(
|
||||||
|
/// BoundRange::from(range).into_keys(),
|
||||||
|
/// (Key::from("a"), None),
|
||||||
|
/// );
|
||||||
|
// ```
|
||||||
|
pub fn into_keys(self) -> (Key, Option<Key>) {
|
||||||
|
let start = match self.from {
|
||||||
|
Bound::Included(v) => v,
|
||||||
|
Bound::Excluded(mut v) => {
|
||||||
|
v.push_zero();
|
||||||
|
v
|
||||||
|
}
|
||||||
|
Bound::Unbounded => unreachable!(),
|
||||||
|
};
|
||||||
|
let end = match self.to {
|
||||||
|
Bound::Included(mut v) => {
|
||||||
|
v.push_zero();
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
Bound::Excluded(v) => Some(v),
|
||||||
|
Bound::Unbounded => None,
|
||||||
|
};
|
||||||
|
(start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key> + Clone> PartialEq<(Bound<T>, Bound<T>)> for BoundRange {
|
||||||
|
fn eq(&self, other: &(Bound<T>, Bound<T>)) -> bool {
|
||||||
|
self.from == convert_to_bound_key(other.0.clone())
|
||||||
|
&& self.to == convert_to_bound_key(other.1.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key>> From<Range<T>> for BoundRange {
|
||||||
|
fn from(other: Range<T>) -> BoundRange {
|
||||||
|
BoundRange::new(
|
||||||
|
Bound::Included(other.start.into()),
|
||||||
|
Bound::Excluded(other.end.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key>> From<RangeFrom<T>> for BoundRange {
|
||||||
|
fn from(other: RangeFrom<T>) -> BoundRange {
|
||||||
|
BoundRange::new(Bound::Included(other.start.into()), Bound::Unbounded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key>> From<RangeInclusive<T>> for BoundRange {
|
||||||
|
fn from(other: RangeInclusive<T>) -> BoundRange {
|
||||||
|
let (start, end) = other.into_inner();
|
||||||
|
BoundRange::new(Bound::Included(start.into()), Bound::Included(end.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key>> From<(T, Option<T>)> for BoundRange {
|
||||||
|
fn from(other: (T, Option<T>)) -> BoundRange {
|
||||||
|
let to = match other.1 {
|
||||||
|
None => Bound::Unbounded,
|
||||||
|
Some(to) => to.into().into_upper_bound(),
|
||||||
|
};
|
||||||
|
|
||||||
|
BoundRange::new(other.0.into().into_lower_bound(), to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key>> From<(T, T)> for BoundRange {
|
||||||
|
fn from(other: (T, T)) -> BoundRange {
|
||||||
|
BoundRange::new(
|
||||||
|
other.0.into().into_lower_bound(),
|
||||||
|
other.1.into().into_upper_bound(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Key> + Eq> TryFrom<(Bound<T>, Bound<T>)> for BoundRange {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(bounds: (Bound<T>, Bound<T>)) -> Result<BoundRange> {
|
||||||
|
if bounds.0 == Bound::Unbounded {
|
||||||
|
Err(Error::invalid_key_range())
|
||||||
|
} else {
|
||||||
|
Ok(BoundRange::new(
|
||||||
|
convert_to_bound_key(bounds.0),
|
||||||
|
convert_to_bound_key(bounds.1),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_to_bound_key<K: Into<Key>>(b: Bound<K>) -> Bound<Key> {
|
||||||
|
match b {
|
||||||
|
Bound::Included(k) => Bound::Included(k.into()),
|
||||||
|
Bound::Excluded(k) => Bound::Excluded(k.into()),
|
||||||
|
Bound::Unbounded => Bound::Unbounded,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
|
use super::HexRepr;
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest::{arbitrary::any_with, collection::size_range};
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest_derive::Arbitrary;
|
||||||
|
use std::ops::Bound;
|
||||||
|
use std::{fmt, str, u8};
|
||||||
|
|
||||||
|
/// The key part of a key/value pair.
|
||||||
|
///
|
||||||
|
/// In TiKV, keys are an ordered sequence of bytes. This has an advantage over choosing `String` as
|
||||||
|
/// valid `UTF-8` is not required. This means that the user is permitted to store any data they wish,
|
||||||
|
/// as long as it can be represented by bytes. (Which is to say, pretty much anything!)
|
||||||
|
///
|
||||||
|
/// This type also implements `From` for many types. With one exception, these are all done without
|
||||||
|
/// reallocation. Using a `&'static str`, like many examples do for simplicity, has an internal
|
||||||
|
/// allocation cost.
|
||||||
|
///
|
||||||
|
/// This type wraps around an owned value, so it should be treated it like `String` or `Vec<u8>`
|
||||||
|
/// over a `&str` or `&[u8]`.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use tikv_client::Key;
|
||||||
|
///
|
||||||
|
/// let static_str: &'static str = "TiKV";
|
||||||
|
/// let from_static_str = Key::from(static_str);
|
||||||
|
///
|
||||||
|
/// let string: String = String::from(static_str);
|
||||||
|
/// let from_string = Key::from(string);
|
||||||
|
/// assert_eq!(from_static_str, from_string);
|
||||||
|
///
|
||||||
|
/// let vec: Vec<u8> = static_str.as_bytes().to_vec();
|
||||||
|
/// let from_vec = Key::from(vec);
|
||||||
|
/// assert_eq!(from_static_str, from_vec);
|
||||||
|
///
|
||||||
|
/// let bytes = static_str.as_bytes().to_vec();
|
||||||
|
/// let from_bytes = Key::from(bytes);
|
||||||
|
/// assert_eq!(from_static_str, from_bytes);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// **But, you should not need to worry about all this:** Many functions which accept a `Key`
|
||||||
|
/// accept an `Into<Key>`, which means all of the above types can be passed directly to those
|
||||||
|
/// functions.
|
||||||
|
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
pub struct Key(
|
||||||
|
#[cfg_attr(
|
||||||
|
test,
|
||||||
|
proptest(
|
||||||
|
strategy = "any_with::<Vec<u8>>((size_range(crate::proptests::PROPTEST_KEY_MAX), ()))"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub(super) Vec<u8>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn zero_terminated(&self) -> bool {
|
||||||
|
self.0.last().map(|i| *i == 0).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn push_zero(&mut self) {
|
||||||
|
self.0.push(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn into_lower_bound(mut self) -> Bound<Key> {
|
||||||
|
if self.zero_terminated() {
|
||||||
|
self.0.pop().unwrap();
|
||||||
|
Bound::Excluded(self)
|
||||||
|
} else {
|
||||||
|
Bound::Included(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn into_upper_bound(mut self) -> Bound<Key> {
|
||||||
|
if self.zero_terminated() {
|
||||||
|
self.0.pop().unwrap();
|
||||||
|
Bound::Included(self)
|
||||||
|
} else {
|
||||||
|
Bound::Excluded(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Key {
|
||||||
|
fn from(v: Vec<u8>) -> Self {
|
||||||
|
Key(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Key {
|
||||||
|
fn from(v: String) -> Key {
|
||||||
|
Key(v.into_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Key {
|
||||||
|
fn from(v: &'static str) -> Key {
|
||||||
|
Key(v.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Vec<u8>> for Key {
|
||||||
|
fn into(self) -> Vec<u8> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<&'a [u8]> for &'a Key {
|
||||||
|
fn into(self) -> &'a [u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Key {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Key({})", HexRepr(&self.0))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
|
use super::{HexRepr, Key, Value};
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest_derive::Arbitrary;
|
||||||
|
use std::{fmt, str};
|
||||||
|
|
||||||
|
/// A key/value pair.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use tikv_client::{Key, Value, KvPair};
|
||||||
|
/// let key = "key";
|
||||||
|
/// let value = "value";
|
||||||
|
/// let constructed = KvPair::new(key, value);
|
||||||
|
/// let from_tuple = KvPair::from((key, value));
|
||||||
|
/// assert_eq!(constructed, from_tuple);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Many functions which accept a `KvPair` accept an `Into<KvPair>`, which means all of the above
|
||||||
|
/// types (Like a `(Key, Value)`) can be passed directly to those functions.
|
||||||
|
#[derive(Default, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
pub struct KvPair(Key, Value);
|
||||||
|
|
||||||
|
impl KvPair {
|
||||||
|
/// Create a new `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(key: impl Into<Key>, value: impl Into<Value>) -> Self {
|
||||||
|
KvPair(key.into(), value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Immutably borrow the `Key` part of the `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn key(&self) -> &Key {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Immutably borrow the `Value` part of the `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn value(&self) -> &Value {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_key(self) -> Key {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_value(self) -> Value {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutably borrow the `Key` part of the `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn key_mut(&mut self) -> &mut Key {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutably borrow the `Value` part of the `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn value_mut(&mut self) -> &mut Value {
|
||||||
|
&mut self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the `Key` part of the `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_key(&mut self, k: impl Into<Key>) {
|
||||||
|
self.0 = k.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the `Value` part of the `KvPair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_value(&mut self, v: impl Into<Value>) {
|
||||||
|
self.1 = v.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> From<(K, V)> for KvPair
|
||||||
|
where
|
||||||
|
K: Into<Key>,
|
||||||
|
V: Into<Value>,
|
||||||
|
{
|
||||||
|
fn from((k, v): (K, V)) -> Self {
|
||||||
|
KvPair(k.into(), v.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(Key, Value)> for KvPair {
|
||||||
|
fn into(self) -> (Key, Value) {
|
||||||
|
(self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for KvPair {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let KvPair(key, value) = self;
|
||||||
|
match str::from_utf8(&value.0) {
|
||||||
|
Ok(s) => write!(f, "KvPair({}, {:?})", HexRepr(&key.0), s),
|
||||||
|
Err(_) => write!(f, "KvPair({}, {})", HexRepr(&key.0), HexRepr(&value.0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
use std::{fmt, u8};
|
||||||
|
|
||||||
|
mod key;
|
||||||
|
pub use key::Key;
|
||||||
|
mod value;
|
||||||
|
pub use value::Value;
|
||||||
|
mod kvpair;
|
||||||
|
pub use kvpair::KvPair;
|
||||||
|
mod bound_range;
|
||||||
|
pub use bound_range::BoundRange;
|
||||||
|
|
||||||
|
struct HexRepr<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for HexRepr<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
for byte in self.0 {
|
||||||
|
write!(f, "{:02X}", byte)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
|
use super::HexRepr;
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest::{arbitrary::any_with, collection::size_range};
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest_derive::Arbitrary;
|
||||||
|
use std::{fmt, str, u8};
|
||||||
|
|
||||||
|
/// The value part of a key/value pair.
|
||||||
|
///
|
||||||
|
/// In TiKV, values are an ordered sequence of bytes. This has an advantage over choosing `String`
|
||||||
|
/// as valid `UTF-8` is not required. This means that the user is permitted to store any data they wish,
|
||||||
|
/// as long as it can be represented by bytes. (Which is to say, pretty much anything!)
|
||||||
|
///
|
||||||
|
/// This type also implements `From` for many types. With one exception, these are all done without
|
||||||
|
/// reallocation. Using a `&'static str`, like many examples do for simplicity, has an internal
|
||||||
|
/// allocation cost.
|
||||||
|
///
|
||||||
|
/// This type wraps around an owned value, so it should be treated it like `String` or `Vec<u8>`
|
||||||
|
/// over a `&str` or `&[u8]`.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use tikv_client::Value;
|
||||||
|
///
|
||||||
|
/// let static_str: &'static str = "TiKV";
|
||||||
|
/// let from_static_str = Value::from(static_str);
|
||||||
|
///
|
||||||
|
/// let string: String = String::from(static_str);
|
||||||
|
/// let from_string = Value::from(string);
|
||||||
|
/// assert_eq!(from_static_str, from_string);
|
||||||
|
///
|
||||||
|
/// let vec: Vec<u8> = static_str.as_bytes().to_vec();
|
||||||
|
/// let from_vec = Value::from(vec);
|
||||||
|
/// assert_eq!(from_static_str, from_vec);
|
||||||
|
///
|
||||||
|
/// let bytes = static_str.as_bytes().to_vec();
|
||||||
|
/// let from_bytes = Value::from(bytes);
|
||||||
|
/// assert_eq!(from_static_str, from_bytes);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// **But, you should not need to worry about all this:** Many functions which accept a `Value`
|
||||||
|
/// accept an `Into<Value>`, which means all of the above types can be passed directly to those
|
||||||
|
/// functions.
|
||||||
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
pub struct Value(
|
||||||
|
#[cfg_attr(
|
||||||
|
test,
|
||||||
|
proptest(
|
||||||
|
strategy = "any_with::<Vec<u8>>((size_range(crate::proptests::PROPTEST_VALUE_MAX), ()))"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub(super) Vec<u8>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Value {
|
||||||
|
fn from(v: Vec<u8>) -> Self {
|
||||||
|
Value(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Value {
|
||||||
|
fn from(v: String) -> Value {
|
||||||
|
Value(v.into_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Value {
|
||||||
|
fn from(v: &'static str) -> Value {
|
||||||
|
Value(v.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Vec<u8>> for Value {
|
||||||
|
fn into(self) -> Vec<u8> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<&'a [u8]> for &'a Value {
|
||||||
|
fn into(self) -> &'a [u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Value {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match str::from_utf8(&self.0) {
|
||||||
|
Ok(s) => write!(f, "Value({:?})", s),
|
||||||
|
Err(_) => write!(f, "Value({})", HexRepr(&self.0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,6 +78,8 @@ mod compat;
|
||||||
mod config;
|
mod config;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod kv;
|
mod kv;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod proptests;
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
|
// Note: This module exists and includes some integration tests because the `/tests/`
|
||||||
|
// directory tests don't have access to `cfg(tests)` functions and we don't want to force
|
||||||
|
// users to depend on proptest or manually enable features to test.
|
||||||
|
|
||||||
|
use proptest::strategy::Strategy;
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
|
mod raw;
|
||||||
|
|
||||||
|
pub(crate) const ENV_PD_ADDR: &str = "PD_ADDR";
|
||||||
|
pub(crate) const PROPTEST_KEY_MAX: usize = 1024 * 2; // 2 KB
|
||||||
|
pub(crate) const PROPTEST_VALUE_MAX: usize = 1024 * 16; // 16 KB
|
||||||
|
pub(crate) const PROPTEST_BATCH_SIZE_MAX: usize = 16;
|
||||||
|
|
||||||
|
pub fn arb_batch<T: core::fmt::Debug>(
|
||||||
|
single_strategy: impl Strategy<Value = T>,
|
||||||
|
max_batch_size: impl Into<Option<usize>>,
|
||||||
|
) -> impl Strategy<Value = Vec<T>> {
|
||||||
|
let max_batch_size = max_batch_size.into().unwrap_or(PROPTEST_BATCH_SIZE_MAX);
|
||||||
|
proptest::collection::vec(single_strategy, 0..max_batch_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pd_addr() -> Vec<String> {
|
||||||
|
var(ENV_PD_ADDR)
|
||||||
|
.expect(&format!("Expected {}:", ENV_PD_ADDR))
|
||||||
|
.split(",")
|
||||||
|
.map(From::from)
|
||||||
|
.collect()
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
|
use super::{arb_batch, pd_addr};
|
||||||
|
use crate::{raw::Client, Config, KvPair, Value};
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use proptest::{arbitrary::any, proptest};
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
/// Test single point (put, get, delete) operations on keys.
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(not(feature = "integration-tests"), ignore)]
|
||||||
|
fn point(
|
||||||
|
pair in any::<KvPair>(),
|
||||||
|
) {
|
||||||
|
let client = block_on(Client::connect(Config::new(pd_addr()))).unwrap();
|
||||||
|
|
||||||
|
block_on(
|
||||||
|
client.put(pair.key().clone(), pair.value().clone())
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let out_value = block_on(
|
||||||
|
client.get(pair.key().clone())
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(Some(Value::from(pair.value().clone())), out_value);
|
||||||
|
|
||||||
|
block_on(
|
||||||
|
client.delete(pair.key().clone())
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
/// Test batch (put, get, delete) operations on keys.
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(not(feature = "integration-tests"), ignore)]
|
||||||
|
fn batch(
|
||||||
|
kvs in arb_batch(any::<KvPair>(), None),
|
||||||
|
) {
|
||||||
|
let client = block_on(Client::connect(Config::new(pd_addr()))).unwrap();
|
||||||
|
let keys = kvs.iter().map(|kv| kv.key()).cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
block_on(
|
||||||
|
client.batch_put(kvs.clone())
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let out_value = block_on(
|
||||||
|
client.batch_get(keys.clone())
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(kvs, out_value);
|
||||||
|
|
||||||
|
block_on(
|
||||||
|
client.batch_delete(keys.clone())
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
|
||||||
|
|
||||||
mod raw;
|
|
||||||
|
|
||||||
use std::env::var;
|
|
||||||
const ENV_PD_ADDR: &str = "PD_ADDR";
|
|
||||||
|
|
||||||
pub fn pd_addr() -> Vec<String> {
|
|
||||||
var(ENV_PD_ADDR)
|
|
||||||
.expect(&format!("Expected {}:", ENV_PD_ADDR))
|
|
||||||
.split(",")
|
|
||||||
.map(From::from)
|
|
||||||
.collect()
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
|
||||||
|
|
||||||
const NUM_TEST_KEYS: u32 = 100;
|
|
||||||
use crate::integration_tests::pd_addr;
|
|
||||||
use tikv_client::{raw::Client, Config, Key, KvPair, Value};
|
|
||||||
|
|
||||||
fn generate_key(id: i32) -> Key {
|
|
||||||
format!("testkey_{}", id).into_bytes().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_value(id: i32) -> Value {
|
|
||||||
format!("testvalue_{}", id).into_bytes().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wipe_all(client: &Client) {
|
|
||||||
let test_key_start = generate_key(0);
|
|
||||||
let test_key_end = generate_key(NUM_TEST_KEYS as i32 - 1);
|
|
||||||
client
|
|
||||||
.delete_range(test_key_start..test_key_end)
|
|
||||||
.await
|
|
||||||
.expect("Could not delete test keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn connect() -> Client {
|
|
||||||
let client = Client::connect(Config::new(pd_addr()))
|
|
||||||
.await
|
|
||||||
.expect("Could not connect to tikv");
|
|
||||||
wipe_all(&client).await;
|
|
||||||
client
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_empty(client: &Client) {
|
|
||||||
let test_key_start = generate_key(0);
|
|
||||||
let test_key_end = generate_key(NUM_TEST_KEYS as i32 - 1);
|
|
||||||
|
|
||||||
assert!(client
|
|
||||||
.scan(test_key_start..test_key_end, NUM_TEST_KEYS)
|
|
||||||
.await
|
|
||||||
.expect("Could not scan")
|
|
||||||
.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_existence<'a>(
|
|
||||||
client: &'a Client,
|
|
||||||
existing_pairs: &'a [KvPair],
|
|
||||||
not_existing_keys: Vec<Key>,
|
|
||||||
) {
|
|
||||||
let test_key_start = generate_key(0);
|
|
||||||
let test_key_end = generate_key(NUM_TEST_KEYS as i32 - 1);
|
|
||||||
|
|
||||||
for pair in existing_pairs.iter().map(Clone::clone) {
|
|
||||||
let (key, value) = pair.into();
|
|
||||||
assert_eq!(
|
|
||||||
client
|
|
||||||
.get(key)
|
|
||||||
.await
|
|
||||||
.expect("Could not get value")
|
|
||||||
.expect("key doesn't exist"),
|
|
||||||
value.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in not_existing_keys.clone().into_iter() {
|
|
||||||
let r = client.get(key).await.expect("Cound not get value");
|
|
||||||
assert!(r.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut existing_keys = Vec::with_capacity(existing_pairs.len());
|
|
||||||
let mut existing_key_only_pairs = Vec::with_capacity(existing_pairs.len());
|
|
||||||
for pair in existing_pairs.iter() {
|
|
||||||
let key = pair.key().clone();
|
|
||||||
existing_keys.push(key.clone());
|
|
||||||
existing_key_only_pairs.push(KvPair::new(key, Value::default()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut all_keys = existing_keys.clone();
|
|
||||||
all_keys.extend_from_slice(¬_existing_keys);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
client
|
|
||||||
.batch_get(all_keys)
|
|
||||||
.await
|
|
||||||
.expect("Could not get value in batch"),
|
|
||||||
existing_pairs,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
client
|
|
||||||
.batch_get(not_existing_keys)
|
|
||||||
.await
|
|
||||||
.expect("Could not get value in batch"),
|
|
||||||
Vec::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
client
|
|
||||||
.scan(test_key_start.clone()..test_key_end.clone(), NUM_TEST_KEYS)
|
|
||||||
.await
|
|
||||||
.expect("Could not scan"),
|
|
||||||
existing_pairs,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
client
|
|
||||||
.with_key_only(true)
|
|
||||||
.scan(test_key_start.clone()..test_key_end.clone(), NUM_TEST_KEYS)
|
|
||||||
.await
|
|
||||||
.expect("Could not scan"),
|
|
||||||
existing_key_only_pairs,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[runtime::test(runtime_tokio::Tokio)]
|
|
||||||
async fn basic_raw_test() {
|
|
||||||
let client = connect().await;
|
|
||||||
|
|
||||||
test_empty(&client).await;
|
|
||||||
|
|
||||||
assert!(client.put(generate_key(0), generate_value(0)).await.is_ok());
|
|
||||||
let existing = &[KvPair::new(generate_key(0), generate_value(0))];
|
|
||||||
test_existence(&client, existing, vec![generate_key(1), generate_key(2)]).await;
|
|
||||||
|
|
||||||
let empty_pairs = Vec::new();
|
|
||||||
assert!(client.delete(generate_key(0)).await.is_ok());
|
|
||||||
test_existence(
|
|
||||||
&client,
|
|
||||||
&empty_pairs,
|
|
||||||
vec![generate_key(0), generate_key(1), generate_key(2)],
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let pairs: Vec<KvPair> = (0..10)
|
|
||||||
.map(|i| KvPair::new(generate_key(i), generate_value(i)))
|
|
||||||
.collect();
|
|
||||||
assert!(client.batch_put(pairs.clone()).await.is_ok());
|
|
||||||
test_existence(
|
|
||||||
&client,
|
|
||||||
&pairs,
|
|
||||||
vec![generate_key(10), generate_key(11), generate_key(12)],
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let keys: Vec<Key> = vec![generate_key(8), generate_key(9)];
|
|
||||||
assert!(client.batch_delete(keys).await.is_ok());
|
|
||||||
let mut pairs = pairs;
|
|
||||||
pairs.truncate(8);
|
|
||||||
test_existence(
|
|
||||||
&client,
|
|
||||||
&pairs,
|
|
||||||
vec![generate_key(8), generate_key(9), generate_key(10)],
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
wipe_all(&client).await;
|
|
||||||
test_existence(
|
|
||||||
&client,
|
|
||||||
&empty_pairs,
|
|
||||||
pairs.into_iter().map(|x| x.into_key()).collect(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.
|
|
||||||
|
|
||||||
#![feature(async_await)]
|
|
||||||
#![type_length_limit = "4500458"]
|
|
||||||
|
|
||||||
#[cfg(feature = "integration-tests")]
|
|
||||||
mod integration_tests;
|
|
Loading…
Reference in New Issue