Implement inclusive and unbounded record ranges

Closes #1412
Closes #1413
This commit is contained in:
Tobie Morgan Hitchcock 2022-10-30 01:11:59 +00:00
parent 82c9d7da2b
commit 12b6e04539
3 changed files with 139 additions and 14 deletions

View file

@ -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

View file

@ -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

View file

@ -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),
}, },
)) ))
} }