use super::entry::EntryValue; use super::inner::models::Entry; use super::inner::schema::data; use super::lang::{Query, QueryComponent, QueryPart, QueryQualifier}; use crate::database::inner::models; use crate::diesel::IntoSql; use crate::diesel::RunQueryDsl; use crate::diesel::{ExpressionMethods, TextExpressionMethods}; use anyhow::Result; use diesel::expression::grouped::Grouped; use diesel::expression::operators::{And, Not, Or}; use diesel::sql_types::Bool; use diesel::sqlite::Sqlite; use diesel::{debug_query, BoxableExpression, QueryDsl}; use diesel::{ r2d2::{ConnectionManager, PooledConnection}, SqliteConnection, }; use log::trace; #[derive(Debug, Clone)] pub struct QueryExecutionError(String); impl std::fmt::Display for QueryExecutionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl std::error::Error for QueryExecutionError {} pub fn execute( connection: &PooledConnection>, query: Query, ) -> Result> { use crate::database::inner::schema::data::dsl::*; let db_query = data.filter(to_sqlite_predicates(query)?); trace!("DB query: {}", debug_query(&db_query)); db_query.load::(connection).map_err(anyhow::Error::from) } type Predicate = dyn BoxableExpression; fn to_sqlite_predicates(query: Query) -> Result, QueryExecutionError> { match query { Query::SingleQuery(qp) => match qp { QueryPart::Matches(eq) => { let mut subqueries: Vec> = vec![]; match &eq.entity { QueryComponent::Exact(q_entity) => { subqueries.push(Box::new(data::entity.eq(q_entity.encode().map_err( |e| QueryExecutionError(format!("failed producing sql: {e}")), )?))) } QueryComponent::In(q_entities) => { let entities: Result, _> = q_entities.iter().map(|t| t.encode()).collect(); subqueries.push(Box::new(data::entity.eq_any(entities.map_err(|e| { QueryExecutionError(format!("failed producing sql: {e}")) })?))) } QueryComponent::Contains(q_entity) => subqueries.push(Box::new( data::entity_searchable.like(format!("%{}%", q_entity)), )), QueryComponent::Any => {} }; match &eq.attribute { QueryComponent::Exact(q_attribute) => { subqueries.push(Box::new(data::attribute.eq(q_attribute.0.clone()))) } QueryComponent::In(q_attributes) => subqueries.push(Box::new( data::attribute.eq_any(q_attributes.iter().map(|a| &a.0).cloned()), )), QueryComponent::Contains(q_attribute) => subqueries .push(Box::new(data::attribute.like(format!("%{}%", q_attribute)))), QueryComponent::Any => {} }; match &eq.value { QueryComponent::Exact(q_value) => match q_value { EntryValue::Number(n) => subqueries.push(Box::new(data::value_num.eq(*n))), _ => subqueries.push(Box::new(data::value_str.eq( q_value.to_string().map_err(|e| { QueryExecutionError(format!("failed producing sql: {e}")) })?, ))), }, QueryComponent::In(q_values) => { let first = q_values.first().ok_or_else(|| { QueryExecutionError( "Malformed expression: Inner value cannot be empty.".into(), ) })?; match first { EntryValue::Number(_) => subqueries.push(Box::new( data::value_num.eq_any( q_values .iter() .map(|v| { if let EntryValue::Number(n) = v { Ok(*n) } else { Err(QueryExecutionError(format!("IN queries must not combine numeric and string values! ({v} is not a number)"))) } }) .collect::, QueryExecutionError>>()?, ), )), _ => subqueries.push(Box::new( data::value_str.eq_any( q_values .iter() .map(|v| { if let EntryValue::Number(_) = v { Err(QueryExecutionError(format!("IN queries must not combine numeric and string values! (Found {v})"))) } else { v.to_string().map_err(|e| QueryExecutionError(format!("failed producing sql: {e}"))) } }) .collect::, QueryExecutionError>>()?, ), )), } } QueryComponent::Contains(q_value) => { subqueries.push(Box::new(data::value_str.like(format!("S%{}%", q_value)))) } QueryComponent::Any => {} }; match subqueries.len() { 0 => Ok(Box::new(true.into_sql::())), 1 => Ok(subqueries.remove(0)), _ => { let mut result: Box, Box>> = Box::new(And::new(subqueries.remove(0), subqueries.remove(0))); while !subqueries.is_empty() { result = Box::new(And::new(result, subqueries.remove(0))); } Ok(Box::new(result)) } } } QueryPart::Type(_) => unimplemented!("Type queries are not yet implemented."), }, Query::MultiQuery(mq) => { let subqueries: Result>, QueryExecutionError> = mq .queries .into_iter() .map(|sq| to_sqlite_predicates(*sq)) .collect(); let mut subqueries: Vec> = subqueries?; match subqueries.len() { 0 => Ok(Box::new(true.into_sql::())), 1 => { if let QueryQualifier::Not = mq.qualifier { Ok(Box::new(Not::new(subqueries.remove(0)))) } else { Ok(subqueries.remove(0)) } } _ => match mq.qualifier { QueryQualifier::And => { let mut result: Box, Box>> = Box::new(And::new(subqueries.remove(0), subqueries.remove(0))); while !subqueries.is_empty() { result = Box::new(And::new(result, subqueries.remove(0))); } Ok(Box::new(Grouped(result))) } QueryQualifier::Or => { let mut result = Box::new(Or::new(subqueries.remove(0), subqueries.remove(0))); while !subqueries.is_empty() { result = Box::new(Or::new(result, subqueries.remove(0))); } Ok(Box::new(Grouped(result))) } QueryQualifier::Not => { Err(QueryExecutionError("NOT only takes one subquery.".into())) } }, } } } }