From 12b6e04539c8dae341cbd4b950413f886c376c8b Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sun, 30 Oct 2022 01:11:59 +0000 Subject: [PATCH] Implement inclusive and unbounded record ranges Closes #1412 Closes #1413 --- lib/src/dbs/channel.rs | 30 ++++++++++++-- lib/src/dbs/iterate.rs | 30 ++++++++++++-- lib/src/sql/range.rs | 93 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 139 insertions(+), 14 deletions(-) diff --git a/lib/src/dbs/channel.rs b/lib/src/dbs/channel.rs index 05bb7cf5..9934a573 100644 --- a/lib/src/dbs/channel.rs +++ b/lib/src/dbs/channel.rs @@ -11,6 +11,7 @@ use crate::sql::dir::Dir; use crate::sql::thing::Thing; use crate::sql::value::Value; use channel::Sender; +use std::ops::Bound; impl Iterable { pub(crate) async fn channel( @@ -138,9 +139,32 @@ impl Iterable { Iterable::Range(v) => { // Check that the table exists txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?; - // Prepare the start and end keys - let beg = thing::new(opt.ns(), opt.db(), &v.tb, &v.beg).encode().unwrap(); - let end = thing::new(opt.ns(), opt.db(), &v.tb, &v.end).encode().unwrap(); + // Prepare the range start key + let beg = match &v.beg { + 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 let mut nxt: Option> = None; // Loop until no more keys diff --git a/lib/src/dbs/iterate.rs b/lib/src/dbs/iterate.rs index 39b12116..262216b6 100644 --- a/lib/src/dbs/iterate.rs +++ b/lib/src/dbs/iterate.rs @@ -11,6 +11,7 @@ use crate::key::thing; use crate::sql::dir::Dir; use crate::sql::thing::Thing; use crate::sql::value::Value; +use std::ops::Bound; impl Iterable { pub(crate) async fn iterate( @@ -134,9 +135,32 @@ impl Iterable { Iterable::Range(v) => { // Check that the table exists txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?; - // Prepare the start and end keys - let beg = thing::new(opt.ns(), opt.db(), &v.tb, &v.beg).encode().unwrap(); - let end = thing::new(opt.ns(), opt.db(), &v.tb, &v.end).encode().unwrap(); + // Prepare the range start key + let beg = match &v.beg { + 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 let mut nxt: Option> = None; // Loop until no more keys diff --git a/lib/src/sql/range.rs b/lib/src/sql/range.rs index b72f1ebf..e637ca22 100644 --- a/lib/src/sql/range.rs +++ b/lib/src/sql/range.rs @@ -1,36 +1,113 @@ use crate::sql::error::IResult; use crate::sql::id::{id, Id}; use crate::sql::ident::ident_raw; +use nom::branch::alt; 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 std::cmp::Ordering; 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 tb: String, - pub beg: Id, - pub end: Id, + pub beg: Bound, + pub end: Bound, +} + +impl PartialOrd for Range { + fn partial_cmp(&self, other: &Self) -> Option { + 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 { 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> { let (i, tb) = ident_raw(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, end) = id(i)?; + let (i, end) = + opt(alt((map(preceded(char('='), id), Bound::Included), map(id, Bound::Excluded))))(i)?; Ok(( i, Range { tb, - beg, - end, + beg: beg.unwrap_or(Bound::Unbounded), + end: end.unwrap_or(Bound::Unbounded), }, )) }