parent
6e6621565d
commit
2e0093c41d
17 changed files with 270 additions and 221 deletions
|
@ -7,7 +7,7 @@ use crate::sql::comment::shouldbespace;
|
|||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::fmt::Fmt;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{plain as idiom, Idiom};
|
||||
use crate::sql::operator::{assigner, Operator};
|
||||
use crate::sql::table::Table;
|
||||
use crate::sql::thing::Thing;
|
||||
|
|
|
@ -24,7 +24,6 @@ pub fn number(i: &str) -> IResult<&str, ()> {
|
|||
map(char('"'), |_| ()),
|
||||
map(char('\''), |_| ()),
|
||||
map(char(';'), |_| ()),
|
||||
map(char('.'), |_| ()),
|
||||
map(char(','), |_| ()),
|
||||
map(tag(".."), |_| ()),
|
||||
map(eof, |_| ()),
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::sql::comment::shouldbespace;
|
|||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::fmt::Fmt;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{plain as idiom, Idiom};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::multi::separated_list1;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::sql::common::commas;
|
|||
use crate::sql::ending::field as ending;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::fmt::Fmt;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{plain as idiom, Idiom};
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
|
|
|
@ -265,7 +265,7 @@ pub fn function(i: &str) -> IResult<&str, Function> {
|
|||
alt((normal, custom, script, cast))(i)
|
||||
}
|
||||
|
||||
fn normal(i: &str) -> IResult<&str, Function> {
|
||||
pub fn normal(i: &str) -> IResult<&str, Function> {
|
||||
let (i, s) = function_names(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
|
@ -275,7 +275,7 @@ fn normal(i: &str) -> IResult<&str, Function> {
|
|||
Ok((i, Function::Normal(s.to_string(), a)))
|
||||
}
|
||||
|
||||
fn custom(i: &str) -> IResult<&str, Function> {
|
||||
pub fn custom(i: &str) -> IResult<&str, Function> {
|
||||
let (i, _) = tag("fn::")(i)?;
|
||||
let (i, s) = take_while1(val_char)(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::sql::comment::shouldbespace;
|
|||
use crate::sql::cond::{cond, Cond};
|
||||
use crate::sql::dir::{dir, Dir};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{plain as idiom, Idiom};
|
||||
use crate::sql::table::{table, tables, Tables};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
|
|
|
@ -24,14 +24,14 @@ const BACKTICK_ESC: &str = r#"\`"#;
|
|||
pub struct Ident(pub String);
|
||||
|
||||
impl From<String> for Ident {
|
||||
fn from(s: String) -> Self {
|
||||
Self(s)
|
||||
fn from(v: String) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Ident {
|
||||
fn from(i: &str) -> Self {
|
||||
Self::from(String::from(i))
|
||||
fn from(v: &str) -> Self {
|
||||
Self::from(String::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::sql::common::commas;
|
|||
use crate::sql::error::IResult;
|
||||
use crate::sql::fmt::{fmt_separated_by, Fmt};
|
||||
use crate::sql::part::Next;
|
||||
use crate::sql::part::{all, field, first, graph, index, last, part, thing, Part};
|
||||
use crate::sql::part::{all, field, first, graph, index, last, part, value, Part};
|
||||
use crate::sql::paths::{ID, IN, OUT};
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::value::Value;
|
||||
|
@ -92,7 +92,7 @@ impl Idiom {
|
|||
self.0
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|p| matches!(p, Part::Field(_) | Part::Thing(_) | Part::Graph(_)))
|
||||
.filter(|p| matches!(p, Part::Field(_) | Part::Value(_) | Part::Graph(_)))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
@ -127,14 +127,13 @@ impl Idiom {
|
|||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
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())
|
||||
// The starting part is a value
|
||||
Some(Part::Value(v)) => {
|
||||
v.compute(ctx, opt, txn, doc)
|
||||
.await?
|
||||
.compute(ctx, opt, txn, Some(&v))
|
||||
.get(ctx, opt, txn, self.as_ref().next())
|
||||
.await?
|
||||
.compute(ctx, opt, txn, doc)
|
||||
.await
|
||||
}
|
||||
// Otherwise use the current document
|
||||
|
@ -148,7 +147,7 @@ impl Idiom {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Idiom {
|
||||
impl Display for Idiom {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt(
|
||||
&Fmt::new(
|
||||
|
@ -178,7 +177,7 @@ impl Serialize for Idiom {
|
|||
}
|
||||
}
|
||||
|
||||
/// Used in a DEFINE FIELD and DEFINE INDEX clauses
|
||||
/// Used in DEFINE FIELD and DEFINE INDEX clauses
|
||||
pub fn local(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many0(alt((all, index, field)))(i)?;
|
||||
|
@ -194,35 +193,50 @@ pub fn basic(i: &str) -> IResult<&str, Idiom> {
|
|||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
/// Used in a $param definition
|
||||
pub fn param(i: &str) -> IResult<&str, Idiom> {
|
||||
/// A simple idiom with one or more parts
|
||||
pub fn plain(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)))
|
||||
}
|
||||
|
||||
/// A complex idiom with graph or many parts
|
||||
pub fn multi(i: &str) -> IResult<&str, Idiom> {
|
||||
alt((
|
||||
|i| {
|
||||
let (i, p) = graph(i)?;
|
||||
let (i, mut v) = many0(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
},
|
||||
|i| {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many1(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
},
|
||||
|i| {
|
||||
let (i, p) = value(i)?;
|
||||
let (i, mut v) = many1(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
},
|
||||
))(i)
|
||||
}
|
||||
|
||||
/// A simple field based idiom
|
||||
pub fn path(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many0(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
/// Used in a RELATE statement
|
||||
pub fn plain(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
Ok((i, Idiom::from(vec![p])))
|
||||
}
|
||||
|
||||
/// A full complex idiom with any number of parts
|
||||
#[cfg(test)]
|
||||
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
|
||||
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)
|
||||
alt((plain, multi))(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -232,10 +246,19 @@ mod tests {
|
|||
use crate::sql::dir::Dir;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::graph::Graph;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::param::Param;
|
||||
use crate::sql::table::Table;
|
||||
use crate::sql::test::Parse;
|
||||
use crate::sql::thing::Thing;
|
||||
|
||||
#[test]
|
||||
fn idiom_number() {
|
||||
let sql = "13.495";
|
||||
let res = idiom(sql);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idiom_normal() {
|
||||
let sql = "test";
|
||||
|
@ -273,7 +296,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("test.temp", format!("{}", out));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"),]));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -283,7 +306,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("test.`some key`", format!("{}", out));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("some key"),]));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("some key")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -293,7 +316,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("test.temp[*]", format!("{}", out));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"), Part::All,]));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"), Part::All]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -303,7 +326,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("test.temp[$]", format!("{}", out));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"), Part::Last,]));
|
||||
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"), Part::Last]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -331,7 +354,7 @@ mod tests {
|
|||
Idiom(vec![
|
||||
Part::from("test"),
|
||||
Part::from("temp"),
|
||||
Part::from(Value::from(Expression::parse("test = true"))),
|
||||
Part::Where(Value::from(Expression::parse("test = true"))),
|
||||
Part::from("text")
|
||||
])
|
||||
);
|
||||
|
@ -349,12 +372,30 @@ mod tests {
|
|||
Idiom(vec![
|
||||
Part::from("test"),
|
||||
Part::from("temp"),
|
||||
Part::from(Value::from(Expression::parse("test = true"))),
|
||||
Part::Where(Value::from(Expression::parse("test = true"))),
|
||||
Part::from("text")
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idiom_start_param_local_field() {
|
||||
let sql = "$test.temporary[0].embedded";
|
||||
let res = idiom(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("$test.temporary[0].embedded", format!("{}", out));
|
||||
assert_eq!(
|
||||
out,
|
||||
Idiom(vec![
|
||||
Part::Value(Param::from("test").into()),
|
||||
Part::from("temporary"),
|
||||
Part::Index(Number::Int(0)),
|
||||
Part::from("embedded"),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idiom_start_thing_remote_traversal() {
|
||||
let sql = "person:test.friend->like->person";
|
||||
|
@ -365,15 +406,15 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom(vec![
|
||||
Part::from(Thing::from(("person", "test"))),
|
||||
Part::Value(Thing::from(("person", "test")).into()),
|
||||
Part::from("friend"),
|
||||
Part::from(Graph {
|
||||
Part::Graph(Graph {
|
||||
dir: Dir::Out,
|
||||
what: Table::from("like").into(),
|
||||
cond: None,
|
||||
alias: None,
|
||||
}),
|
||||
Part::from(Graph {
|
||||
Part::Graph(Graph {
|
||||
dir: Dir::Out,
|
||||
what: Table::from("person").into(),
|
||||
cond: None,
|
||||
|
|
|
@ -3,10 +3,7 @@ use crate::dbs::Options;
|
|||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::idiom;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::part::Next;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::value::Value;
|
||||
use nom::character::complete::char;
|
||||
|
@ -18,16 +15,28 @@ use std::str;
|
|||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Param";
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize, Hash)]
|
||||
pub struct Param(pub Idiom);
|
||||
pub struct Param(pub Ident);
|
||||
|
||||
impl From<Idiom> for Param {
|
||||
fn from(p: Idiom) -> Self {
|
||||
Self(p)
|
||||
impl From<Ident> for Param {
|
||||
fn from(v: Ident) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Param {
|
||||
fn from(v: String) -> Self {
|
||||
Self(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Param {
|
||||
fn from(v: &str) -> Self {
|
||||
Self(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Param {
|
||||
type Target = Idiom;
|
||||
type Target = Ident;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
@ -41,54 +50,36 @@ impl Param {
|
|||
txn: &Transaction,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Find a base variable by name
|
||||
match self.first() {
|
||||
// The first part will be a field
|
||||
Some(Part::Field(v)) => match v.as_str() {
|
||||
// This is a special param
|
||||
"this" | "self" => match doc {
|
||||
// The base document exists
|
||||
Some(v) => {
|
||||
// Get the path parts
|
||||
let pth: &[Part] = self;
|
||||
// Process the parameter value
|
||||
let res = v.compute(ctx, opt, txn, doc).await?;
|
||||
// Return the desired field
|
||||
res.get(ctx, opt, txn, pth.next()).await
|
||||
}
|
||||
// The base document does not exist
|
||||
None => Ok(Value::None),
|
||||
},
|
||||
// This is a normal param
|
||||
_ => match ctx.value(v) {
|
||||
// The param has been set locally
|
||||
Some(v) => {
|
||||
// Get the path parts
|
||||
let pth: &[Part] = self;
|
||||
// Process the parameter value
|
||||
let res = v.compute(ctx, opt, txn, doc).await?;
|
||||
// Return the desired field
|
||||
res.get(ctx, opt, txn, pth.next()).await
|
||||
}
|
||||
// The param has not been set locally
|
||||
None => {
|
||||
// Clone transaction
|
||||
let run = txn.clone();
|
||||
// Claim transaction
|
||||
let mut run = run.lock().await;
|
||||
// Get the param definition
|
||||
let val = run.get_pa(opt.ns(), opt.db(), v).await;
|
||||
// Check if the param has been set globally
|
||||
match val {
|
||||
// The param has been set globally
|
||||
Ok(v) => Ok(v.value),
|
||||
// The param has not been set globally
|
||||
Err(_) => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
},
|
||||
// Find the variable by name
|
||||
match self.as_str() {
|
||||
// This is a special param
|
||||
"this" | "self" => match doc {
|
||||
// The base document exists
|
||||
Some(v) => v.compute(ctx, opt, txn, doc).await,
|
||||
// The base document does not exist
|
||||
None => Ok(Value::None),
|
||||
},
|
||||
// This is a normal param
|
||||
v => match ctx.value(v) {
|
||||
// The param has been set locally
|
||||
Some(v) => v.compute(ctx, opt, txn, doc).await,
|
||||
// The param has not been set locally
|
||||
None => {
|
||||
// Clone transaction
|
||||
let run = txn.clone();
|
||||
// Claim transaction
|
||||
let mut run = run.lock().await;
|
||||
// Get the param definition
|
||||
let val = run.get_pa(opt.ns(), opt.db(), v).await;
|
||||
// Check if the param has been set globally
|
||||
match val {
|
||||
// The param has been set globally
|
||||
Ok(v) => Ok(v.value),
|
||||
// The param has not been set globally
|
||||
Err(_) => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,13 +105,7 @@ impl Serialize for Param {
|
|||
|
||||
pub fn param(i: &str) -> IResult<&str, Param> {
|
||||
let (i, _) = char('$')(i)?;
|
||||
let (i, v) = idiom::param(i)?;
|
||||
Ok((i, Param::from(v)))
|
||||
}
|
||||
|
||||
pub fn plain(i: &str) -> IResult<&str, Param> {
|
||||
let (i, _) = char('$')(i)?;
|
||||
let (i, v) = idiom::plain(i)?;
|
||||
let (i, v) = ident(i)?;
|
||||
Ok((i, Param::from(v)))
|
||||
}
|
||||
|
||||
|
@ -149,14 +134,4 @@ mod tests {
|
|||
assert_eq!("$test_and_deliver", format!("{}", out));
|
||||
assert_eq!(out, Param::parse("$test_and_deliver"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_embedded() {
|
||||
let sql = "$test.temporary[0].embedded";
|
||||
let res = param(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("$test.temporary[0].embedded", format!("{}", out));
|
||||
assert_eq!(out, Param::parse("$test.temporary[0].embedded"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
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::graph::{self, Graph};
|
||||
use crate::sql::ident::{self, 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 crate::sql::value::{self, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::char;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::peek;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
@ -23,8 +24,8 @@ pub enum Part {
|
|||
Field(Ident),
|
||||
Index(Number),
|
||||
Where(Value),
|
||||
Thing(Thing),
|
||||
Graph(Graph),
|
||||
Value(Value),
|
||||
}
|
||||
|
||||
impl From<i32> for Part {
|
||||
|
@ -45,6 +46,12 @@ impl From<usize> for Part {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<String> for Part {
|
||||
fn from(v: String) -> Self {
|
||||
Self::Field(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Number> for Part {
|
||||
fn from(v: Number) -> Self {
|
||||
Self::Index(v)
|
||||
|
@ -63,24 +70,12 @@ impl From<Value> for Part {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Thing> for Part {
|
||||
fn from(v: Thing) -> Self {
|
||||
Self::Thing(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Graph> for Part {
|
||||
fn from(v: Graph) -> Self {
|
||||
Self::Graph(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Part {
|
||||
fn from(v: String) -> Self {
|
||||
Self::Field(Ident(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Part {
|
||||
fn from(v: &str) -> Self {
|
||||
match v.parse::<isize>() {
|
||||
|
@ -109,8 +104,8 @@ 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}"),
|
||||
Part::Value(v) => write!(f, "{v}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +132,8 @@ pub fn part(i: &str) -> IResult<&str, Part> {
|
|||
}
|
||||
|
||||
pub fn first(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = ident(i)?;
|
||||
let (i, _) = peek(not(number))(i)?;
|
||||
let (i, v) = ident::ident(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Part::Field(v)))
|
||||
}
|
||||
|
@ -175,7 +171,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, v) = ident::ident(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Part::Field(v)))
|
||||
}
|
||||
|
@ -184,18 +180,18 @@ pub fn filter(i: &str) -> IResult<&str, Part> {
|
|||
let (i, _) = char('[')(i)?;
|
||||
let (i, _) = alt((tag_no_case("WHERE"), tag("?")))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
let (i, v) = value::value(i)?;
|
||||
let (i, _) = char(']')(i)?;
|
||||
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 value(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = value::start(i)?;
|
||||
Ok((i, Part::Value(v)))
|
||||
}
|
||||
|
||||
pub fn graph(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = graph_raw(i)?;
|
||||
let (i, v) = graph::graph(i)?;
|
||||
Ok((i, Part::Graph(v)))
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::sql::comment::shouldbespace;
|
|||
use crate::sql::data::{data, Data};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::param::plain as param;
|
||||
use crate::sql::param::param;
|
||||
use crate::sql::subquery::subquery;
|
||||
use crate::sql::table::table;
|
||||
use crate::sql::thing::thing;
|
||||
|
|
|
@ -97,6 +97,29 @@ impl Value {
|
|||
try_join_all(futs).await.map(Into::into)
|
||||
}
|
||||
},
|
||||
// Current path part is an edges
|
||||
Value::Edges(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::Edges(val)),
|
||||
// Remote embedded field, so fetch the thing
|
||||
_ => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All], false),
|
||||
what: Values(vec![Value::from(val)]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
stm.compute(ctx, opt, txn, None)
|
||||
.await?
|
||||
.first()
|
||||
.get(ctx, opt, txn, path)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
// Current path part is a thing
|
||||
Value::Thing(v) => {
|
||||
// Clone the thing
|
||||
|
|
|
@ -9,14 +9,6 @@ impl Value {
|
|||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Thing(t) => match v.get_mut(t.to_raw().as_str()) {
|
||||
Some(v) if v.is_some() => v.put(path.next(), val),
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
obj.put(path.next(), val);
|
||||
v.insert(t.to_raw(), obj);
|
||||
}
|
||||
},
|
||||
Part::Graph(g) => match v.get_mut(g.to_raw().as_str()) {
|
||||
Some(v) if v.is_some() => v.put(path.next(), val),
|
||||
_ => {
|
||||
|
|
|
@ -54,8 +54,8 @@ impl ser::Serializer for Serializer {
|
|||
"Field" => Ok(Part::Field(Ident(value.serialize(ser::string::Serializer.wrap())?))),
|
||||
"Index" => Ok(Part::Index(value.serialize(ser::number::Serializer.wrap())?)),
|
||||
"Where" => Ok(Part::Where(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
"Thing" => Ok(Part::Thing(value.serialize(ser::thing::Serializer.wrap())?)),
|
||||
"Graph" => Ok(Part::Graph(value.serialize(ser::graph::Serializer.wrap())?)),
|
||||
"Value" => Ok(Part::Value(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
variant => {
|
||||
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
|
||||
}
|
||||
|
@ -113,17 +113,17 @@ mod tests {
|
|||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thing() {
|
||||
let part = Part::Thing(sql::thing("foo:bar").unwrap());
|
||||
let serialized = serialize_internal(|| part.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph() {
|
||||
let part = Part::Graph(Default::default());
|
||||
let serialized = serialize_internal(|| part.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value() {
|
||||
let part = Part::Value(sql::thing("foo:bar").unwrap().into());
|
||||
let serialized = serialize_internal(|| part.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::sql::Block;
|
|||
use crate::sql::Datetime;
|
||||
use crate::sql::Duration;
|
||||
use crate::sql::Future;
|
||||
use crate::sql::Ident;
|
||||
use crate::sql::Idiom;
|
||||
use crate::sql::Param;
|
||||
use crate::sql::Regex;
|
||||
|
@ -222,7 +223,7 @@ impl ser::Serializer for Serializer {
|
|||
Ok(Value::Idiom(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?)))
|
||||
}
|
||||
sql::param::TOKEN => {
|
||||
Ok(Value::Param(Param(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?))))
|
||||
Ok(Value::Param(Param(Ident(value.serialize(ser::string::Serializer.wrap())?))))
|
||||
}
|
||||
sql::array::TOKEN => Ok(Value::Array(Array(value.serialize(vec::Serializer.wrap())?))),
|
||||
sql::object::TOKEN => {
|
||||
|
|
|
@ -24,15 +24,6 @@ impl Value {
|
|||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
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,
|
||||
_ => {
|
||||
|
|
|
@ -14,11 +14,11 @@ use crate::sql::edges::{edges, Edges};
|
|||
use crate::sql::error::IResult;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::fmt::{Fmt, Pretty};
|
||||
use crate::sql::function::{function, Function};
|
||||
use crate::sql::function::{self, function, Function};
|
||||
use crate::sql::future::{future, Future};
|
||||
use crate::sql::geometry::{geometry, Geometry};
|
||||
use crate::sql::id::Id;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{self, Idiom};
|
||||
use crate::sql::kind::Kind;
|
||||
use crate::sql::model::{model, Model};
|
||||
use crate::sql::number::{number, Number};
|
||||
|
@ -280,6 +280,12 @@ impl From<Expression> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Box<Edges>> for Value {
|
||||
fn from(v: Box<Edges>) -> Self {
|
||||
Value::Edges(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i8> for Value {
|
||||
fn from(v: i8) -> Self {
|
||||
Value::Number(Number::from(v))
|
||||
|
@ -962,8 +968,8 @@ impl Value {
|
|||
/// Converts this Value into a field name
|
||||
pub fn to_idiom(&self) -> Idiom {
|
||||
match self {
|
||||
Value::Param(v) => v.simplify(),
|
||||
Value::Idiom(v) => v.simplify(),
|
||||
Value::Param(v) => v.to_raw().into(),
|
||||
Value::Strand(v) => v.0.to_string().into(),
|
||||
Value::Datetime(v) => v.0.to_string().into(),
|
||||
Value::Future(_) => "future".to_string().into(),
|
||||
|
@ -1545,14 +1551,12 @@ impl ops::Div for Value {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse any `Value` including binary expressions
|
||||
pub fn value(i: &str) -> IResult<&str, Value> {
|
||||
alt((double, single))(i)
|
||||
}
|
||||
|
||||
pub fn double(i: &str) -> IResult<&str, Value> {
|
||||
map(expression, Value::from)(i)
|
||||
alt((map(expression, Value::from), single))(i)
|
||||
}
|
||||
|
||||
/// Parse any `Value` excluding binary expressions
|
||||
pub fn single(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
alt((
|
||||
|
@ -1562,41 +1566,9 @@ pub fn single(i: &str) -> IResult<&str, Value> {
|
|||
map(tag_no_case("false"), |_| Value::False),
|
||||
)),
|
||||
alt((
|
||||
map(subquery, Value::from),
|
||||
map(idiom::multi, Value::from),
|
||||
map(function, Value::from),
|
||||
map(constant, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(geometry, Value::from),
|
||||
map(future, Value::from),
|
||||
map(unique, Value::from),
|
||||
map(number, Value::from),
|
||||
map(object, Value::from),
|
||||
map(array, Value::from),
|
||||
map(block, Value::from),
|
||||
map(param, Value::from),
|
||||
map(regex, Value::from),
|
||||
map(model, Value::from),
|
||||
map(idiom, Value::from),
|
||||
map(range, Value::from),
|
||||
map(thing, Value::from),
|
||||
map(strand, Value::from),
|
||||
)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
pub fn select(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
alt((
|
||||
map(tag_no_case("NONE"), |_| Value::None),
|
||||
map(tag_no_case("NULL"), |_| Value::Null),
|
||||
map(tag_no_case("true"), |_| Value::True),
|
||||
map(tag_no_case("false"), |_| Value::False),
|
||||
)),
|
||||
alt((
|
||||
map(expression, Value::from),
|
||||
map(subquery, Value::from),
|
||||
map(function, Value::from),
|
||||
map(constant, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
|
@ -1613,17 +1585,75 @@ pub fn select(i: &str) -> IResult<&str, Value> {
|
|||
map(edges, Value::from),
|
||||
map(range, Value::from),
|
||||
map(thing, Value::from),
|
||||
map(table, Value::from),
|
||||
map(strand, Value::from),
|
||||
map(idiom::path, Value::from),
|
||||
)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
pub fn select(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
alt((
|
||||
map(expression, Value::from),
|
||||
map(tag_no_case("NONE"), |_| Value::None),
|
||||
map(tag_no_case("NULL"), |_| Value::Null),
|
||||
map(tag_no_case("true"), |_| Value::True),
|
||||
map(tag_no_case("false"), |_| Value::False),
|
||||
)),
|
||||
alt((
|
||||
map(idiom::multi, Value::from),
|
||||
map(function, Value::from),
|
||||
map(subquery, Value::from),
|
||||
map(constant, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(geometry, Value::from),
|
||||
map(future, Value::from),
|
||||
map(unique, Value::from),
|
||||
map(number, Value::from),
|
||||
map(strand, Value::from),
|
||||
map(object, Value::from),
|
||||
map(array, Value::from),
|
||||
map(block, Value::from),
|
||||
map(param, Value::from),
|
||||
map(regex, Value::from),
|
||||
map(model, Value::from),
|
||||
map(edges, Value::from),
|
||||
map(range, Value::from),
|
||||
map(thing, Value::from),
|
||||
map(table, Value::from),
|
||||
)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
/// Used as the starting part of a complex Idiom
|
||||
pub fn start(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
map(function::normal, Value::from),
|
||||
map(function::custom, Value::from),
|
||||
map(subquery, Value::from),
|
||||
map(constant, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(unique, Value::from),
|
||||
map(number, Value::from),
|
||||
map(strand, Value::from),
|
||||
map(object, Value::from),
|
||||
map(array, Value::from),
|
||||
map(param, Value::from),
|
||||
map(edges, Value::from),
|
||||
map(thing, Value::from),
|
||||
))(i)
|
||||
}
|
||||
|
||||
/// Used in CREATE, UPDATE, and DELETE clauses
|
||||
pub fn what(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
map(subquery, Value::from),
|
||||
map(function, Value::from),
|
||||
map(subquery, Value::from),
|
||||
map(constant, Value::from),
|
||||
map(datetime, Value::from),
|
||||
map(duration, Value::from),
|
||||
map(future, Value::from),
|
||||
map(block, Value::from),
|
||||
map(param, Value::from),
|
||||
|
@ -1635,6 +1665,7 @@ pub fn what(i: &str) -> IResult<&str, Value> {
|
|||
))(i)
|
||||
}
|
||||
|
||||
/// Used to parse any simple JSON-like value
|
||||
pub fn json(i: &str) -> IResult<&str, Value> {
|
||||
alt((
|
||||
map(tag_no_case("NULL"), |_| Value::Null),
|
||||
|
|
Loading…
Reference in a new issue