Implement inclusive and unbounded record ranges
Closes #1412 Closes #1413
This commit is contained in:
parent
82c9d7da2b
commit
12b6e04539
3 changed files with 139 additions and 14 deletions
|
@ -11,6 +11,7 @@ use crate::sql::dir::Dir;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use channel::Sender;
|
use channel::Sender;
|
||||||
|
use std::ops::Bound;
|
||||||
|
|
||||||
impl Iterable {
|
impl Iterable {
|
||||||
pub(crate) async fn channel(
|
pub(crate) async fn channel(
|
||||||
|
@ -138,9 +139,32 @@ impl Iterable {
|
||||||
Iterable::Range(v) => {
|
Iterable::Range(v) => {
|
||||||
// Check that the table exists
|
// Check that the table exists
|
||||||
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
|
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
|
||||||
// Prepare the start and end keys
|
// Prepare the range start key
|
||||||
let beg = thing::new(opt.ns(), opt.db(), &v.tb, &v.beg).encode().unwrap();
|
let beg = match &v.beg {
|
||||||
let end = thing::new(opt.ns(), opt.db(), &v.tb, &v.end).encode().unwrap();
|
Bound::Unbounded => thing::prefix(opt.ns(), opt.db(), &v.tb),
|
||||||
|
Bound::Included(id) => {
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap()
|
||||||
|
}
|
||||||
|
Bound::Excluded(id) => {
|
||||||
|
let mut key =
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap();
|
||||||
|
key.push(0x00);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Prepare the range end key
|
||||||
|
let end = match &v.end {
|
||||||
|
Bound::Unbounded => thing::suffix(opt.ns(), opt.db(), &v.tb),
|
||||||
|
Bound::Excluded(id) => {
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap()
|
||||||
|
}
|
||||||
|
Bound::Included(id) => {
|
||||||
|
let mut key =
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap();
|
||||||
|
key.push(0x00);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
};
|
||||||
// Prepare the next holder key
|
// Prepare the next holder key
|
||||||
let mut nxt: Option<Vec<u8>> = None;
|
let mut nxt: Option<Vec<u8>> = None;
|
||||||
// Loop until no more keys
|
// Loop until no more keys
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::key::thing;
|
||||||
use crate::sql::dir::Dir;
|
use crate::sql::dir::Dir;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
use std::ops::Bound;
|
||||||
|
|
||||||
impl Iterable {
|
impl Iterable {
|
||||||
pub(crate) async fn iterate(
|
pub(crate) async fn iterate(
|
||||||
|
@ -134,9 +135,32 @@ impl Iterable {
|
||||||
Iterable::Range(v) => {
|
Iterable::Range(v) => {
|
||||||
// Check that the table exists
|
// Check that the table exists
|
||||||
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
|
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
|
||||||
// Prepare the start and end keys
|
// Prepare the range start key
|
||||||
let beg = thing::new(opt.ns(), opt.db(), &v.tb, &v.beg).encode().unwrap();
|
let beg = match &v.beg {
|
||||||
let end = thing::new(opt.ns(), opt.db(), &v.tb, &v.end).encode().unwrap();
|
Bound::Unbounded => thing::prefix(opt.ns(), opt.db(), &v.tb),
|
||||||
|
Bound::Included(id) => {
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap()
|
||||||
|
}
|
||||||
|
Bound::Excluded(id) => {
|
||||||
|
let mut key =
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap();
|
||||||
|
key.push(0x00);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Prepare the range end key
|
||||||
|
let end = match &v.end {
|
||||||
|
Bound::Unbounded => thing::suffix(opt.ns(), opt.db(), &v.tb),
|
||||||
|
Bound::Excluded(id) => {
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap()
|
||||||
|
}
|
||||||
|
Bound::Included(id) => {
|
||||||
|
let mut key =
|
||||||
|
thing::new(opt.ns(), opt.db(), &v.tb, id).encode().unwrap();
|
||||||
|
key.push(0x00);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
};
|
||||||
// Prepare the next holder key
|
// Prepare the next holder key
|
||||||
let mut nxt: Option<Vec<u8>> = None;
|
let mut nxt: Option<Vec<u8>> = None;
|
||||||
// Loop until no more keys
|
// Loop until no more keys
|
||||||
|
|
|
@ -1,36 +1,113 @@
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::id::{id, Id};
|
use crate::sql::id::{id, Id};
|
||||||
use crate::sql::ident::ident_raw;
|
use crate::sql::ident::ident_raw;
|
||||||
|
use nom::branch::alt;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
|
use nom::combinator::map;
|
||||||
|
use nom::combinator::opt;
|
||||||
|
use nom::sequence::preceded;
|
||||||
|
use nom::sequence::terminated;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::Bound;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
pub struct Range {
|
pub struct Range {
|
||||||
pub tb: String,
|
pub tb: String,
|
||||||
pub beg: Id,
|
pub beg: Bound<Id>,
|
||||||
pub end: Id,
|
pub end: Bound<Id>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Range {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match self.tb.partial_cmp(&other.tb) {
|
||||||
|
Some(Ordering::Equal) => match &self.beg {
|
||||||
|
Bound::Unbounded => match &other.beg {
|
||||||
|
Bound::Unbounded => Some(Ordering::Equal),
|
||||||
|
_ => Some(Ordering::Less),
|
||||||
|
},
|
||||||
|
Bound::Included(v) => match &other.beg {
|
||||||
|
Bound::Unbounded => Some(Ordering::Greater),
|
||||||
|
Bound::Included(w) => match v.partial_cmp(w) {
|
||||||
|
Some(Ordering::Equal) => match &self.end {
|
||||||
|
Bound::Unbounded => match &other.end {
|
||||||
|
Bound::Unbounded => Some(Ordering::Equal),
|
||||||
|
_ => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
Bound::Included(v) => match &other.end {
|
||||||
|
Bound::Unbounded => Some(Ordering::Less),
|
||||||
|
Bound::Included(w) => v.partial_cmp(w),
|
||||||
|
_ => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
Bound::Excluded(v) => match &other.end {
|
||||||
|
Bound::Excluded(w) => v.partial_cmp(w),
|
||||||
|
_ => Some(Ordering::Less),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ordering => ordering,
|
||||||
|
},
|
||||||
|
_ => Some(Ordering::Less),
|
||||||
|
},
|
||||||
|
Bound::Excluded(v) => match &other.beg {
|
||||||
|
Bound::Excluded(w) => match v.partial_cmp(w) {
|
||||||
|
Some(Ordering::Equal) => match &self.end {
|
||||||
|
Bound::Unbounded => match &other.end {
|
||||||
|
Bound::Unbounded => Some(Ordering::Equal),
|
||||||
|
_ => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
Bound::Included(v) => match &other.end {
|
||||||
|
Bound::Unbounded => Some(Ordering::Less),
|
||||||
|
Bound::Included(w) => v.partial_cmp(w),
|
||||||
|
_ => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
Bound::Excluded(v) => match &other.end {
|
||||||
|
Bound::Excluded(w) => v.partial_cmp(w),
|
||||||
|
_ => Some(Ordering::Less),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ordering => ordering,
|
||||||
|
},
|
||||||
|
_ => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ordering => ordering,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Range {
|
impl fmt::Display for Range {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}:{}..{}", self.tb, self.beg, self.end)
|
write!(f, "{}:", self.tb)?;
|
||||||
|
match &self.beg {
|
||||||
|
Bound::Unbounded => write!(f, ""),
|
||||||
|
Bound::Included(id) => write!(f, "{}", id),
|
||||||
|
Bound::Excluded(id) => write!(f, "{}>", id),
|
||||||
|
}?;
|
||||||
|
match &self.end {
|
||||||
|
Bound::Unbounded => write!(f, ".."),
|
||||||
|
Bound::Excluded(id) => write!(f, "..{}", id),
|
||||||
|
Bound::Included(id) => write!(f, "..={}", id),
|
||||||
|
}?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(i: &str) -> IResult<&str, Range> {
|
pub fn range(i: &str) -> IResult<&str, Range> {
|
||||||
let (i, tb) = ident_raw(i)?;
|
let (i, tb) = ident_raw(i)?;
|
||||||
let (i, _) = char(':')(i)?;
|
let (i, _) = char(':')(i)?;
|
||||||
let (i, beg) = id(i)?;
|
let (i, beg) =
|
||||||
|
opt(alt((map(terminated(id, char('>')), Bound::Excluded), map(id, Bound::Included))))(i)?;
|
||||||
let (i, _) = char('.')(i)?;
|
let (i, _) = char('.')(i)?;
|
||||||
let (i, _) = char('.')(i)?;
|
let (i, _) = char('.')(i)?;
|
||||||
let (i, end) = id(i)?;
|
let (i, end) =
|
||||||
|
opt(alt((map(preceded(char('='), id), Bound::Included), map(id, Bound::Excluded))))(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
Range {
|
Range {
|
||||||
tb,
|
tb,
|
||||||
beg,
|
beg: beg.unwrap_or(Bound::Unbounded),
|
||||||
end,
|
end: end.unwrap_or(Bound::Unbounded),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue