Improve SQL Idiom path parsing

Closes #1653
This commit is contained in:
Tobie Morgan Hitchcock 2023-03-31 16:42:29 +01:00
parent 6e6621565d
commit 2e0093c41d
17 changed files with 270 additions and 221 deletions

View file

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

View file

@ -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, |_| ()),

View file

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

View file

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

View file

@ -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)?;

View file

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

View file

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

View file

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

View file

@ -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"));
}
}

View file

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

View file

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

View file

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

View file

@ -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),
_ => {

View file

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

View file

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

View file

@ -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,
_ => {

View file

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