Feature - unary operators (#2139)

This commit is contained in:
Finn Bear 2023-06-20 16:31:23 -07:00 committed by GitHub
parent 64adb2e913
commit 41e119a5f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 499 additions and 131 deletions

View file

@ -321,7 +321,7 @@ impl<'a> Document<'a> {
Operator::Equal, Operator::Equal,
Value::Subquery(Box::new(Subquery::Ifelse(IfelseStatement { Value::Subquery(Box::new(Subquery::Ifelse(IfelseStatement {
exprs: vec![( exprs: vec![(
Value::Expression(Box::new(Expression { Value::Expression(Box::new(Expression::Binary {
l: Value::Idiom(key.clone()), l: Value::Idiom(key.clone()),
o: Operator::MoreThan, o: Operator::MoreThan,
r: val.clone(), r: val.clone(),
@ -341,7 +341,7 @@ impl<'a> Document<'a> {
Operator::Equal, Operator::Equal,
Value::Subquery(Box::new(Subquery::Ifelse(IfelseStatement { Value::Subquery(Box::new(Subquery::Ifelse(IfelseStatement {
exprs: vec![( exprs: vec![(
Value::Expression(Box::new(Expression { Value::Expression(Box::new(Expression::Binary {
l: Value::Idiom(key.clone()), l: Value::Idiom(key.clone()),
o: Operator::LessThan, o: Operator::LessThan,
r: val.clone(), r: val.clone(),
@ -363,13 +363,13 @@ impl<'a> Document<'a> {
ops.push(( ops.push((
key.clone(), key.clone(),
Operator::Equal, Operator::Equal,
Value::Expression(Box::new(Expression { Value::Expression(Box::new(Expression::Binary {
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new( l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
Expression { Expression::Binary {
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new( l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
Expression { Expression::Binary {
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression( l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(
Box::new(Expression { Box::new(Expression::Binary {
l: Value::Idiom(key), l: Value::Idiom(key),
o: Operator::Nco, o: Operator::Nco,
r: Value::Number(Number::Int(0)), r: Value::Number(Number::Int(0)),
@ -377,7 +377,7 @@ impl<'a> Document<'a> {
)))), )))),
o: Operator::Mul, o: Operator::Mul,
r: Value::Subquery(Box::new(Subquery::Value(Value::Expression( r: Value::Subquery(Box::new(Subquery::Value(Value::Expression(
Box::new(Expression { Box::new(Expression::Binary {
l: Value::Idiom(key_c.clone()), l: Value::Idiom(key_c.clone()),
o: Operator::Nco, o: Operator::Nco,
r: Value::Number(Number::Int(0)), r: Value::Number(Number::Int(0)),
@ -395,9 +395,9 @@ impl<'a> Document<'a> {
))))), ))))),
o: Operator::Div, o: Operator::Div,
r: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new( r: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
Expression { Expression::Binary {
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new( l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
Expression { Expression::Binary {
l: Value::Idiom(key_c.clone()), l: Value::Idiom(key_c.clone()),
o: Operator::Nco, o: Operator::Nco,
r: Value::Number(Number::Int(0)), r: Value::Number(Number::Int(0)),

View file

@ -430,6 +430,10 @@ pub enum Error {
#[error("Cannot raise the value '{0}' with '{1}'")] #[error("Cannot raise the value '{0}' with '{1}'")]
TryPow(String, String), TryPow(String, String),
/// Cannot perform negation
#[error("Cannot negate the value '{0}'")]
TryNeg(String),
/// It's is not possible to convert between the two types /// It's is not possible to convert between the two types
#[error("Cannot convert from '{0}' to '{1}'")] #[error("Cannot convert from '{0}' to '{1}'")]
TryFrom(String, &'static str), TryFrom(String, &'static str),

View file

@ -3,11 +3,20 @@ use crate::err::Error;
use crate::sql::value::TryAdd; use crate::sql::value::TryAdd;
use crate::sql::value::TryDiv; use crate::sql::value::TryDiv;
use crate::sql::value::TryMul; use crate::sql::value::TryMul;
use crate::sql::value::TryNeg;
use crate::sql::value::TryPow; use crate::sql::value::TryPow;
use crate::sql::value::TrySub; use crate::sql::value::TrySub;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Expression; use crate::sql::Expression;
pub fn neg(a: Value) -> Result<Value, Error> {
a.try_neg()
}
pub fn not(a: Value) -> Result<Value, Error> {
super::not::not((a,))
}
pub fn or(a: Value, b: Value) -> Result<Value, Error> { pub fn or(a: Value, b: Value) -> Result<Value, Error> {
Ok(match a.is_truthy() { Ok(match a.is_truthy() {
true => a, true => a,

View file

@ -87,17 +87,30 @@ impl<'a> TreeBuilder<'a> {
} }
async fn eval_expression(&mut self, e: &Expression) -> Result<Node, Error> { async fn eval_expression(&mut self, e: &Expression) -> Result<Node, Error> {
let left = self.eval_value(&e.l).await?; match e {
let right = self.eval_value(&e.r).await?; Expression::Unary {
..
} => {
return Err(Error::FeatureNotYetImplemented {
feature: "unary expressions in index",
});
}
Expression::Binary {
l,
o,
r,
} => {
let left = self.eval_value(l).await?;
let right = self.eval_value(r).await?;
let mut index_option = None; let mut index_option = None;
if let Some(ix) = left.is_indexed_field() { if let Some(ix) = left.is_indexed_field() {
if let Some(io) = IndexOption::found(ix, &e.o, &right, e) { if let Some(io) = IndexOption::found(ix, o, &right, e) {
index_option = Some(io.clone()); index_option = Some(io.clone());
self.add_index(e, io); self.add_index(e, io);
} }
} }
if let Some(ix) = right.is_indexed_field() { if let Some(ix) = right.is_indexed_field() {
if let Some(io) = IndexOption::found(ix, &e.o, &left, e) { if let Some(io) = IndexOption::found(ix, o, &left, e) {
index_option = Some(io.clone()); index_option = Some(io.clone());
self.add_index(e, io); self.add_index(e, io);
} }
@ -106,9 +119,11 @@ impl<'a> TreeBuilder<'a> {
index_option, index_option,
left: Box::new(left), left: Box::new(left),
right: Box::new(right), right: Box::new(right),
operator: e.o.to_owned(), operator: o.to_owned(),
}) })
} }
}
}
fn add_index(&mut self, e: &Expression, io: IndexOption) { fn add_index(&mut self, e: &Expression, io: IndexOption) {
match self.index_map.entry(e.clone()) { match self.index_map.entry(e.clone()) {

View file

@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Cast";
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Cast")] #[serde(rename = "$surrealdb::private::sql::Cast")]
pub struct Cast(pub Kind, pub Value); pub struct Cast(pub Kind, pub Value);

View file

@ -1,7 +1,7 @@
use crate::sql::comment::comment; use crate::sql::comment::comment;
use crate::sql::comment::{mightbespace, shouldbespace}; use crate::sql::comment::{mightbespace, shouldbespace};
use crate::sql::error::IResult; use crate::sql::error::IResult;
use crate::sql::operator::{assigner, operator}; use crate::sql::operator::{assigner, binary};
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
@ -15,7 +15,7 @@ use nom::sequence::preceded;
pub fn number(i: &str) -> IResult<&str, ()> { pub fn number(i: &str) -> IResult<&str, ()> {
peek(alt(( peek(alt((
map(multispace1, |_| ()), map(multispace1, |_| ()),
map(operator, |_| ()), map(binary, |_| ()),
map(assigner, |_| ()), map(assigner, |_| ()),
map(comment, |_| ()), map(comment, |_| ()),
map(char(')'), |_| ()), map(char(')'), |_| ()),
@ -33,7 +33,7 @@ pub fn number(i: &str) -> IResult<&str, ()> {
pub fn ident(i: &str) -> IResult<&str, ()> { pub fn ident(i: &str) -> IResult<&str, ()> {
peek(alt(( peek(alt((
map(multispace1, |_| ()), map(multispace1, |_| ()),
map(operator, |_| ()), map(binary, |_| ()),
map(assigner, |_| ()), map(assigner, |_| ()),
map(comment, |_| ()), map(comment, |_| ()),
map(char(')'), |_| ()), map(char(')'), |_| ()),
@ -51,7 +51,7 @@ pub fn ident(i: &str) -> IResult<&str, ()> {
pub fn duration(i: &str) -> IResult<&str, ()> { pub fn duration(i: &str) -> IResult<&str, ()> {
peek(alt(( peek(alt((
map(multispace1, |_| ()), map(multispace1, |_| ()),
map(operator, |_| ()), map(binary, |_| ()),
map(assigner, |_| ()), map(assigner, |_| ()),
map(comment, |_| ()), map(comment, |_| ()),
map(char(')'), |_| ()), map(char(')'), |_| ()),

View file

@ -2,8 +2,9 @@ use crate::ctx::Context;
use crate::dbs::Options; use crate::dbs::Options;
use crate::err::Error; use crate::err::Error;
use crate::fnc; use crate::fnc;
use crate::sql::comment::mightbespace;
use crate::sql::error::IResult; use crate::sql::error::IResult;
use crate::sql::operator::{operator, Operator}; use crate::sql::operator::{self, Operator};
use crate::sql::value::{single, value, Value}; use crate::sql::value::{single, value, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
@ -11,17 +12,24 @@ use std::str;
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Expression"; pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Expression";
/// Binary expressions.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Expression")] #[serde(rename = "$surrealdb::private::sql::Expression")]
pub struct Expression { pub enum Expression {
pub l: Value, Unary {
pub o: Operator, o: Operator,
pub r: Value, v: Value,
},
Binary {
l: Value,
o: Operator,
r: Value,
},
} }
impl Default for Expression { impl Default for Expression {
fn default() -> Expression { fn default() -> Expression {
Expression { Expression::Binary {
l: Value::Null, l: Value::Null,
o: Operator::default(), o: Operator::default(),
r: Value::Null, r: Value::Null,
@ -30,9 +38,9 @@ impl Default for Expression {
} }
impl Expression { impl Expression {
/// Create a new expression /// Create a new binary expression
pub(crate) fn new(l: Value, o: Operator, r: Value) -> Self { pub(crate) fn new(l: Value, o: Operator, r: Value) -> Self {
Self { Self::Binary {
l, l,
o, o,
r, r,
@ -40,29 +48,67 @@ impl Expression {
} }
/// Augment an existing expression /// Augment an existing expression
fn augment(mut self, l: Value, o: Operator) -> Self { fn augment(mut self, l: Value, o: Operator) -> Self {
if o.precedence() >= self.o.precedence() { match &mut self {
match self.l { Self::Binary {
l: left,
o: op,
..
} if o.precedence() >= op.precedence() => match left {
Value::Expression(x) => { Value::Expression(x) => {
self.l = x.augment(l, o).into(); *x.as_mut() = std::mem::take(x).augment(l, o);
self self
} }
_ => { _ => {
self.l = Self::new(l, o, self.l).into(); *left = Self::new(l, o, std::mem::take(left)).into();
self self
} }
} },
} else { e => {
let r = Value::from(self); let r = Value::from(std::mem::take(e));
Self::new(l, o, r) Self::new(l, o, r)
} }
} }
} }
}
impl Expression { impl Expression {
pub(crate) fn writeable(&self) -> bool {
match self {
Self::Unary {
v,
..
} => v.writeable(),
Self::Binary {
l,
r,
..
} => l.writeable() || r.writeable(),
}
}
/// Process this type returning a computed simple Value /// Process this type returning a computed simple Value
pub(crate) async fn compute(&self, ctx: &Context<'_>, opt: &Options) -> Result<Value, Error> { pub(crate) async fn compute(&self, ctx: &Context<'_>, opt: &Options) -> Result<Value, Error> {
let l = self.l.compute(ctx, opt).await?; let (l, o, r) = match self {
match self.o { Self::Unary {
o,
v,
} => {
let operand = v.compute(ctx, opt).await?;
return match o {
Operator::Neg => fnc::operate::neg(operand),
Operator::Not => fnc::operate::not(operand),
op => unreachable!("{op:?} is not a unary op"),
};
}
Self::Binary {
l,
o,
r,
} => (l, o, r),
};
let l = l.compute(ctx, opt).await?;
match o {
Operator::Or => { Operator::Or => {
if let true = l.is_truthy() { if let true = l.is_truthy() {
return Ok(l); return Ok(l);
@ -85,8 +131,8 @@ impl Expression {
} }
_ => {} // Continue _ => {} // Continue
} }
let r = self.r.compute(ctx, opt).await?; let r = r.compute(ctx, opt).await?;
match self.o { match o {
Operator::Or => fnc::operate::or(l, r), Operator::Or => fnc::operate::or(l, r),
Operator::And => fnc::operate::and(l, r), Operator::And => fnc::operate::and(l, r),
Operator::Tco => fnc::operate::tco(l, r), Operator::Tco => fnc::operate::tco(l, r),
@ -129,13 +175,36 @@ impl Expression {
impl fmt::Display for Expression { impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.l, self.o, self.r) match self {
Self::Unary {
o,
v,
} => write!(f, "{o}{v}"),
Self::Binary {
l,
o,
r,
} => write!(f, "{l} {o} {r}"),
}
} }
} }
pub fn expression(i: &str) -> IResult<&str, Expression> { pub fn unary(i: &str) -> IResult<&str, Expression> {
let (i, o) = operator::unary(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = single(i)?;
Ok((
i,
Expression::Unary {
o,
v,
},
))
}
pub fn binary(i: &str) -> IResult<&str, Expression> {
let (i, l) = single(i)?; let (i, l) = single(i)?;
let (i, o) = operator(i)?; let (i, o) = operator::binary(i)?;
let (i, r) = value(i)?; let (i, r) = value(i)?;
let v = match r { let v = match r {
Value::Expression(r) => r.augment(l, o), Value::Expression(r) => r.augment(l, o),
@ -152,7 +221,7 @@ mod tests {
#[test] #[test]
fn expression_statement() { fn expression_statement() {
let sql = "true AND false"; let sql = "true AND false";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("true AND false", format!("{}", out)); assert_eq!("true AND false", format!("{}", out));
@ -161,7 +230,7 @@ mod tests {
#[test] #[test]
fn expression_left_opened() { fn expression_left_opened() {
let sql = "3 * 3 * 3 = 27"; let sql = "3 * 3 * 3 = 27";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("3 * 3 * 3 = 27", format!("{}", out)); assert_eq!("3 * 3 * 3 = 27", format!("{}", out));
@ -170,7 +239,7 @@ mod tests {
#[test] #[test]
fn expression_left_closed() { fn expression_left_closed() {
let sql = "(3 * 3 * 3) = 27"; let sql = "(3 * 3 * 3) = 27";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("(3 * 3 * 3) = 27", format!("{}", out)); assert_eq!("(3 * 3 * 3) = 27", format!("{}", out));
@ -179,7 +248,7 @@ mod tests {
#[test] #[test]
fn expression_right_opened() { fn expression_right_opened() {
let sql = "27 = 3 * 3 * 3"; let sql = "27 = 3 * 3 * 3";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("27 = 3 * 3 * 3", format!("{}", out)); assert_eq!("27 = 3 * 3 * 3", format!("{}", out));
@ -188,7 +257,7 @@ mod tests {
#[test] #[test]
fn expression_right_closed() { fn expression_right_closed() {
let sql = "27 = (3 * 3 * 3)"; let sql = "27 = (3 * 3 * 3)";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("27 = (3 * 3 * 3)", format!("{}", out)); assert_eq!("27 = (3 * 3 * 3)", format!("{}", out));
@ -197,7 +266,7 @@ mod tests {
#[test] #[test]
fn expression_both_opened() { fn expression_both_opened() {
let sql = "3 * 3 * 3 = 3 * 3 * 3"; let sql = "3 * 3 * 3 = 3 * 3 * 3";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("3 * 3 * 3 = 3 * 3 * 3", format!("{}", out)); assert_eq!("3 * 3 * 3 = 3 * 3 * 3", format!("{}", out));
@ -206,9 +275,27 @@ mod tests {
#[test] #[test]
fn expression_both_closed() { fn expression_both_closed() {
let sql = "(3 * 3 * 3) = (3 * 3 * 3)"; let sql = "(3 * 3 * 3) = (3 * 3 * 3)";
let res = expression(sql); let res = binary(sql);
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("(3 * 3 * 3) = (3 * 3 * 3)", format!("{}", out)); assert_eq!("(3 * 3 * 3) = (3 * 3 * 3)", format!("{}", out));
} }
#[test]
fn expression_unary() {
let sql = "-a";
let res = unary(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out));
}
#[test]
fn expression_with_unary() {
let sql = "-(5) + 5";
let res = binary(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out));
}
} }

View file

@ -16,7 +16,7 @@ use std::fmt::{self, Display, Formatter};
use std::hash; use std::hash;
use std::iter::Product; use std::iter::Product;
use std::iter::Sum; use std::iter::Sum;
use std::ops; use std::ops::{self, Neg};
use std::str::FromStr; use std::str::FromStr;
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Number"; pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Number";
@ -572,6 +572,18 @@ impl<'a, 'b> ops::Div<&'b Number> for &'a Number {
} }
} }
impl Neg for Number {
type Output = Self;
fn neg(self) -> Self::Output {
match self {
Self::Int(n) => Number::Int(-n),
Self::Float(n) => Number::Float(-n),
Self::Decimal(n) => Number::Decimal(-n),
}
}
}
// ------------------------------ // ------------------------------
impl Sum<Self> for Number { impl Sum<Self> for Number {

View file

@ -11,8 +11,12 @@ use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
/// Binary operators.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
pub enum Operator { pub enum Operator {
//
Neg, // -
Not, // !
// //
Or, // || Or, // ||
And, // && And, // &&
@ -70,14 +74,14 @@ impl Operator {
#[inline] #[inline]
pub fn precedence(&self) -> u8 { pub fn precedence(&self) -> u8 {
match self { match self {
Operator::Or => 1, Self::Or => 1,
Operator::And => 2, Self::And => 2,
Operator::Tco => 3, Self::Tco => 3,
Operator::Nco => 4, Self::Nco => 4,
Operator::Sub => 6, Self::Sub => 6,
Operator::Add => 7, Self::Add => 7,
Operator::Mul => 8, Self::Mul => 8,
Operator::Div => 9, Self::Div => 9,
_ => 5, _ => 5,
} }
} }
@ -86,6 +90,8 @@ impl Operator {
impl fmt::Display for Operator { impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Self::Neg => f.write_str("-"),
Self::Not => f.write_str("!"),
Self::Or => f.write_str("OR"), Self::Or => f.write_str("OR"),
Self::And => f.write_str("AND"), Self::And => f.write_str("AND"),
Self::Tco => f.write_str("?:"), Self::Tco => f.write_str("?:"),
@ -143,11 +149,23 @@ pub fn assigner(i: &str) -> IResult<&str, Operator> {
))(i) ))(i)
} }
pub fn operator(i: &str) -> IResult<&str, Operator> { pub fn unary(i: &str) -> IResult<&str, Operator> {
alt((symbols, phrases))(i) unary_symbols(i)
} }
pub fn symbols(i: &str) -> IResult<&str, Operator> { pub fn unary_symbols(i: &str) -> IResult<&str, Operator> {
let (i, _) = mightbespace(i)?;
let (i, v) =
alt((alt((map(tag("-"), |_| Operator::Neg), map(tag("!"), |_| Operator::Not))),))(i)?;
let (i, _) = mightbespace(i)?;
Ok((i, v))
}
pub fn binary(i: &str) -> IResult<&str, Operator> {
alt((binary_symbols, binary_phrases))(i)
}
pub fn binary_symbols(i: &str) -> IResult<&str, Operator> {
let (i, _) = mightbespace(i)?; let (i, _) = mightbespace(i)?;
let (i, v) = alt(( let (i, v) = alt((
alt(( alt((
@ -203,7 +221,7 @@ pub fn symbols(i: &str) -> IResult<&str, Operator> {
Ok((i, v)) Ok((i, v))
} }
pub fn phrases(i: &str) -> IResult<&str, Operator> { pub fn binary_phrases(i: &str) -> IResult<&str, Operator> {
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, v) = alt(( let (i, v) = alt((
alt(( alt((

View file

@ -1,5 +1,5 @@
use crate::sql::array::{array, Array}; use crate::sql::array::{array, Array};
use crate::sql::expression::{expression, Expression}; use crate::sql::expression::{binary, Expression};
use crate::sql::idiom::{idiom, Idiom}; use crate::sql::idiom::{idiom, Idiom};
use crate::sql::param::{param, Param}; use crate::sql::param::{param, Param};
use crate::sql::script::{script, Script}; use crate::sql::script::{script, Script};
@ -48,6 +48,6 @@ impl Parse<Self> for Thing {
impl Parse<Self> for Expression { impl Parse<Self> for Expression {
fn parse(val: &str) -> Self { fn parse(val: &str) -> Self {
expression(val).unwrap().1 binary(val).unwrap().1
} }
} }

View file

@ -19,29 +19,104 @@ impl ser::Serializer for Serializer {
type SerializeTupleStruct = Impossible<Expression, Error>; type SerializeTupleStruct = Impossible<Expression, Error>;
type SerializeTupleVariant = Impossible<Expression, Error>; type SerializeTupleVariant = Impossible<Expression, Error>;
type SerializeMap = Impossible<Expression, Error>; type SerializeMap = Impossible<Expression, Error>;
type SerializeStruct = SerializeExpression; type SerializeStruct = Impossible<Expression, Error>;
type SerializeStructVariant = Impossible<Expression, Error>; type SerializeStructVariant = SerializeExpression;
const EXPECTED: &'static str = "a struct `Expression`"; const EXPECTED: &'static str = "an enum `Expression`";
#[inline] #[inline]
fn serialize_struct( fn serialize_struct_variant(
self, self,
_name: &'static str, name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize, _len: usize,
) -> Result<Self::SerializeStruct, Error> { ) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(SerializeExpression::default()) debug_assert_eq!(name, crate::sql::expression::TOKEN);
match variant {
"Unary" => Ok(SerializeExpression::Unary(Default::default())),
"Binary" => Ok(SerializeExpression::Binary(Default::default())),
_ => Err(Error::custom(format!("unexpected `Expression::{name}`"))),
}
}
}
pub(super) enum SerializeExpression {
Unary(SerializeUnary),
Binary(SerializeBinary),
}
impl serde::ser::SerializeStructVariant for SerializeExpression {
type Ok = Expression;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
match self {
Self::Unary(unary) => unary.serialize_field(key, value),
Self::Binary(binary) => binary.serialize_field(key, value),
}
}
fn end(self) -> Result<Self::Ok, Error> {
match self {
Self::Unary(unary) => unary.end(),
Self::Binary(binary) => binary.end(),
}
} }
} }
#[derive(Default)] #[derive(Default)]
pub(super) struct SerializeExpression { pub(super) struct SerializeUnary {
o: Option<Operator>,
v: Option<Value>,
}
impl serde::ser::SerializeStructVariant for SerializeUnary {
type Ok = Expression;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
match key {
"o" => {
self.o = Some(value.serialize(ser::operator::Serializer.wrap())?);
}
"v" => {
self.v = Some(value.serialize(ser::value::Serializer.wrap())?);
}
key => {
return Err(Error::custom(format!(
"unexpected field `Expression::Unary{{{key}}}`"
)));
}
}
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
match (self.o, self.v) {
(Some(o), Some(v)) => Ok(Expression::Unary {
o,
v,
}),
_ => Err(Error::custom("`Expression::Unary` missing required field(s)")),
}
}
}
#[derive(Default)]
pub(super) struct SerializeBinary {
l: Option<Value>, l: Option<Value>,
o: Option<Operator>, o: Option<Operator>,
r: Option<Value>, r: Option<Value>,
} }
impl serde::ser::SerializeStruct for SerializeExpression { impl serde::ser::SerializeStructVariant for SerializeBinary {
type Ok = Expression; type Ok = Expression;
type Error = Error; type Error = Error;
@ -60,7 +135,9 @@ impl serde::ser::SerializeStruct for SerializeExpression {
self.r = Some(value.serialize(ser::value::Serializer.wrap())?); self.r = Some(value.serialize(ser::value::Serializer.wrap())?);
} }
key => { key => {
return Err(Error::custom(format!("unexpected field `Expression::{key}`"))); return Err(Error::custom(format!(
"unexpected field `Expression::Binary{{{key}}}`"
)));
} }
} }
Ok(()) Ok(())
@ -68,12 +145,12 @@ impl serde::ser::SerializeStruct for SerializeExpression {
fn end(self) -> Result<Self::Ok, Error> { fn end(self) -> Result<Self::Ok, Error> {
match (self.l, self.o, self.r) { match (self.l, self.o, self.r) {
(Some(l), Some(o), Some(r)) => Ok(Expression { (Some(l), Some(o), Some(r)) => Ok(Expression::Binary {
l, l,
o, o,
r, r,
}), }),
_ => Err(Error::custom("`Expression` missing required field(s)")), _ => Err(Error::custom("`Expression::Binary` missing required field(s)")),
} }
} }
} }
@ -90,9 +167,19 @@ mod tests {
assert_eq!(expression, serialized); assert_eq!(expression, serialized);
} }
#[test]
fn unary() {
let expression = Expression::Unary {
o: Operator::Not,
v: "Bar".into(),
};
let serialized = expression.serialize(Serializer.wrap()).unwrap();
assert_eq!(expression, serialized);
}
#[test] #[test]
fn foo_equals_bar() { fn foo_equals_bar() {
let expression = Expression { let expression = Expression::Binary {
l: "foo".into(), l: "foo".into(),
o: Operator::Equal, o: Operator::Equal,
r: "Bar".into(), r: "Bar".into(),

View file

@ -28,6 +28,8 @@ impl ser::Serializer for Serializer {
variant: &'static str, variant: &'static str,
) -> Result<Self::Ok, Error> { ) -> Result<Self::Ok, Error> {
match variant { match variant {
"Neg" => Ok(Operator::Neg),
"Not" => Ok(Operator::Not),
"Or" => Ok(Operator::Or), "Or" => Ok(Operator::Or),
"And" => Ok(Operator::And), "And" => Ok(Operator::And),
"Tco" => Ok(Operator::Tco), "Tco" => Ok(Operator::Tco),

View file

@ -20,6 +20,7 @@ use crate::sql::Table;
use crate::sql::Uuid; use crate::sql::Uuid;
use map::SerializeValueMap; use map::SerializeValueMap;
use rust_decimal::Decimal; use rust_decimal::Decimal;
use ser::cast::SerializeCast;
use ser::edges::SerializeEdges; use ser::edges::SerializeEdges;
use ser::expression::SerializeExpression; use ser::expression::SerializeExpression;
use ser::function::SerializeFunction; use ser::function::SerializeFunction;
@ -60,7 +61,7 @@ impl ser::Serializer for Serializer {
type SerializeSeq = SerializeArray; type SerializeSeq = SerializeArray;
type SerializeTuple = SerializeArray; type SerializeTuple = SerializeArray;
type SerializeTupleStruct = SerializeArray; type SerializeTupleStruct = SerializeTupleStruct;
type SerializeTupleVariant = SerializeTupleVariant; type SerializeTupleVariant = SerializeTupleVariant;
type SerializeMap = SerializeMap; type SerializeMap = SerializeMap;
type SerializeStruct = SerializeStruct; type SerializeStruct = SerializeStruct;
@ -299,10 +300,13 @@ impl ser::Serializer for Serializer {
fn serialize_tuple_struct( fn serialize_tuple_struct(
self, self,
_name: &'static str, name: &'static str,
len: usize, _len: usize,
) -> Result<Self::SerializeTupleStruct, Error> { ) -> Result<Self::SerializeTupleStruct, Error> {
self.serialize_seq(Some(len)) match name {
sql::cast::TOKEN => Ok(SerializeTupleStruct::Cast(Default::default())),
_ => Ok(SerializeTupleStruct::Array(Default::default())),
}
} }
fn serialize_tuple_variant( fn serialize_tuple_variant(
@ -347,7 +351,6 @@ impl ser::Serializer for Serializer {
) -> Result<Self::SerializeStruct, Error> { ) -> Result<Self::SerializeStruct, Error> {
Ok(match name { Ok(match name {
sql::thing::TOKEN => SerializeStruct::Thing(Default::default()), sql::thing::TOKEN => SerializeStruct::Thing(Default::default()),
sql::expression::TOKEN => SerializeStruct::Expression(Default::default()),
sql::edges::TOKEN => SerializeStruct::Edges(Default::default()), sql::edges::TOKEN => SerializeStruct::Edges(Default::default()),
sql::range::TOKEN => SerializeStruct::Range(Default::default()), sql::range::TOKEN => SerializeStruct::Range(Default::default()),
_ => SerializeStruct::Unknown(Default::default()), _ => SerializeStruct::Unknown(Default::default()),
@ -356,18 +359,27 @@ impl ser::Serializer for Serializer {
fn serialize_struct_variant( fn serialize_struct_variant(
self, self,
_name: &'static str, name: &'static str,
_variant_index: u32, _variant_index: u32,
variant: &'static str, variant: &'static str,
_len: usize, _len: usize,
) -> Result<Self::SerializeStructVariant, Error> { ) -> Result<Self::SerializeStructVariant, Error> {
Ok(SerializeStructVariant { Ok(if name == sql::expression::TOKEN {
SerializeStructVariant::Expression(match variant {
"Unary" => SerializeExpression::Unary(Default::default()),
"Binary" => SerializeExpression::Binary(Default::default()),
_ => return Err(Error::custom(format!("unexpected `Expression::{name}`"))),
})
} else {
SerializeStructVariant::Object {
name: String::from(variant), name: String::from(variant),
map: Object::default(), map: Object::default(),
}
}) })
} }
} }
#[derive(Default)]
pub(super) struct SerializeArray(vec::SerializeValueVec); pub(super) struct SerializeArray(vec::SerializeValueVec);
impl serde::ser::SerializeSeq for SerializeArray { impl serde::ser::SerializeSeq for SerializeArray {
@ -452,6 +464,33 @@ pub(super) enum SerializeTupleVariant {
}, },
} }
pub(super) enum SerializeTupleStruct {
Cast(SerializeCast),
Array(SerializeArray),
}
impl serde::ser::SerializeTupleStruct for SerializeTupleStruct {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
match self {
Self::Cast(cast) => cast.serialize_field(value),
Self::Array(array) => array.serialize_field(value),
}
}
fn end(self) -> Result<Value, Error> {
match self {
Self::Cast(cast) => Ok(Value::Cast(Box::new(cast.end()?))),
Self::Array(array) => Ok(serde::ser::SerializeTupleStruct::end(array)?),
}
}
}
impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
type Ok = Value; type Ok = Value;
type Error = Error; type Error = Error;
@ -461,9 +500,9 @@ impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
T: ?Sized + Serialize, T: ?Sized + Serialize,
{ {
match self { match self {
SerializeTupleVariant::Model(model) => model.serialize_field(value), Self::Model(model) => model.serialize_field(value),
SerializeTupleVariant::Function(function) => function.serialize_field(value), Self::Function(function) => function.serialize_field(value),
SerializeTupleVariant::Unknown { Self::Unknown {
ref mut fields, ref mut fields,
.. ..
} => fields.serialize_element(value), } => fields.serialize_element(value),
@ -472,11 +511,9 @@ impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
fn end(self) -> Result<Value, Error> { fn end(self) -> Result<Value, Error> {
match self { match self {
SerializeTupleVariant::Model(model) => Ok(Value::Model(model.end()?)), Self::Model(model) => Ok(Value::Model(model.end()?)),
SerializeTupleVariant::Function(function) => { Self::Function(function) => Ok(Value::Function(Box::new(function.end()?))),
Ok(Value::Function(Box::new(function.end()?))) Self::Unknown {
}
SerializeTupleVariant::Unknown {
variant, variant,
fields, fields,
} => Ok(map! { } => Ok(map! {
@ -489,7 +526,6 @@ impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
pub(super) enum SerializeStruct { pub(super) enum SerializeStruct {
Thing(SerializeThing), Thing(SerializeThing),
Expression(SerializeExpression),
Edges(SerializeEdges), Edges(SerializeEdges),
Range(SerializeRange), Range(SerializeRange),
Unknown(SerializeValueMap), Unknown(SerializeValueMap),
@ -504,28 +540,29 @@ impl serde::ser::SerializeStruct for SerializeStruct {
T: ?Sized + Serialize, T: ?Sized + Serialize,
{ {
match self { match self {
SerializeStruct::Thing(thing) => thing.serialize_field(key, value), Self::Thing(thing) => thing.serialize_field(key, value),
SerializeStruct::Expression(expr) => expr.serialize_field(key, value), Self::Edges(edges) => edges.serialize_field(key, value),
SerializeStruct::Edges(edges) => edges.serialize_field(key, value), Self::Range(range) => range.serialize_field(key, value),
SerializeStruct::Range(range) => range.serialize_field(key, value), Self::Unknown(map) => map.serialize_entry(key, value),
SerializeStruct::Unknown(map) => map.serialize_entry(key, value),
} }
} }
fn end(self) -> Result<Value, Error> { fn end(self) -> Result<Value, Error> {
match self { match self {
SerializeStruct::Thing(thing) => Ok(Value::Thing(thing.end()?)), Self::Thing(thing) => Ok(Value::Thing(thing.end()?)),
SerializeStruct::Expression(expr) => Ok(Value::Expression(Box::new(expr.end()?))), Self::Edges(edges) => Ok(Value::Edges(Box::new(edges.end()?))),
SerializeStruct::Edges(edges) => Ok(Value::Edges(Box::new(edges.end()?))), Self::Range(range) => Ok(Value::Range(Box::new(range.end()?))),
SerializeStruct::Range(range) => Ok(Value::Range(Box::new(range.end()?))), Self::Unknown(map) => Ok(Value::Object(Object(map.end()?))),
SerializeStruct::Unknown(map) => Ok(Value::Object(Object(map.end()?))),
} }
} }
} }
pub(super) struct SerializeStructVariant { pub(super) enum SerializeStructVariant {
Expression(SerializeExpression),
Object {
name: String, name: String,
map: Object, map: Object,
},
} }
impl serde::ser::SerializeStructVariant for SerializeStructVariant { impl serde::ser::SerializeStructVariant for SerializeStructVariant {
@ -536,18 +573,34 @@ impl serde::ser::SerializeStructVariant for SerializeStructVariant {
where where
T: ?Sized + Serialize, T: ?Sized + Serialize,
{ {
self.map.0.insert(String::from(key), value.serialize(Serializer.wrap())?); match self {
Self::Expression(expression) => expression.serialize_field(key, value),
Self::Object {
map,
..
} => {
map.0.insert(String::from(key), value.serialize(Serializer.wrap())?);
Ok(()) Ok(())
} }
}
}
fn end(self) -> Result<Value, Error> { fn end(self) -> Result<Value, Error> {
match self {
Self::Expression(expression) => Ok(Value::from(expression.end()?)),
Self::Object {
name,
map,
} => {
let mut object = Object::default(); let mut object = Object::default();
object.insert(self.name, Value::Object(self.map)); object.insert(name, Value::Object(map));
Ok(Value::Object(object)) Ok(Value::Object(object))
} }
} }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -825,7 +878,7 @@ mod tests {
#[test] #[test]
fn expression() { fn expression() {
let expression = Box::new(Expression { let expression = Box::new(Expression::Binary {
l: "foo".into(), l: "foo".into(),
o: Operator::Equal, o: Operator::Equal,
r: "Bar".into(), r: "Bar".into(),

View file

@ -38,6 +38,7 @@ impl ser::Serializer for Serializer {
} }
} }
#[derive(Default)]
pub struct SerializeValueVec(pub Vec<Value>); pub struct SerializeValueVec(pub Vec<Value>);
impl serde::ser::SerializeSeq for SerializeValueVec { impl serde::ser::SerializeSeq for SerializeValueVec {

View file

@ -15,7 +15,7 @@ use crate::sql::datetime::{datetime, Datetime};
use crate::sql::duration::{duration, Duration}; use crate::sql::duration::{duration, Duration};
use crate::sql::edges::{edges, Edges}; use crate::sql::edges::{edges, Edges};
use crate::sql::error::IResult; use crate::sql::error::IResult;
use crate::sql::expression::{expression, Expression}; use crate::sql::expression::{binary, unary, Expression};
use crate::sql::fmt::{Fmt, Pretty}; use crate::sql::fmt::{Fmt, Pretty};
use crate::sql::function::{self, function, Function}; use crate::sql::function::{self, function, Function};
use crate::sql::future::{future, Future}; use crate::sql::future::{future, Future};
@ -58,6 +58,7 @@ use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Display, Formatter, Write}; use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref; use std::ops::Deref;
use std::ops::Neg;
use std::str::FromStr; use std::str::FromStr;
static MATCHER: Lazy<SkimMatcherV2> = Lazy::new(|| SkimMatcherV2::default().ignore_case()); static MATCHER: Lazy<SkimMatcherV2> = Lazy::new(|| SkimMatcherV2::default().ignore_case());
@ -2477,7 +2478,7 @@ impl Value {
Value::Object(v) => v.iter().any(|(_, v)| v.writeable()), Value::Object(v) => v.iter().any(|(_, v)| v.writeable()),
Value::Function(v) => v.is_custom() || v.args().iter().any(Value::writeable), Value::Function(v) => v.is_custom() || v.args().iter().any(Value::writeable),
Value::Subquery(v) => v.writeable(), Value::Subquery(v) => v.writeable(),
Value::Expression(v) => v.l.writeable() || v.r.writeable(), Value::Expression(v) => v.writeable(),
_ => false, _ => false,
} }
} }
@ -2658,9 +2659,24 @@ impl TryPow for Value {
// ------------------------------ // ------------------------------
/// Parse any `Value` including binary expressions pub(crate) trait TryNeg<Rhs = Self> {
type Output;
fn try_neg(self) -> Result<Self::Output, Error>;
}
impl TryNeg for Value {
type Output = Self;
fn try_neg(self) -> Result<Self, Error> {
match self {
Self::Number(n) if !matches!(n, Number::Int(i64::MIN)) => Ok(Self::Number(n.neg())),
v => Err(Error::TryNeg(v.to_string())),
}
}
}
/// Parse any `Value` including expressions
pub fn value(i: &str) -> IResult<&str, Value> { pub fn value(i: &str) -> IResult<&str, Value> {
alt((map(expression, Value::from), single))(i) alt((map(binary, Value::from), single))(i)
} }
/// Parse any `Value` excluding binary expressions /// Parse any `Value` excluding binary expressions
@ -2684,8 +2700,11 @@ pub fn single(i: &str) -> IResult<&str, Value> {
map(future, Value::from), map(future, Value::from),
map(unique, Value::from), map(unique, Value::from),
map(number, Value::from), map(number, Value::from),
map(unary, Value::from),
map(object, Value::from), map(object, Value::from),
map(array, Value::from), map(array, Value::from),
)),
alt((
map(block, Value::from), map(block, Value::from),
map(param, Value::from), map(param, Value::from),
map(regex, Value::from), map(regex, Value::from),
@ -2702,7 +2721,8 @@ pub fn single(i: &str) -> IResult<&str, Value> {
pub fn select(i: &str) -> IResult<&str, Value> { pub fn select(i: &str) -> IResult<&str, Value> {
alt(( alt((
alt(( alt((
map(expression, Value::from), map(unary, Value::from),
map(binary, Value::from),
map(tag_no_case("NONE"), |_| Value::None), map(tag_no_case("NONE"), |_| Value::None),
map(tag_no_case("NULL"), |_| Value::Null), map(tag_no_case("NULL"), |_| Value::Null),
map(tag_no_case("true"), |_| Value::Bool(true)), map(tag_no_case("true"), |_| Value::Bool(true)),

View file

@ -65,6 +65,64 @@ async fn select_field_value() -> Result<(), Error> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn select_expression_value() -> Result<(), Error> {
let sql = "
CREATE thing:a SET number = 5, boolean = true;
CREATE thing:b SET number = -5, boolean = false;
SELECT VALUE -number FROM thing;
SELECT VALUE !boolean FROM thing;
";
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
assert_eq!(res.len(), 4);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
boolean: true,
id: thing:a,
number: 5
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
boolean: false,
id: thing:b,
number: -5
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
-5,
5,
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
false,
true
]",
);
assert_eq!(tmp, val);
//
Ok(())
}
#[tokio::test] #[tokio::test]
async fn select_writeable_subqueries() -> Result<(), Error> { async fn select_writeable_subqueries() -> Result<(), Error> {
let sql = " let sql = "