surrealpatch/lib/src/sql/special.rs
2023-06-20 12:48:20 +01:00

129 lines
4.1 KiB
Rust

use crate::sql::error::Error;
use crate::sql::field::{Field, Fields};
use crate::sql::group::Groups;
use crate::sql::order::Orders;
use crate::sql::split::Splits;
use crate::sql::value::Value;
use crate::sql::Idiom;
use nom::Err;
use nom::Err::Failure;
/// Check to see whether the expression is in the SELECT clause
fn contains_idiom(fields: &Fields, idiom: &Idiom) -> bool {
fields.iter().any(|field| {
match field {
// There is a SELECT * expression, so presume everything is ok
Field::All => true,
// Check each field
Field::Single {
expr,
alias,
} => {
if let Some(i) = alias {
// This field is aliased, so check the alias name
i.as_ref() == idiom.as_ref()
} else {
// This field is not aliased, so check the field value
match expr {
// Use raw idiom (TODO: should this use `simplify`?)
Value::Idiom(i) => i.as_ref() == idiom.as_ref(),
// Check the expression
v => v.to_idiom().as_ref() == idiom.as_ref(),
}
}
}
}
})
}
pub fn check_split_on_fields<'a>(
i: &'a str,
fields: &Fields,
splits: &Option<Splits>,
) -> Result<(), Err<Error<&'a str>>> {
// Check to see if a SPLIT ON clause has been defined
if let Some(splits) = splits {
// Loop over each of the expressions in the SPLIT ON clause
for split in splits.iter() {
if !contains_idiom(fields, &split.0) {
// If the expression isn't specified in the SELECT clause, then error
return Err(Failure(Error::Split(i, split.to_string())));
}
}
}
// This query is ok to run
Ok(())
}
pub fn check_order_by_fields<'a>(
i: &'a str,
fields: &Fields,
orders: &Option<Orders>,
) -> Result<(), Err<Error<&'a str>>> {
// Check to see if a ORDER BY clause has been defined
if let Some(orders) = orders {
// Loop over each of the expressions in the ORDER BY clause
for order in orders.iter() {
if !contains_idiom(fields, order) {
// If the expression isn't specified in the SELECT clause, then error
return Err(Failure(Error::Order(i, order.to_string())));
}
}
}
// This query is ok to run
Ok(())
}
pub fn check_group_by_fields<'a>(
i: &'a str,
fields: &Fields,
groups: &Option<Groups>,
) -> Result<(), Err<Error<&'a str>>> {
// Check to see if a GROUP BY clause has been defined
if let Some(groups) = groups {
// Loop over each of the expressions in the GROUP BY clause
for group in groups.iter() {
if !contains_idiom(fields, &group.0) {
// If the expression isn't specified in the SELECT clause, then error
return Err(Failure(Error::Group(i, group.to_string())));
}
}
// Check if this is a GROUP ALL clause or a GROUP BY clause
if !groups.is_empty() {
// Loop over each of the expressions in the SELECT clause
'outer: for field in fields.iter() {
// Loop over each of the expressions in the GROUP BY clause
for group in groups.iter() {
// Check to see whether the expression is in the GROUP BY clause or is an aggregate
if let Field::Single {
expr,
alias,
} = field
{
if alias.as_ref().map(|i| i.as_ref() == group.as_ref()).unwrap_or(false) {
// This field is aliased, and the alias name matched
continue 'outer;
} else {
match expr {
// If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY
Value::Idiom(i) if i == &group.0 => continue 'outer,
// If the expression in the SELECT clause is a function, check to see if it is an aggregate function
Value::Function(f) if f.is_aggregate() => continue 'outer,
// Otherwise check if the expression itself exists in the GROUP BY clause
v if v.to_idiom() == group.0 => continue 'outer,
// Check if this is a static value which can be used in the GROUP BY clause
v if v.is_static() => continue 'outer,
// If not, then this query should fail
_ => (),
}
}
}
}
// If the expression isn't an aggregate function and isn't specified in the GROUP BY clause, then error
return Err(Failure(Error::Field(i, field.to_string())));
}
}
}
// This query is ok to run
Ok(())
}