3e80aa9914
`sql::Value` is an integral part of `surrealdb`. It's the internal type used by our storage layer. Because of this, we do a lot of converting between this type and native Rust types. Currently this conversion is done through `JSON` using the `serde_json` crate because we do not have our own custom data format implementation. This works because `SQL` is a superset of `JSON`. This, however, means that this conversion is lossy and can cause surprises in some cases. For example expecting record IDs to be deserialized into a `String` instead of its corresponding Rust native type. This change implements a custom data format around `sql::Value` and introduces a `to_value` function that facilitates that conversion.
237 lines
6.1 KiB
Rust
237 lines
6.1 KiB
Rust
use crate::ctx::Context;
|
|
use crate::dbs::Options;
|
|
use crate::dbs::Transaction;
|
|
use crate::err::Error;
|
|
use crate::fnc;
|
|
use crate::sql::error::IResult;
|
|
use crate::sql::operator::{operator, Operator};
|
|
use crate::sql::serde::is_internal_serialization;
|
|
use crate::sql::value::{single, value, Value};
|
|
use serde::ser::SerializeStruct;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fmt;
|
|
use std::str;
|
|
|
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Expression";
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Deserialize, Hash)]
|
|
pub struct Expression {
|
|
pub l: Value,
|
|
pub o: Operator,
|
|
pub r: Value,
|
|
}
|
|
|
|
impl Default for Expression {
|
|
fn default() -> Expression {
|
|
Expression {
|
|
l: Value::Null,
|
|
o: Operator::default(),
|
|
r: Value::Null,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Expression {
|
|
/// Create a new expression
|
|
fn new(l: Value, o: Operator, r: Value) -> Self {
|
|
Self {
|
|
l,
|
|
o,
|
|
r,
|
|
}
|
|
}
|
|
/// Augment an existing expression
|
|
fn augment(mut self, l: Value, o: Operator) -> Self {
|
|
if o.precedence() >= self.o.precedence() {
|
|
match self.l {
|
|
Value::Expression(x) => {
|
|
self.l = x.augment(l, o).into();
|
|
self
|
|
}
|
|
_ => {
|
|
self.l = Self::new(l, o, self.l).into();
|
|
self
|
|
}
|
|
}
|
|
} else {
|
|
let r = Value::from(self);
|
|
Self::new(l, o, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Expression {
|
|
pub(crate) async fn compute(
|
|
&self,
|
|
ctx: &Context<'_>,
|
|
opt: &Options,
|
|
txn: &Transaction,
|
|
doc: Option<&Value>,
|
|
) -> Result<Value, Error> {
|
|
let l = self.l.compute(ctx, opt, txn, doc).await?;
|
|
match self.o {
|
|
Operator::Or => {
|
|
if let true = l.is_truthy() {
|
|
return Ok(l);
|
|
}
|
|
}
|
|
Operator::And => {
|
|
if let false = l.is_truthy() {
|
|
return Ok(l);
|
|
}
|
|
}
|
|
Operator::Tco => {
|
|
if let true = l.is_truthy() {
|
|
return Ok(l);
|
|
}
|
|
}
|
|
Operator::Nco => {
|
|
if let true = l.is_some() {
|
|
return Ok(l);
|
|
}
|
|
}
|
|
_ => {} // Continue
|
|
}
|
|
let r = self.r.compute(ctx, opt, txn, doc).await?;
|
|
match self.o {
|
|
Operator::Or => fnc::operate::or(l, r),
|
|
Operator::And => fnc::operate::and(l, r),
|
|
Operator::Tco => fnc::operate::tco(l, r),
|
|
Operator::Nco => fnc::operate::nco(l, r),
|
|
Operator::Add => fnc::operate::add(l, r),
|
|
Operator::Sub => fnc::operate::sub(l, r),
|
|
Operator::Mul => fnc::operate::mul(l, r),
|
|
Operator::Div => fnc::operate::div(l, r),
|
|
Operator::Pow => fnc::operate::pow(l, r),
|
|
Operator::Equal => fnc::operate::equal(&l, &r),
|
|
Operator::Exact => fnc::operate::exact(&l, &r),
|
|
Operator::NotEqual => fnc::operate::not_equal(&l, &r),
|
|
Operator::AllEqual => fnc::operate::all_equal(&l, &r),
|
|
Operator::AnyEqual => fnc::operate::any_equal(&l, &r),
|
|
Operator::Like => fnc::operate::like(&l, &r),
|
|
Operator::NotLike => fnc::operate::not_like(&l, &r),
|
|
Operator::AllLike => fnc::operate::all_like(&l, &r),
|
|
Operator::AnyLike => fnc::operate::any_like(&l, &r),
|
|
Operator::LessThan => fnc::operate::less_than(&l, &r),
|
|
Operator::LessThanOrEqual => fnc::operate::less_than_or_equal(&l, &r),
|
|
Operator::MoreThan => fnc::operate::more_than(&l, &r),
|
|
Operator::MoreThanOrEqual => fnc::operate::more_than_or_equal(&l, &r),
|
|
Operator::Contain => fnc::operate::contain(&l, &r),
|
|
Operator::NotContain => fnc::operate::not_contain(&l, &r),
|
|
Operator::ContainAll => fnc::operate::contain_all(&l, &r),
|
|
Operator::ContainAny => fnc::operate::contain_any(&l, &r),
|
|
Operator::ContainNone => fnc::operate::contain_none(&l, &r),
|
|
Operator::Inside => fnc::operate::inside(&l, &r),
|
|
Operator::NotInside => fnc::operate::not_inside(&l, &r),
|
|
Operator::AllInside => fnc::operate::inside_all(&l, &r),
|
|
Operator::AnyInside => fnc::operate::inside_any(&l, &r),
|
|
Operator::NoneInside => fnc::operate::inside_none(&l, &r),
|
|
Operator::Outside => fnc::operate::outside(&l, &r),
|
|
Operator::Intersects => fnc::operate::intersects(&l, &r),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Expression {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{} {} {}", self.l, self.o, self.r)
|
|
}
|
|
}
|
|
|
|
impl Serialize for Expression {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: serde::Serializer,
|
|
{
|
|
if is_internal_serialization() {
|
|
let mut val = serializer.serialize_struct(TOKEN, 3)?;
|
|
val.serialize_field("l", &self.l)?;
|
|
val.serialize_field("o", &self.o)?;
|
|
val.serialize_field("r", &self.r)?;
|
|
val.end()
|
|
} else {
|
|
serializer.serialize_none()
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn expression(i: &str) -> IResult<&str, Expression> {
|
|
let (i, l) = single(i)?;
|
|
let (i, o) = operator(i)?;
|
|
let (i, r) = value(i)?;
|
|
let v = match r {
|
|
Value::Expression(r) => r.augment(l, o),
|
|
_ => Expression::new(l, o, r),
|
|
};
|
|
Ok((i, v))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn expression_statement() {
|
|
let sql = "true AND false";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("true AND false", format!("{}", out));
|
|
}
|
|
|
|
#[test]
|
|
fn expression_left_opened() {
|
|
let sql = "3 * 3 * 3 = 27";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("3 * 3 * 3 = 27", format!("{}", out));
|
|
}
|
|
|
|
#[test]
|
|
fn expression_left_closed() {
|
|
let sql = "(3 * 3 * 3) = 27";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("(3 * 3 * 3) = 27", format!("{}", out));
|
|
}
|
|
|
|
#[test]
|
|
fn expression_right_opened() {
|
|
let sql = "27 = 3 * 3 * 3";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("27 = 3 * 3 * 3", format!("{}", out));
|
|
}
|
|
|
|
#[test]
|
|
fn expression_right_closed() {
|
|
let sql = "27 = (3 * 3 * 3)";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("27 = (3 * 3 * 3)", format!("{}", out));
|
|
}
|
|
|
|
#[test]
|
|
fn expression_both_opened() {
|
|
let sql = "3 * 3 * 3 = 3 * 3 * 3";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("3 * 3 * 3 = 3 * 3 * 3", format!("{}", out));
|
|
}
|
|
|
|
#[test]
|
|
fn expression_both_closed() {
|
|
let sql = "(3 * 3 * 3) = (3 * 3 * 3)";
|
|
let res = expression(sql);
|
|
assert!(res.is_ok());
|
|
let out = res.unwrap().1;
|
|
assert_eq!("(3 * 3 * 3) = (3 * 3 * 3)", format!("{}", out));
|
|
}
|
|
}
|