diff --git a/lib/src/sql/data.rs b/lib/src/sql/data.rs index 36c352e0..ae9c7e03 100644 --- a/lib/src/sql/data.rs +++ b/lib/src/sql/data.rs @@ -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; diff --git a/lib/src/sql/ending.rs b/lib/src/sql/ending.rs index 5e814cbc..79953d7c 100644 --- a/lib/src/sql/ending.rs +++ b/lib/src/sql/ending.rs @@ -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, |_| ()), diff --git a/lib/src/sql/fetch.rs b/lib/src/sql/fetch.rs index da88ca0a..2913116f 100644 --- a/lib/src/sql/fetch.rs +++ b/lib/src/sql/fetch.rs @@ -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}; diff --git a/lib/src/sql/field.rs b/lib/src/sql/field.rs index 91077d2c..4de3bc16 100644 --- a/lib/src/sql/field.rs +++ b/lib/src/sql/field.rs @@ -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; diff --git a/lib/src/sql/function.rs b/lib/src/sql/function.rs index 8cd54ec4..37545fc8 100644 --- a/lib/src/sql/function.rs +++ b/lib/src/sql/function.rs @@ -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)?; diff --git a/lib/src/sql/graph.rs b/lib/src/sql/graph.rs index ed5de6f6..0a0a7e3a 100644 --- a/lib/src/sql/graph.rs +++ b/lib/src/sql/graph.rs @@ -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; diff --git a/lib/src/sql/ident.rs b/lib/src/sql/ident.rs index ee3e66fd..c06522ff 100644 --- a/lib/src/sql/ident.rs +++ b/lib/src/sql/ident.rs @@ -24,14 +24,14 @@ const BACKTICK_ESC: &str = r#"\`"#; pub struct Ident(pub String); impl From 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)) } } diff --git a/lib/src/sql/idiom.rs b/lib/src/sql/idiom.rs index 3cfda567..a415a155 100644 --- a/lib/src/sql/idiom.rs +++ b/lib/src/sql/idiom.rs @@ -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::>() .into() } @@ -127,14 +127,13 @@ impl Idiom { doc: Option<&Value>, ) -> Result { 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, diff --git a/lib/src/sql/param.rs b/lib/src/sql/param.rs index 4e9aeb0d..a354ca42 100644 --- a/lib/src/sql/param.rs +++ b/lib/src/sql/param.rs @@ -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 for Param { - fn from(p: Idiom) -> Self { - Self(p) +impl From for Param { + fn from(v: Ident) -> Self { + Self(v) + } +} + +impl From 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 { - // 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")); - } } diff --git a/lib/src/sql/part.rs b/lib/src/sql/part.rs index 3846d667..533fc07d 100644 --- a/lib/src/sql/part.rs +++ b/lib/src/sql/part.rs @@ -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 for Part { @@ -45,6 +46,12 @@ impl From for Part { } } +impl From for Part { + fn from(v: String) -> Self { + Self::Field(v.into()) + } +} + impl From for Part { fn from(v: Number) -> Self { Self::Index(v) @@ -63,24 +70,12 @@ impl From for Part { } } -impl From for Part { - fn from(v: Thing) -> Self { - Self::Thing(v) - } -} - impl From for Part { fn from(v: Graph) -> Self { Self::Graph(v) } } -impl From for Part { - fn from(v: String) -> Self { - Self::Field(Ident(v)) - } -} - impl From<&str> for Part { fn from(v: &str) -> Self { match v.parse::() { @@ -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))) } diff --git a/lib/src/sql/statements/relate.rs b/lib/src/sql/statements/relate.rs index 07caedf5..ee6e028b 100644 --- a/lib/src/sql/statements/relate.rs +++ b/lib/src/sql/statements/relate.rs @@ -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; diff --git a/lib/src/sql/value/get.rs b/lib/src/sql/value/get.rs index 29afc552..ac60af1a 100644 --- a/lib/src/sql/value/get.rs +++ b/lib/src/sql/value/get.rs @@ -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 diff --git a/lib/src/sql/value/put.rs b/lib/src/sql/value/put.rs index b110d5b5..ad7e8107 100644 --- a/lib/src/sql/value/put.rs +++ b/lib/src/sql/value/put.rs @@ -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), _ => { diff --git a/lib/src/sql/value/serde/ser/part/mod.rs b/lib/src/sql/value/serde/ser/part/mod.rs index 1f0897bf..c9302252 100644 --- a/lib/src/sql/value/serde/ser/part/mod.rs +++ b/lib/src/sql/value/serde/ser/part/mod.rs @@ -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); + } } diff --git a/lib/src/sql/value/serde/ser/value/mod.rs b/lib/src/sql/value/serde/ser/value/mod.rs index 2c97adb8..d52091f7 100644 --- a/lib/src/sql/value/serde/ser/value/mod.rs +++ b/lib/src/sql/value/serde/ser/value/mod.rs @@ -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 => { diff --git a/lib/src/sql/value/set.rs b/lib/src/sql/value/set.rs index 0e2899bb..586c2229 100644 --- a/lib/src/sql/value/set.rs +++ b/lib/src/sql/value/set.rs @@ -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, _ => { diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index a5cf265b..8c9dff0a 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -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 for Value { } } +impl From> for Value { + fn from(v: Box) -> Self { + Value::Edges(v) + } +} + impl From 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),