Implement graph traversal functionality
This commit is contained in:
parent
8ce5d01727
commit
c0a78d8470
19 changed files with 806 additions and 208 deletions
|
@ -5,7 +5,9 @@ use crate::dbs::Options;
|
|||
use crate::dbs::Statement;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::key::graph;
|
||||
use crate::key::thing;
|
||||
use crate::sql::dir::Dir;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
use channel::Sender;
|
||||
|
@ -39,55 +41,6 @@ impl Iterable {
|
|||
// Process the document record
|
||||
chn.send((Some(v), val)).await?;
|
||||
}
|
||||
Iterable::Table(v) => {
|
||||
let beg = thing::prefix(opt.ns(), opt.db(), &v);
|
||||
let end = thing::suffix(opt.ns(), opt.db(), &v);
|
||||
let mut nxt: Option<Vec<u8>> = None;
|
||||
loop {
|
||||
if ctx.is_ok() {
|
||||
let res = match nxt {
|
||||
None => {
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
Some(ref mut beg) => {
|
||||
beg.push(0x00);
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
};
|
||||
if !res.is_empty() {
|
||||
// Get total results
|
||||
let n = res.len();
|
||||
// Exit when settled
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
// Loop over results
|
||||
for (i, (k, v)) in res.into_iter().enumerate() {
|
||||
if ctx.is_ok() {
|
||||
// Ready the next
|
||||
if n == i + 1 {
|
||||
nxt = Some(k.clone());
|
||||
}
|
||||
// Parse the data from the store
|
||||
let key: crate::key::thing::Thing = (&k).into();
|
||||
let val: crate::sql::value::Value = (&v).into();
|
||||
let rid = Thing::from((key.tb, key.id));
|
||||
// Create a new operable value
|
||||
let val = Operable::Value(val);
|
||||
// Process the record
|
||||
chn.send((Some(rid), val)).await?;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Iterable::Mergeable(v, o) => {
|
||||
// Fetch the data from the store
|
||||
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
|
||||
|
@ -116,6 +69,187 @@ impl Iterable {
|
|||
// Process the document record
|
||||
chn.send((Some(v), val)).await?;
|
||||
}
|
||||
Iterable::Table(v) => {
|
||||
// Prepare the start and end keys
|
||||
let beg = thing::prefix(opt.ns(), opt.db(), &v);
|
||||
let end = thing::suffix(opt.ns(), opt.db(), &v);
|
||||
// Prepare the next holder key
|
||||
let mut nxt: Option<Vec<u8>> = None;
|
||||
// Loop until no more keys
|
||||
loop {
|
||||
// Check if the context is finished
|
||||
ctx.check()?;
|
||||
// Get the next 1000 key-value entries
|
||||
let res = match nxt {
|
||||
None => {
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
Some(ref mut beg) => {
|
||||
beg.push(0x00);
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
};
|
||||
// If there are key-value entries then fetch them
|
||||
if !res.is_empty() {
|
||||
// Get total results
|
||||
let n = res.len();
|
||||
// Exit when settled
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
// Loop over results
|
||||
for (i, (k, v)) in res.into_iter().enumerate() {
|
||||
// Check the context
|
||||
ctx.check()?;
|
||||
// Ready the next
|
||||
if n == i + 1 {
|
||||
nxt = Some(k.clone());
|
||||
}
|
||||
// Parse the data from the store
|
||||
let key: crate::key::thing::Thing = (&k).into();
|
||||
let val: crate::sql::value::Value = (&v).into();
|
||||
let rid = Thing::from((key.tb, key.id));
|
||||
// Create a new operable value
|
||||
let val = Operable::Value(val);
|
||||
// Process the record
|
||||
chn.send((Some(rid), val)).await?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Iterable::Edges(e) => {
|
||||
// Pull out options
|
||||
let ns = opt.ns();
|
||||
let db = opt.db();
|
||||
let tb = &e.from.tb;
|
||||
let id = &e.from.id;
|
||||
// Fetch start and end key pairs
|
||||
let keys = match e.what.len() {
|
||||
0 => match e.dir {
|
||||
// /ns/db/tb/id
|
||||
Dir::Both => {
|
||||
vec![(graph::prefix(ns, db, tb, id), graph::suffix(ns, db, tb, id))]
|
||||
}
|
||||
// /ns/db/tb/id/IN
|
||||
Dir::In => vec![(
|
||||
graph::egprefix(ns, db, tb, id, &e.dir),
|
||||
graph::egsuffix(ns, db, tb, id, &e.dir),
|
||||
)],
|
||||
// /ns/db/tb/id/OUT
|
||||
Dir::Out => vec![(
|
||||
graph::egprefix(ns, db, tb, id, &e.dir),
|
||||
graph::egsuffix(ns, db, tb, id, &e.dir),
|
||||
)],
|
||||
},
|
||||
_ => match e.dir {
|
||||
// /ns/db/tb/id/IN/TB
|
||||
Dir::In => e
|
||||
.what
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.map(|v| {
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &e.dir, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &e.dir, &v),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
// /ns/db/tb/id/OUT/TB
|
||||
Dir::Out => e
|
||||
.what
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.map(|v| {
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &e.dir, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &e.dir, &v),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
// /ns/db/tb/id/IN/TB, /ns/db/tb/id/OUT/TB
|
||||
Dir::Both => e
|
||||
.what
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.flat_map(|v| {
|
||||
vec![
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &Dir::In, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &Dir::In, &v),
|
||||
),
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &Dir::Out, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &Dir::Out, &v),
|
||||
),
|
||||
]
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
};
|
||||
//
|
||||
for (beg, end) in keys.iter() {
|
||||
// Prepare the next holder key
|
||||
let mut nxt: Option<Vec<u8>> = None;
|
||||
// Loop until no more keys
|
||||
loop {
|
||||
// Check if the context is finished
|
||||
ctx.check()?;
|
||||
// Get the next 1000 key-value entries
|
||||
let res = match nxt {
|
||||
None => {
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
Some(ref mut beg) => {
|
||||
beg.push(0x00);
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
};
|
||||
// If there are key-value entries then fetch them
|
||||
if !res.is_empty() {
|
||||
// Get total results
|
||||
let n = res.len();
|
||||
// Exit when settled
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
// Loop over results
|
||||
for (i, (k, _)) in res.into_iter().enumerate() {
|
||||
// Check the context
|
||||
ctx.check()?;
|
||||
// Ready the next
|
||||
if n == i + 1 {
|
||||
nxt = Some(k.clone());
|
||||
}
|
||||
// Parse the data from the store
|
||||
let gra: crate::key::graph::Graph = (&k).into();
|
||||
// Fetch the data from the store
|
||||
let key = thing::new(opt.ns(), opt.db(), &gra.ft, &gra.fk);
|
||||
let val = txn.clone().lock().await.get(key).await?;
|
||||
let rid = Thing::from((gra.ft, gra.fk));
|
||||
// Parse the data from the store
|
||||
let val = Operable::Value(match val {
|
||||
Some(v) => Value::from(v),
|
||||
None => Value::None,
|
||||
});
|
||||
// Process the record
|
||||
chn.send((Some(rid), val)).await?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -6,7 +6,9 @@ use crate::dbs::Options;
|
|||
use crate::dbs::Statement;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::key::graph;
|
||||
use crate::key::thing;
|
||||
use crate::sql::dir::Dir;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
|
@ -39,55 +41,6 @@ impl Iterable {
|
|||
// Process the document record
|
||||
ite.process(ctx, opt, txn, stm, Some(v), val).await;
|
||||
}
|
||||
Iterable::Table(v) => {
|
||||
let beg = thing::prefix(opt.ns(), opt.db(), &v);
|
||||
let end = thing::suffix(opt.ns(), opt.db(), &v);
|
||||
let mut nxt: Option<Vec<u8>> = None;
|
||||
loop {
|
||||
if ctx.is_ok() {
|
||||
let res = match nxt {
|
||||
None => {
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
Some(ref mut beg) => {
|
||||
beg.push(0x00);
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
};
|
||||
if !res.is_empty() {
|
||||
// Get total results
|
||||
let n = res.len();
|
||||
// Exit when settled
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
// Loop over results
|
||||
for (i, (k, v)) in res.into_iter().enumerate() {
|
||||
if ctx.is_ok() {
|
||||
// Ready the next
|
||||
if n == i + 1 {
|
||||
nxt = Some(k.clone());
|
||||
}
|
||||
// Parse the data from the store
|
||||
let key: crate::key::thing::Thing = (&k).into();
|
||||
let val: crate::sql::value::Value = (&v).into();
|
||||
let rid = Thing::from((key.tb, key.id));
|
||||
// Create a new operable value
|
||||
let val = Operable::Value(val);
|
||||
// Process the record
|
||||
ite.process(ctx, opt, txn, stm, Some(rid), val).await;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Iterable::Mergeable(v, o) => {
|
||||
// Fetch the data from the store
|
||||
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
|
||||
|
@ -116,6 +69,187 @@ impl Iterable {
|
|||
// Process the document record
|
||||
ite.process(ctx, opt, txn, stm, Some(v), val).await;
|
||||
}
|
||||
Iterable::Table(v) => {
|
||||
// Prepare the start and end keys
|
||||
let beg = thing::prefix(opt.ns(), opt.db(), &v);
|
||||
let end = thing::suffix(opt.ns(), opt.db(), &v);
|
||||
// Prepare the next holder key
|
||||
let mut nxt: Option<Vec<u8>> = None;
|
||||
// Loop until no more keys
|
||||
loop {
|
||||
// Check if the context is finished
|
||||
ctx.check()?;
|
||||
// Get the next 1000 key-value entries
|
||||
let res = match nxt {
|
||||
None => {
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
Some(ref mut beg) => {
|
||||
beg.push(0x00);
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
};
|
||||
// If there are key-value entries then fetch them
|
||||
if !res.is_empty() {
|
||||
// Get total results
|
||||
let n = res.len();
|
||||
// Exit when settled
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
// Loop over results
|
||||
for (i, (k, v)) in res.into_iter().enumerate() {
|
||||
// Check the context
|
||||
ctx.check()?;
|
||||
// Ready the next
|
||||
if n == i + 1 {
|
||||
nxt = Some(k.clone());
|
||||
}
|
||||
// Parse the data from the store
|
||||
let key: crate::key::thing::Thing = (&k).into();
|
||||
let val: crate::sql::value::Value = (&v).into();
|
||||
let rid = Thing::from((key.tb, key.id));
|
||||
// Create a new operable value
|
||||
let val = Operable::Value(val);
|
||||
// Process the record
|
||||
ite.process(ctx, opt, txn, stm, Some(rid), val).await;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Iterable::Edges(e) => {
|
||||
// Pull out options
|
||||
let ns = opt.ns();
|
||||
let db = opt.db();
|
||||
let tb = &e.from.tb;
|
||||
let id = &e.from.id;
|
||||
// Fetch start and end key pairs
|
||||
let keys = match e.what.len() {
|
||||
0 => match e.dir {
|
||||
// /ns/db/tb/id
|
||||
Dir::Both => {
|
||||
vec![(graph::prefix(ns, db, tb, id), graph::suffix(ns, db, tb, id))]
|
||||
}
|
||||
// /ns/db/tb/id/IN
|
||||
Dir::In => vec![(
|
||||
graph::egprefix(ns, db, tb, id, &e.dir),
|
||||
graph::egsuffix(ns, db, tb, id, &e.dir),
|
||||
)],
|
||||
// /ns/db/tb/id/OUT
|
||||
Dir::Out => vec![(
|
||||
graph::egprefix(ns, db, tb, id, &e.dir),
|
||||
graph::egsuffix(ns, db, tb, id, &e.dir),
|
||||
)],
|
||||
},
|
||||
_ => match e.dir {
|
||||
// /ns/db/tb/id/IN/TB
|
||||
Dir::In => e
|
||||
.what
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.map(|v| {
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &e.dir, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &e.dir, &v),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
// /ns/db/tb/id/OUT/TB
|
||||
Dir::Out => e
|
||||
.what
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.map(|v| {
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &e.dir, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &e.dir, &v),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
// /ns/db/tb/id/IN/TB, /ns/db/tb/id/OUT/TB
|
||||
Dir::Both => e
|
||||
.what
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.flat_map(|v| {
|
||||
vec![
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &Dir::In, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &Dir::In, &v),
|
||||
),
|
||||
(
|
||||
graph::ftprefix(ns, db, tb, id, &Dir::Out, &v),
|
||||
graph::ftsuffix(ns, db, tb, id, &Dir::Out, &v),
|
||||
),
|
||||
]
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
};
|
||||
//
|
||||
for (beg, end) in keys.iter() {
|
||||
// Prepare the next holder key
|
||||
let mut nxt: Option<Vec<u8>> = None;
|
||||
// Loop until no more keys
|
||||
loop {
|
||||
// Check if the context is finished
|
||||
ctx.check()?;
|
||||
// Get the next 1000 key-value entries
|
||||
let res = match nxt {
|
||||
None => {
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
Some(ref mut beg) => {
|
||||
beg.push(0x00);
|
||||
let min = beg.clone();
|
||||
let max = end.clone();
|
||||
txn.clone().lock().await.scan(min..max, 1000).await?
|
||||
}
|
||||
};
|
||||
// If there are key-value entries then fetch them
|
||||
if !res.is_empty() {
|
||||
// Get total results
|
||||
let n = res.len();
|
||||
// Exit when settled
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
// Loop over results
|
||||
for (i, (k, _)) in res.into_iter().enumerate() {
|
||||
// Check the context
|
||||
ctx.check()?;
|
||||
// Ready the next
|
||||
if n == i + 1 {
|
||||
nxt = Some(k.clone());
|
||||
}
|
||||
// Parse the data from the store
|
||||
let gra: crate::key::graph::Graph = (&k).into();
|
||||
// Fetch the data from the store
|
||||
let key = thing::new(opt.ns(), opt.db(), &gra.ft, &gra.fk);
|
||||
let val = txn.clone().lock().await.get(key).await?;
|
||||
let rid = Thing::from((gra.ft, gra.fk));
|
||||
// Parse the data from the store
|
||||
let val = Operable::Value(match val {
|
||||
Some(v) => Value::from(v),
|
||||
None => Value::None,
|
||||
});
|
||||
// Process the record
|
||||
ite.process(ctx, opt, txn, stm, Some(rid), val).await;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::dbs::Transaction;
|
|||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::array::Array;
|
||||
use crate::sql::edges::Edges;
|
||||
use crate::sql::field::Field;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::table::Table;
|
||||
|
@ -19,6 +20,7 @@ pub enum Iterable {
|
|||
Value(Value),
|
||||
Table(Table),
|
||||
Thing(Thing),
|
||||
Edges(Edges),
|
||||
Mergeable(Thing, Value),
|
||||
Relatable(Thing, Thing, Thing),
|
||||
}
|
||||
|
|
106
lib/src/sql/edges.rs
Normal file
106
lib/src/sql/edges.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::dir::{dir, Dir};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::table::{table, tables, Tables};
|
||||
use crate::sql::thing::{thing, Thing};
|
||||
use nom::branch::alt;
|
||||
use nom::character::complete::char;
|
||||
use nom::combinator::map;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Edges {
|
||||
pub dir: Dir,
|
||||
pub from: Thing,
|
||||
pub what: Tables,
|
||||
}
|
||||
|
||||
impl fmt::Display for Edges {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.what.len() {
|
||||
0 => write!(f, "{}{}?", self.from, self.dir,),
|
||||
1 => write!(f, "{}{}{}", self.from, self.dir, self.what),
|
||||
_ => write!(f, "{}{}({})", self.from, self.dir, self.what),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edges(i: &str) -> IResult<&str, Edges> {
|
||||
let (i, from) = thing(i)?;
|
||||
let (i, dir) = dir(i)?;
|
||||
let (i, what) = alt((simple, custom))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Edges {
|
||||
dir,
|
||||
from,
|
||||
what,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn simple(i: &str) -> IResult<&str, Tables> {
|
||||
let (i, w) = alt((any, one))(i)?;
|
||||
Ok((i, w))
|
||||
}
|
||||
|
||||
fn custom(i: &str) -> IResult<&str, Tables> {
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, w) = alt((any, tables))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, w))
|
||||
}
|
||||
|
||||
fn one(i: &str) -> IResult<&str, Tables> {
|
||||
let (i, v) = table(i)?;
|
||||
Ok((i, Tables::from(v)))
|
||||
}
|
||||
|
||||
fn any(i: &str) -> IResult<&str, Tables> {
|
||||
map(char('?'), |_| Tables::default())(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn edges_in() {
|
||||
let sql = "person:test<-likes";
|
||||
let res = edges(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("person:test<-likes", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edges_out() {
|
||||
let sql = "person:test->likes";
|
||||
let res = edges(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("person:test->likes", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edges_both() {
|
||||
let sql = "person:test<->likes";
|
||||
let res = edges(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("person:test<->likes", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edges_multiple() {
|
||||
let sql = "person:test->(likes, follows)";
|
||||
let res = edges(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("person:test->(likes, follows)", format!("{}", out));
|
||||
}
|
||||
}
|
42
lib/src/sql/ending.rs
Normal file
42
lib/src/sql/ending.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use crate::sql::comment::comment;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::operator::{assigner, operator};
|
||||
use nom::branch::alt;
|
||||
use nom::character::complete::char;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::peek;
|
||||
|
||||
pub fn number(i: &str) -> IResult<&str, ()> {
|
||||
peek(alt((
|
||||
map(multispace1, |_| ()),
|
||||
map(operator, |_| ()),
|
||||
map(assigner, |_| ()),
|
||||
map(comment, |_| ()),
|
||||
map(char(')'), |_| ()),
|
||||
map(char(']'), |_| ()),
|
||||
map(char('}'), |_| ()),
|
||||
map(char(';'), |_| ()),
|
||||
map(char(','), |_| ()),
|
||||
map(eof, |_| ()),
|
||||
)))(i)
|
||||
}
|
||||
|
||||
pub fn ident(i: &str) -> IResult<&str, ()> {
|
||||
peek(alt((
|
||||
map(multispace1, |_| ()),
|
||||
map(operator, |_| ()),
|
||||
map(assigner, |_| ()),
|
||||
map(comment, |_| ()),
|
||||
map(char(')'), |_| ()),
|
||||
map(char(']'), |_| ()),
|
||||
map(char('}'), |_| ()),
|
||||
map(char(';'), |_| ()),
|
||||
map(char(','), |_| ()),
|
||||
map(char('.'), |_| ()),
|
||||
map(char('['), |_| ()),
|
||||
map(char('-'), |_| ()),
|
||||
map(eof, |_| ()),
|
||||
)))(i)
|
||||
}
|
|
@ -21,6 +21,12 @@ pub struct Graph {
|
|||
pub alias: Option<Idiom>,
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
pub fn to_raw(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Graph {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.what.0.len() <= 1 && self.cond.is_none() && self.alias.is_none() {
|
||||
|
|
|
@ -4,11 +4,12 @@ use crate::dbs::Transaction;
|
|||
use crate::err::Error;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::part::{all, field, first, graph, index, last, part, Part};
|
||||
use crate::sql::part::Next;
|
||||
use crate::sql::part::{all, field, first, graph, index, last, part, thing, Part};
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::multi::{many0, many1};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
@ -72,7 +73,7 @@ impl Idiom {
|
|||
self.0
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|p| matches!(p, Part::Field(_) | Part::Graph(_)))
|
||||
.filter(|p| matches!(p, Part::Field(_) | Part::Thing(_) | Part::Graph(_)))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
@ -94,11 +95,24 @@ impl Idiom {
|
|||
txn: &Transaction,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match doc {
|
||||
// There is a current document
|
||||
Some(v) => v.get(ctx, opt, txn, self).await?.compute(ctx, opt, txn, doc).await,
|
||||
// There isn't any document
|
||||
None => Ok(Value::None),
|
||||
match self.first() {
|
||||
// The first part is a thing record
|
||||
Some(Part::Thing(v)) => {
|
||||
// Use the thing as the document
|
||||
let v: Value = v.clone().into();
|
||||
// Fetch the Idiom from the document
|
||||
v.get(ctx, opt, txn, self.as_ref().next())
|
||||
.await?
|
||||
.compute(ctx, opt, txn, Some(&v))
|
||||
.await
|
||||
}
|
||||
// Otherwise use the current document
|
||||
_ => match doc {
|
||||
// There is a current document
|
||||
Some(v) => v.get(ctx, opt, txn, self).await?.compute(ctx, opt, txn, doc).await,
|
||||
// There isn't any document
|
||||
None => Ok(Value::None),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,18 +160,32 @@ pub fn param(i: &str) -> IResult<&str, Idiom> {
|
|||
}
|
||||
|
||||
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = alt((first, graph))(i)?;
|
||||
let (i, mut v) = many0(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
alt((
|
||||
|i| {
|
||||
let (i, p) = alt((thing, graph))(i)?;
|
||||
let (i, mut v) = many1(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
},
|
||||
|i| {
|
||||
let (i, p) = alt((first, graph))(i)?;
|
||||
let (i, mut v) = many0(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
},
|
||||
))(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::dir::Dir;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::graph::Graph;
|
||||
use crate::sql::table::Table;
|
||||
use crate::sql::test::Parse;
|
||||
use crate::sql::thing::Thing;
|
||||
|
||||
#[test]
|
||||
fn idiom_normal() {
|
||||
|
@ -277,4 +305,32 @@ mod tests {
|
|||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idiom_start_thing_remote_traversal() {
|
||||
let sql = "person:test.friend->like->person";
|
||||
let res = idiom(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("person:test.friend->like->person", format!("{}", out));
|
||||
assert_eq!(
|
||||
out,
|
||||
Idiom(vec![
|
||||
Part::from(Thing::from(("person", "test"))),
|
||||
Part::from("friend"),
|
||||
Part::from(Graph {
|
||||
dir: Dir::Out,
|
||||
what: Table::from("like").into(),
|
||||
cond: None,
|
||||
alias: None,
|
||||
}),
|
||||
Part::from(Graph {
|
||||
dir: Dir::Out,
|
||||
what: Table::from("person").into(),
|
||||
cond: None,
|
||||
alias: None,
|
||||
}),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ pub(crate) mod data;
|
|||
pub(crate) mod datetime;
|
||||
pub(crate) mod dir;
|
||||
pub(crate) mod duration;
|
||||
pub(crate) mod edges;
|
||||
pub(crate) mod ending;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod escape;
|
||||
pub(crate) mod expression;
|
||||
|
@ -64,6 +66,7 @@ pub use self::data::Data;
|
|||
pub use self::datetime::Datetime;
|
||||
pub use self::dir::Dir;
|
||||
pub use self::duration::Duration;
|
||||
pub use self::edges::Edges;
|
||||
pub use self::error::Error;
|
||||
pub use self::expression::Expression;
|
||||
pub use self::fetch::Fetch;
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
use crate::sql::comment::comment;
|
||||
use crate::sql::ending::number as ending;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::operator::{assigner, operator};
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive;
|
||||
use bigdecimal::ToPrimitive;
|
||||
use nom::branch::alt;
|
||||
use nom::character::complete::char;
|
||||
use nom::character::complete::i64;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::peek;
|
||||
use nom::number::complete::recognize_float;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
@ -509,35 +504,13 @@ pub fn number(i: &str) -> IResult<&str, Number> {
|
|||
|
||||
pub fn integer(i: &str) -> IResult<&str, i64> {
|
||||
let (i, v) = i64(i)?;
|
||||
let (i, _) = peek(alt((
|
||||
map(multispace1, |_| ()),
|
||||
map(operator, |_| ()),
|
||||
map(assigner, |_| ()),
|
||||
map(comment, |_| ()),
|
||||
map(char(')'), |_| ()),
|
||||
map(char(']'), |_| ()),
|
||||
map(char('}'), |_| ()),
|
||||
map(char(';'), |_| ()),
|
||||
map(char(','), |_| ()),
|
||||
map(eof, |_| ()),
|
||||
)))(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
pub fn decimal(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = recognize_float(i)?;
|
||||
let (i, _) = peek(alt((
|
||||
map(multispace1, |_| ()),
|
||||
map(operator, |_| ()),
|
||||
map(assigner, |_| ()),
|
||||
map(comment, |_| ()),
|
||||
map(char(')'), |_| ()),
|
||||
map(char(']'), |_| ()),
|
||||
map(char('}'), |_| ()),
|
||||
map(char(';'), |_| ()),
|
||||
map(char(','), |_| ()),
|
||||
map(eof, |_| ()),
|
||||
)))(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::ending::ident as ending;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::graph::{graph as graph_raw, Graph};
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::number::{number, Number};
|
||||
use crate::sql::thing::{thing as thing_raw, Thing};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
|
@ -21,6 +23,7 @@ pub enum Part {
|
|||
Field(Ident),
|
||||
Index(Number),
|
||||
Where(Value),
|
||||
Thing(Thing),
|
||||
Graph(Graph),
|
||||
}
|
||||
|
||||
|
@ -60,6 +63,12 @@ impl From<Value> for Part {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Thing> for Part {
|
||||
fn from(v: Thing) -> Self {
|
||||
Part::Thing(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Graph> for Part {
|
||||
fn from(v: Graph) -> Self {
|
||||
Part::Graph(v)
|
||||
|
@ -100,6 +109,7 @@ impl fmt::Display for Part {
|
|||
Part::Field(v) => write!(f, ".{}", v),
|
||||
Part::Index(v) => write!(f, "[{}]", v),
|
||||
Part::Where(v) => write!(f, "[WHERE {}]", v),
|
||||
Part::Thing(v) => write!(f, "{}", v),
|
||||
Part::Graph(v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +138,7 @@ pub fn part(i: &str) -> IResult<&str, Part> {
|
|||
|
||||
pub fn first(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = ident(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Part::Field(v)))
|
||||
}
|
||||
|
||||
|
@ -165,6 +176,7 @@ pub fn index(i: &str) -> IResult<&str, Part> {
|
|||
pub fn field(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = char('.')(i)?;
|
||||
let (i, v) = ident(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Part::Field(v)))
|
||||
}
|
||||
|
||||
|
@ -177,6 +189,11 @@ pub fn filter(i: &str) -> IResult<&str, Part> {
|
|||
Ok((i, Part::Where(v)))
|
||||
}
|
||||
|
||||
pub fn thing(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = thing_raw(i)?;
|
||||
Ok((i, Part::Thing(v)))
|
||||
}
|
||||
|
||||
pub fn graph(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = graph_raw(i)?;
|
||||
Ok((i, Part::Graph(v)))
|
||||
|
|
|
@ -53,6 +53,7 @@ impl DeleteStatement {
|
|||
match v {
|
||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||
Value::Model(v) => {
|
||||
for v in v {
|
||||
i.ingest(Iterable::Thing(v));
|
||||
|
@ -63,6 +64,7 @@ impl DeleteStatement {
|
|||
match v {
|
||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||
Value::Model(v) => {
|
||||
for v in v {
|
||||
i.ingest(Iterable::Thing(v));
|
||||
|
|
|
@ -92,6 +92,7 @@ impl SelectStatement {
|
|||
match v {
|
||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||
Value::Model(v) => {
|
||||
for v in v {
|
||||
i.ingest(Iterable::Thing(v));
|
||||
|
@ -102,6 +103,7 @@ impl SelectStatement {
|
|||
match v {
|
||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||
Value::Model(v) => {
|
||||
for v in v {
|
||||
i.ingest(Iterable::Thing(v));
|
||||
|
|
|
@ -54,6 +54,7 @@ impl UpdateStatement {
|
|||
match v {
|
||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||
Value::Model(v) => {
|
||||
for v in v {
|
||||
i.ingest(Iterable::Thing(v));
|
||||
|
@ -64,6 +65,7 @@ impl UpdateStatement {
|
|||
match v {
|
||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||
Value::Model(v) => {
|
||||
for v in v {
|
||||
i.ingest(Iterable::Thing(v));
|
||||
|
|
|
@ -33,6 +33,21 @@ impl From<(String, String)> for Thing {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str)> for Thing {
|
||||
fn from(v: (&str, &str)) -> Self {
|
||||
Thing {
|
||||
tb: v.0.to_owned(),
|
||||
id: Id::from(v.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Thing {
|
||||
pub fn to_raw(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Thing {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", escape_ident(&self.tb), self.id)
|
||||
|
|
19
lib/src/sql/value/flatten.rs
Normal file
19
lib/src/sql/value/flatten.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use crate::sql::array::Array;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn flatten(self) -> Self {
|
||||
match self {
|
||||
Value::Array(v) => {
|
||||
v.0.into_iter()
|
||||
.flat_map(|v| match v {
|
||||
Value::Array(v) => v,
|
||||
_ => Array::from(v),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ use crate::ctx::Context;
|
|||
use crate::dbs::Options;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::edges::Edges;
|
||||
use crate::sql::field::{Field, Fields};
|
||||
use crate::sql::part::Next;
|
||||
use crate::sql::part::Part;
|
||||
|
@ -9,6 +10,9 @@ use crate::sql::statements::select::SelectStatement;
|
|||
use crate::sql::value::{Value, Values};
|
||||
use async_recursion::async_recursion;
|
||||
use futures::future::try_join_all;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
static ID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]);
|
||||
|
||||
impl Value {
|
||||
#[cfg_attr(feature = "parallel", async_recursion)]
|
||||
|
@ -25,6 +29,10 @@ impl Value {
|
|||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Graph(_) => match v.rid() {
|
||||
Some(v) => Value::Thing(v).get(ctx, opt, txn, path).await,
|
||||
None => Ok(Value::None),
|
||||
},
|
||||
Part::Field(f) => match v.get(f as &str) {
|
||||
Some(v) => v.get(ctx, opt, txn, path.next()).await,
|
||||
None => Ok(Value::None),
|
||||
|
@ -67,23 +75,62 @@ impl Value {
|
|||
}
|
||||
},
|
||||
// Current path part is a thing
|
||||
Value::Thing(v) => match path.len() {
|
||||
// No remote embedded fields, so just return this
|
||||
0 => Ok(Value::Thing(v.clone())),
|
||||
// Remote embedded field, so fetch the thing
|
||||
_ => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All]),
|
||||
what: Values(vec![Value::Thing(v.clone())]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
stm.compute(ctx, opt, txn, None)
|
||||
.await?
|
||||
.first()
|
||||
.get(ctx, opt, txn, path)
|
||||
.await
|
||||
Value::Thing(v) => {
|
||||
// Clone the thing
|
||||
let val = v.clone();
|
||||
// Check how many path parts are remaining
|
||||
match path.len() {
|
||||
// No remote embedded fields, so just return this
|
||||
0 => Ok(Value::Thing(val)),
|
||||
// Remote embedded field, so fetch the thing
|
||||
_ => match p {
|
||||
// This is a graph traversal expression
|
||||
Part::Graph(g) => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All]),
|
||||
what: Values(vec![Value::from(Edges {
|
||||
from: val,
|
||||
dir: g.dir.clone(),
|
||||
what: g.what.clone(),
|
||||
})]),
|
||||
cond: g.cond.clone(),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
match path.len() {
|
||||
1 => stm
|
||||
.compute(ctx, opt, txn, None)
|
||||
.await?
|
||||
.all()
|
||||
.get(ctx, opt, txn, ID.as_ref())
|
||||
.await?
|
||||
.flatten()
|
||||
.ok(),
|
||||
_ => stm
|
||||
.compute(ctx, opt, txn, None)
|
||||
.await?
|
||||
.all()
|
||||
.get(ctx, opt, txn, path.next())
|
||||
.await?
|
||||
.flatten()
|
||||
.ok(),
|
||||
}
|
||||
}
|
||||
// This is a remote field expression
|
||||
_ => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All]),
|
||||
what: Values(vec![Value::from(val)]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
stm.compute(ctx, opt, txn, None)
|
||||
.await?
|
||||
.first()
|
||||
.get(ctx, opt, txn, path)
|
||||
.await
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
// Ignore everything else
|
||||
_ => Ok(Value::None),
|
||||
},
|
||||
|
|
|
@ -14,6 +14,7 @@ mod diff;
|
|||
mod each;
|
||||
mod every;
|
||||
mod first;
|
||||
mod flatten;
|
||||
mod get;
|
||||
mod increment;
|
||||
mod last;
|
||||
|
|
|
@ -24,7 +24,25 @@ impl Value {
|
|||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Field(f) => match v.get_mut(f as &str) {
|
||||
Part::Thing(t) => match v.get_mut(t.to_raw().as_str()) {
|
||||
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
obj.set(ctx, opt, txn, path.next(), val).await?;
|
||||
v.insert(t.to_raw(), obj);
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
Part::Graph(g) => match v.get_mut(g.to_raw().as_str()) {
|
||||
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
obj.set(ctx, opt, txn, path.next(), val).await?;
|
||||
v.insert(g.to_raw(), obj);
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
Part::Field(f) => match v.get_mut(f.to_raw().as_str()) {
|
||||
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::sql::array::{array, Array};
|
|||
use crate::sql::common::commas;
|
||||
use crate::sql::datetime::{datetime, Datetime};
|
||||
use crate::sql::duration::{duration, Duration};
|
||||
use crate::sql::edges::{edges, Edges};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::function::{function, Function};
|
||||
|
@ -110,6 +111,7 @@ pub enum Value {
|
|||
Thing(Thing),
|
||||
Model(Model),
|
||||
Regex(Regex),
|
||||
Edges(Box<Edges>),
|
||||
Function(Box<Function>),
|
||||
Subquery(Box<Subquery>),
|
||||
Expression(Box<Expression>),
|
||||
|
@ -150,6 +152,12 @@ impl From<Idiom> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Model> for Value {
|
||||
fn from(v: Model) -> Self {
|
||||
Value::Model(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table> for Value {
|
||||
fn from(v: Table) -> Self {
|
||||
Value::Table(v)
|
||||
|
@ -210,21 +218,27 @@ impl From<Duration> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Edges> for Value {
|
||||
fn from(v: Edges) -> Self {
|
||||
Value::Edges(Box::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Function> for Value {
|
||||
fn from(v: Function) -> Self {
|
||||
Value::Function(Box::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expression> for Value {
|
||||
fn from(v: Expression) -> Self {
|
||||
Value::Expression(Box::new(v))
|
||||
impl From<Subquery> for Value {
|
||||
fn from(v: Subquery) -> Self {
|
||||
Value::Subquery(Box::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<Expression>> for Value {
|
||||
fn from(v: Box<Expression>) -> Self {
|
||||
Value::Expression(v)
|
||||
impl From<Expression> for Value {
|
||||
fn from(v: Expression) -> Self {
|
||||
Value::Expression(Box::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -964,6 +978,7 @@ impl fmt::Display for Value {
|
|||
Value::Thing(v) => write!(f, "{}", v),
|
||||
Value::Model(v) => write!(f, "{}", v),
|
||||
Value::Regex(v) => write!(f, "{}", v),
|
||||
Value::Edges(v) => write!(f, "{}", v),
|
||||
Value::Function(v) => write!(f, "{}", v),
|
||||
Value::Subquery(v) => write!(f, "{}", v),
|
||||
Value::Expression(v) => write!(f, "{}", v),
|
||||
|
@ -1035,9 +1050,10 @@ impl Serialize for Value {
|
|||
Value::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v),
|
||||
Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v),
|
||||
Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v),
|
||||
Value::Function(v) => s.serialize_newtype_variant("Value", 18, "Function", v),
|
||||
Value::Subquery(v) => s.serialize_newtype_variant("Value", 19, "Subquery", v),
|
||||
Value::Expression(v) => s.serialize_newtype_variant("Value", 20, "Expression", v),
|
||||
Value::Edges(v) => s.serialize_newtype_variant("Value", 18, "Edges", v),
|
||||
Value::Function(v) => s.serialize_newtype_variant("Value", 19, "Function", v),
|
||||
Value::Subquery(v) => s.serialize_newtype_variant("Value", 20, "Subquery", v),
|
||||
Value::Expression(v) => s.serialize_newtype_variant("Value", 21, "Expression", v),
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
|
@ -1113,7 +1129,7 @@ pub fn value(i: &str) -> IResult<&str, Value> {
|
|||
}
|
||||
|
||||
pub fn double(i: &str) -> IResult<&str, Value> {
|
||||
map(expression, |v| Value::Expression(Box::new(v)))(i)
|
||||
map(expression, Value::from)(i)
|
||||
}
|
||||
|
||||
pub fn single(i: &str) -> IResult<&str, Value> {
|
||||
|
@ -1123,20 +1139,20 @@ pub fn single(i: &str) -> IResult<&str, Value> {
|
|||
map(tag_no_case("NULL"), |_| Value::Null),
|
||||
map(tag_no_case("true"), |_| Value::True),
|
||||
map(tag_no_case("false"), |_| Value::False),
|
||||
map(subquery, |v| Value::Subquery(Box::new(v))),
|
||||
map(function, |v| Value::Function(Box::new(v))),
|
||||
map(datetime, Value::Datetime),
|
||||
map(duration, Value::Duration),
|
||||
map(geometry, Value::Geometry),
|
||||
map(number, Value::Number),
|
||||
map(strand, Value::Strand),
|
||||
map(object, Value::Object),
|
||||
map(array, Value::Array),
|
||||
map(param, Value::Param),
|
||||
map(regex, Value::Regex),
|
||||
map(thing, Value::Thing),
|
||||
map(model, Value::Model),
|
||||
map(idiom, Value::Idiom),
|
||||
map(subquery, Value::from),
|
||||
map(function, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(geometry, Value::from),
|
||||
map(number, Value::from),
|
||||
map(strand, Value::from),
|
||||
map(object, Value::from),
|
||||
map(array, Value::from),
|
||||
map(param, Value::from),
|
||||
map(regex, Value::from),
|
||||
map(model, Value::from),
|
||||
map(idiom, Value::from),
|
||||
map(thing, Value::from),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -1147,31 +1163,33 @@ pub fn select(i: &str) -> IResult<&str, Value> {
|
|||
map(tag_no_case("NULL"), |_| Value::Null),
|
||||
map(tag_no_case("true"), |_| Value::True),
|
||||
map(tag_no_case("false"), |_| Value::False),
|
||||
map(subquery, |v| Value::Subquery(Box::new(v))),
|
||||
map(function, |v| Value::Function(Box::new(v))),
|
||||
map(datetime, Value::Datetime),
|
||||
map(duration, Value::Duration),
|
||||
map(geometry, Value::Geometry),
|
||||
map(number, Value::Number),
|
||||
map(strand, Value::Strand),
|
||||
map(object, Value::Object),
|
||||
map(array, Value::Array),
|
||||
map(param, Value::Param),
|
||||
map(regex, Value::Regex),
|
||||
map(thing, Value::Thing),
|
||||
map(model, Value::Model),
|
||||
map(table, Value::Table),
|
||||
map(subquery, Value::from),
|
||||
map(function, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(geometry, Value::from),
|
||||
map(number, Value::from),
|
||||
map(strand, Value::from),
|
||||
map(object, Value::from),
|
||||
map(array, Value::from),
|
||||
map(param, Value::from),
|
||||
map(regex, Value::from),
|
||||
map(model, Value::from),
|
||||
map(edges, Value::from),
|
||||
map(thing, Value::from),
|
||||
map(table, Value::from),
|
||||
))(i)
|
||||
}
|
||||
|
||||
pub fn what(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
map(subquery, |v| Value::Subquery(Box::new(v))),
|
||||
map(function, |v| Value::Function(Box::new(v))),
|
||||
map(param, Value::Param),
|
||||
map(model, Value::Model),
|
||||
map(thing, Value::Thing),
|
||||
map(table, Value::Table),
|
||||
map(subquery, Value::from),
|
||||
map(function, Value::from),
|
||||
map(param, Value::from),
|
||||
map(model, Value::from),
|
||||
map(edges, Value::from),
|
||||
map(thing, Value::from),
|
||||
map(table, Value::from),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -1180,13 +1198,13 @@ pub fn json(i: &str) -> IResult<&str, Value> {
|
|||
map(tag_no_case("NULL"), |_| Value::Null),
|
||||
map(tag_no_case("true"), |_| Value::True),
|
||||
map(tag_no_case("false"), |_| Value::False),
|
||||
map(datetime, Value::Datetime),
|
||||
map(duration, Value::Duration),
|
||||
map(geometry, Value::Geometry),
|
||||
map(number, Value::Number),
|
||||
map(object, Value::Object),
|
||||
map(array, Value::Array),
|
||||
map(strand, Value::Strand),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(geometry, Value::from),
|
||||
map(number, Value::from),
|
||||
map(object, Value::from),
|
||||
map(array, Value::from),
|
||||
map(strand, Value::from),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -1367,6 +1385,7 @@ mod tests {
|
|||
assert_eq!(56, std::mem::size_of::<crate::sql::thing::Thing>());
|
||||
assert_eq!(48, std::mem::size_of::<crate::sql::model::Model>());
|
||||
assert_eq!(24, std::mem::size_of::<crate::sql::regex::Regex>());
|
||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::edges::Edges>>());
|
||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::function::Function>>());
|
||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::subquery::Subquery>>());
|
||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::expression::Expression>>());
|
||||
|
|
Loading…
Reference in a new issue