surrealpatch/lib/src/sql/function.rs

537 lines
12 KiB
Rust
Raw Normal View History

use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
2021-03-29 15:43:37 +00:00
use crate::err::Error;
use crate::fnc;
2020-06-29 15:36:01 +00:00
use crate::sql::comment::mightbespace;
use crate::sql::common::commas;
2022-01-16 20:31:50 +00:00
use crate::sql::error::IResult;
use crate::sql::fmt::Fmt;
use crate::sql::script::{script as func, Script};
use crate::sql::value::{single, value, Value};
2020-06-29 15:36:01 +00:00
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::char;
2021-03-29 15:43:37 +00:00
use nom::multi::separated_list0;
2020-06-29 15:36:01 +00:00
use serde::{Deserialize, Serialize};
2021-03-29 15:43:37 +00:00
use std::cmp::Ordering;
2020-06-29 15:36:01 +00:00
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
2021-03-29 15:43:37 +00:00
pub enum Function {
Cast(String, Value),
Normal(String, Vec<Value>),
Script(Script, Vec<Value>),
2021-03-29 15:43:37 +00:00
}
impl PartialOrd for Function {
#[inline]
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
2022-03-23 11:56:39 +00:00
None
2021-03-29 15:43:37 +00:00
}
2020-06-29 15:36:01 +00:00
}
2022-03-25 18:43:22 +00:00
impl Function {
/// Get function name if applicable
pub fn name(&self) -> &str {
match self {
Self::Normal(n, _) => n.as_str(),
_ => unreachable!(),
}
}
/// Get function arguments if applicable
2022-03-25 18:43:22 +00:00
pub fn args(&self) -> &[Value] {
match self {
Self::Normal(_, a) => a,
2022-03-25 18:43:22 +00:00
_ => &[],
}
}
/// Convert this function to an aggregate
pub fn aggregate(&self, val: Value) -> Self {
2022-03-25 18:43:22 +00:00
match self {
Self::Normal(n, a) => {
2022-03-25 18:43:22 +00:00
let mut a = a.to_owned();
match a.len() {
0 => a.insert(0, val),
_ => {
a.remove(0);
a.insert(0, val);
}
}
Self::Normal(n.to_owned(), a)
2022-03-25 18:43:22 +00:00
}
_ => unreachable!(),
}
}
/// Check if this function is a rolling function
2022-03-25 18:43:22 +00:00
pub fn is_rolling(&self) -> bool {
match self {
Self::Normal(f, _) if f == "count" => true,
Self::Normal(f, _) if f == "math::max" => true,
Self::Normal(f, _) if f == "math::mean" => true,
Self::Normal(f, _) if f == "math::min" => true,
Self::Normal(f, _) if f == "math::sum" => true,
2022-03-25 18:43:22 +00:00
_ => false,
}
}
/// Check if this function is a grouping function
2022-03-25 18:43:22 +00:00
pub fn is_aggregate(&self) -> bool {
match self {
Self::Normal(f, _) if f == "array::concat" => true,
Self::Normal(f, _) if f == "array::distinct" => true,
Self::Normal(f, _) if f == "array::union" => true,
Self::Normal(f, _) if f == "count" => true,
Self::Normal(f, _) if f == "math::bottom" => true,
Self::Normal(f, _) if f == "math::interquartile" => true,
Self::Normal(f, _) if f == "math::max" => true,
Self::Normal(f, _) if f == "math::mean" => true,
Self::Normal(f, _) if f == "math::median" => true,
Self::Normal(f, _) if f == "math::midhinge" => true,
Self::Normal(f, _) if f == "math::min" => true,
Self::Normal(f, _) if f == "math::mode" => true,
Self::Normal(f, _) if f == "math::nearestrank" => true,
Self::Normal(f, _) if f == "math::percentile" => true,
Self::Normal(f, _) if f == "math::sample" => true,
Self::Normal(f, _) if f == "math::spread" => true,
Self::Normal(f, _) if f == "math::stddev" => true,
Self::Normal(f, _) if f == "math::sum" => true,
Self::Normal(f, _) if f == "math::top" => true,
Self::Normal(f, _) if f == "math::trimean" => true,
Self::Normal(f, _) if f == "math::variance" => true,
2022-03-25 18:43:22 +00:00
_ => false,
}
}
}
2022-01-14 08:12:56 +00:00
impl Function {
pub(crate) async fn compute(
2021-03-29 15:43:37 +00:00
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&Value>,
) -> Result<Value, Error> {
// Prevent long function chains
let opt = &opt.dive(1)?;
// Process the function type
2021-03-29 15:43:37 +00:00
match self {
Self::Cast(s, x) => {
let v = x.compute(ctx, opt, txn, doc).await?;
fnc::cast::run(ctx, s, v)
2021-03-29 15:43:37 +00:00
}
Self::Normal(s, x) => {
let mut a: Vec<Value> = Vec::with_capacity(x.len());
for v in x {
a.push(v.compute(ctx, opt, txn, doc).await?);
2021-03-29 15:43:37 +00:00
}
fnc::run(ctx, s, a).await
2021-03-29 15:43:37 +00:00
}
#[allow(unused_variables)]
Self::Script(s, x) => {
#[cfg(feature = "scripting")]
{
let mut a: Vec<Value> = Vec::with_capacity(x.len());
for v in x {
a.push(v.compute(ctx, opt, txn, doc).await?);
}
fnc::script::run(ctx, doc, s, a).await
}
#[cfg(not(feature = "scripting"))]
{
Err(Error::InvalidScript {
message: String::from("Embedded functions are not enabled."),
})
}
}
2020-06-29 15:36:01 +00:00
}
}
}
2022-01-14 08:12:56 +00:00
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Cast(ref s, ref e) => write!(f, "<{}> {}", s, e),
Self::Script(ref s, ref e) => {
write!(f, "function({}) {{{}}}", Fmt::comma_separated(e), s)
}
Self::Normal(ref s, ref e) => write!(f, "{}({})", s, Fmt::comma_separated(e)),
2022-01-14 08:12:56 +00:00
}
}
}
2020-06-29 15:36:01 +00:00
pub fn function(i: &str) -> IResult<&str, Function> {
2022-10-31 23:12:41 +00:00
alt((normal, script, cast))(i)
2021-03-29 15:43:37 +00:00
}
fn normal(i: &str) -> IResult<&str, Function> {
let (i, s) = function_names(i)?;
let (i, _) = char('(')(i)?;
let (i, _) = mightbespace(i)?;
let (i, a) = separated_list0(commas, value)(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = char(')')(i)?;
Ok((i, Function::Normal(s.to_string(), a)))
}
fn script(i: &str) -> IResult<&str, Function> {
let (i, _) = alt((tag("fn::script"), tag("fn"), tag("function")))(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag("(")(i)?;
let (i, _) = mightbespace(i)?;
let (i, a) = separated_list0(commas, value)(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag(")")(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = char('{')(i)?;
let (i, v) = func(i)?;
let (i, _) = char('}')(i)?;
Ok((i, Function::Script(v, a)))
}
fn cast(i: &str) -> IResult<&str, Function> {
let (i, _) = char('<')(i)?;
2021-03-29 15:43:37 +00:00
let (i, s) = function_casts(i)?;
let (i, _) = char('>')(i)?;
2021-03-29 15:43:37 +00:00
let (i, _) = mightbespace(i)?;
let (i, v) = single(i)?;
2021-03-29 15:43:37 +00:00
Ok((i, Function::Cast(s.to_string(), v)))
}
fn function_casts(i: &str) -> IResult<&str, &str> {
alt((
tag("bool"),
tag("int"),
tag("float"),
tag("string"),
tag("number"),
tag("decimal"),
tag("datetime"),
tag("duration"),
))(i)
}
fn function_names(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
function_array,
function_count,
function_crypto,
function_duration,
2020-06-29 15:36:01 +00:00
function_geo,
function_http,
function_is,
function_math,
function_meta,
2020-06-29 15:36:01 +00:00
function_parse,
function_rand,
2022-08-17 14:09:08 +00:00
function_session,
2020-06-29 15:36:01 +00:00
function_string,
function_time,
function_type,
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_array(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
tag("array::combine"),
tag("array::complement"),
tag("array::concat"),
2021-03-29 15:43:37 +00:00
tag("array::difference"),
tag("array::flatten"),
2021-03-29 15:43:37 +00:00
tag("array::distinct"),
tag("array::insert"),
2021-03-29 15:43:37 +00:00
tag("array::intersect"),
tag("array::len"),
2022-08-11 15:16:42 +00:00
tag("array::sort::asc"),
tag("array::sort::desc"),
tag("array::sort"),
2021-03-29 15:43:37 +00:00
tag("array::union"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_count(i: &str) -> IResult<&str, &str> {
tag("count")(i)
}
fn function_crypto(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
tag("crypto::argon2::compare"),
tag("crypto::argon2::generate"),
tag("crypto::bcrypt::compare"),
tag("crypto::bcrypt::generate"),
2022-08-11 15:16:42 +00:00
tag("crypto::md5"),
tag("crypto::pbkdf2::compare"),
tag("crypto::pbkdf2::generate"),
tag("crypto::scrypt::compare"),
tag("crypto::scrypt::generate"),
2022-08-11 15:16:42 +00:00
tag("crypto::sha1"),
tag("crypto::sha256"),
tag("crypto::sha512"),
2020-06-29 15:36:01 +00:00
))(i)
}
fn function_duration(i: &str) -> IResult<&str, &str> {
alt((
tag("duration::days"),
tag("duration::hours"),
tag("duration::mins"),
tag("duration::secs"),
tag("duration::weeks"),
tag("duration::years"),
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_geo(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
2021-05-17 16:59:50 +00:00
tag("geo::area"),
tag("geo::bearing"),
tag("geo::centroid"),
2021-03-29 15:43:37 +00:00
tag("geo::distance"),
tag("geo::hash::decode"),
tag("geo::hash::encode"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_http(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
2021-03-29 15:43:37 +00:00
tag("http::head"),
tag("http::get"),
tag("http::put"),
tag("http::post"),
tag("http::patch"),
tag("http::delete"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_is(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
2021-03-29 15:43:37 +00:00
tag("is::alphanum"),
tag("is::alpha"),
tag("is::ascii"),
tag("is::domain"),
tag("is::email"),
tag("is::hexadecimal"),
tag("is::latitude"),
tag("is::longitude"),
tag("is::numeric"),
tag("is::semver"),
tag("is::url"),
2021-03-29 15:43:37 +00:00
tag("is::uuid"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_math(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
alt((
2021-03-29 15:43:37 +00:00
tag("math::abs"),
tag("math::bottom"),
tag("math::ceil"),
tag("math::fixed"),
tag("math::floor"),
tag("math::interquartile"),
2020-06-29 15:36:01 +00:00
)),
alt((
2021-03-29 15:43:37 +00:00
tag("math::max"),
tag("math::mean"),
tag("math::median"),
tag("math::midhinge"),
tag("math::min"),
tag("math::mode"),
2020-06-29 15:36:01 +00:00
)),
alt((
2021-03-29 15:43:37 +00:00
tag("math::nearestrank"),
tag("math::percentile"),
tag("math::pow"),
tag("math::product"),
2021-03-29 15:43:37 +00:00
tag("math::round"),
tag("math::spread"),
tag("math::sqrt"),
tag("math::stddev"),
tag("math::sum"),
tag("math::top"),
tag("math::trimean"),
tag("math::variance"),
2020-06-29 15:36:01 +00:00
)),
))(i)
}
fn function_meta(i: &str) -> IResult<&str, &str> {
alt((tag("meta::id"), tag("meta::table"), tag("meta::tb")))(i)
}
2021-03-29 15:43:37 +00:00
fn function_parse(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
tag("parse::email::host"),
2021-03-29 15:43:37 +00:00
tag("parse::email::user"),
tag("parse::url::domain"),
tag("parse::url::fragment"),
2021-03-29 15:43:37 +00:00
tag("parse::url::host"),
tag("parse::url::port"),
tag("parse::url::path"),
tag("parse::url::query"),
tag("parse::url::scheme"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_rand(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
tag("rand::bool"),
tag("rand::enum"),
tag("rand::float"),
tag("rand::guid"),
tag("rand::int"),
tag("rand::string"),
tag("rand::time"),
tag("rand::uuid::v4"),
tag("rand::uuid::v7"),
tag("rand::uuid"),
2021-03-29 15:43:37 +00:00
tag("rand"),
2020-06-29 15:36:01 +00:00
))(i)
}
2022-08-17 14:09:08 +00:00
fn function_session(i: &str) -> IResult<&str, &str> {
alt((
tag("session::db"),
tag("session::id"),
tag("session::ip"),
tag("session::ns"),
tag("session::origin"),
tag("session::sc"),
tag("session::sd"),
tag("session::token"),
2022-08-17 14:09:08 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_string(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
2021-03-29 15:43:37 +00:00
tag("string::concat"),
tag("string::endsWith"),
tag("string::join"),
tag("string::length"),
tag("string::lowercase"),
tag("string::repeat"),
tag("string::replace"),
tag("string::reverse"),
tag("string::slice"),
tag("string::slug"),
tag("string::split"),
tag("string::startsWith"),
tag("string::trim"),
tag("string::uppercase"),
tag("string::words"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_time(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
2021-03-29 15:43:37 +00:00
tag("time::day"),
tag("time::floor"),
2022-10-04 21:09:24 +00:00
tag("time::format"),
2022-01-18 22:23:46 +00:00
tag("time::group"),
2021-03-29 15:43:37 +00:00
tag("time::hour"),
tag("time::minute"),
2021-03-29 15:43:37 +00:00
tag("time::month"),
tag("time::nano"),
tag("time::now"),
tag("time::round"),
tag("time::second"),
2021-03-29 15:43:37 +00:00
tag("time::unix"),
tag("time::wday"),
tag("time::week"),
tag("time::yday"),
tag("time::year"),
2020-06-29 15:36:01 +00:00
))(i)
}
2021-03-29 15:43:37 +00:00
fn function_type(i: &str) -> IResult<&str, &str> {
2020-06-29 15:36:01 +00:00
alt((
tag("type::bool"),
tag("type::datetime"),
tag("type::decimal"),
tag("type::duration"),
tag("type::float"),
tag("type::int"),
tag("type::number"),
2021-03-29 15:43:37 +00:00
tag("type::point"),
tag("type::regex"),
tag("type::string"),
2021-03-29 15:43:37 +00:00
tag("type::table"),
tag("type::thing"),
2020-06-29 15:36:01 +00:00
))(i)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::test::Parse;
2020-06-29 15:36:01 +00:00
#[test]
fn function_single() {
let sql = "count()";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("count()", format!("{}", out));
2021-03-29 15:43:37 +00:00
assert_eq!(out, Function::Normal(String::from("count"), vec![]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn function_module() {
let sql = "rand::uuid()";
2020-06-29 15:36:01 +00:00
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("rand::uuid()", format!("{}", out));
assert_eq!(out, Function::Normal(String::from("rand::uuid"), vec![]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn function_arguments() {
let sql = "is::numeric(null)";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("is::numeric(NULL)", format!("{}", out));
assert_eq!(out, Function::Normal(String::from("is::numeric"), vec![Value::Null]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn function_casting_number() {
2021-03-29 15:43:37 +00:00
let sql = "<int>1.2345";
2020-06-29 15:36:01 +00:00
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
2022-01-14 08:12:56 +00:00
assert_eq!("<int> 1.2345", format!("{}", out));
assert_eq!(out, Function::Cast(String::from("int"), 1.2345.into()));
2020-06-29 15:36:01 +00:00
}
#[test]
fn function_casting_string() {
let sql = "<string>1.2345";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
2022-01-14 08:12:56 +00:00
assert_eq!("<string> 1.2345", format!("{}", out));
assert_eq!(out, Function::Cast(String::from("string"), 1.2345.into()));
2020-06-29 15:36:01 +00:00
}
#[test]
fn function_script_expression() {
let sql = "function() { return this.tags.filter(t => { return t.length > 3; }); }";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
"function() { return this.tags.filter(t => { return t.length > 3; }); }",
format!("{}", out)
);
assert_eq!(
out,
Function::Script(
Script::parse(" return this.tags.filter(t => { return t.length > 3; }); "),
vec![]
)
);
}
2020-06-29 15:36:01 +00:00
}