reorganize query code in preparation for lang upgrade

feat/vaults
Tomáš Mládek 2022-04-15 20:35:26 +02:00
parent cc57ea7a78
commit eec4f96293
No known key found for this signature in database
GPG Key ID: 65E225C8B3E2ED8A
4 changed files with 221 additions and 213 deletions

189
src/database/engine.rs Normal file
View File

@ -0,0 +1,189 @@
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<ConnectionManager<SqliteConnection>>,
query: Query,
) -> Result<Vec<Entry>> {
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::<models::Entry>(connection).map_err(anyhow::Error::from)
}
type Predicate = dyn BoxableExpression<data::table, Sqlite, SqlType = Bool>;
fn to_sqlite_predicates(query: Query) -> Result<Box<Predicate>, QueryExecutionError> {
match query {
Query::SingleQuery(qp) => match qp {
QueryPart::Matches(eq) => {
let mut subqueries: Vec<Box<Predicate>> = 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<Vec<_>, _> =
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::<Result<Vec<f64>, 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::<Result<Vec<String>, 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::<Bool>())),
1 => Ok(subqueries.remove(0)),
_ => {
let mut result: Box<And<Box<Predicate>, Box<Predicate>>> =
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<Vec<Box<Predicate>>, QueryExecutionError> = mq
.queries
.into_iter()
.map(|sq| to_sqlite_predicates(*sq))
.collect();
let mut subqueries: Vec<Box<Predicate>> = subqueries?;
match subqueries.len() {
0 => Ok(Box::new(true.into_sql::<Bool>())),
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<And<Box<Predicate>, Box<Predicate>>> =
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()))
}
},
}
}
}
}

View File

@ -11,7 +11,7 @@ use crate::database::constants::{
HIER_ADDR, HIER_HAS_ATTR, HIER_INVARIANT, IS_OF_TYPE_ATTR, LABEL_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, HIER_ADDR, HIER_HAS_ATTR, HIER_INVARIANT, IS_OF_TYPE_ATTR, LABEL_ATTR, TYPE_ADDR, TYPE_HAS_ATTR,
}; };
use crate::database::entry::{Entry, EntryValue}; use crate::database::entry::{Entry, EntryValue};
use crate::database::lang::{EntryQuery, Query, QueryComponent, QueryPart}; use crate::database::lang::{PatternQuery, Query, QueryComponent, QueryPart};
use super::UpEndConnection; use super::UpEndConnection;
@ -95,7 +95,7 @@ impl PointerEntries for Vec<Entry> {
pub fn list_roots(connection: &UpEndConnection) -> Result<Vec<Address>> { pub fn list_roots(connection: &UpEndConnection) -> Result<Vec<Address>> {
let all_directories: Vec<Entry> = let all_directories: Vec<Entry> =
connection.query(Query::SingleQuery(QueryPart::Matches(EntryQuery { connection.query(Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Exact(IS_OF_TYPE_ATTR.into()), attribute: QueryComponent::Exact(IS_OF_TYPE_ATTR.into()),
value: QueryComponent::Exact(HIER_ADDR.clone().into()), value: QueryComponent::Exact(HIER_ADDR.clone().into()),
@ -103,7 +103,7 @@ pub fn list_roots(connection: &UpEndConnection) -> Result<Vec<Address>> {
// TODO: this is horrible // TODO: this is horrible
let directories_with_parents: Vec<Address> = connection let directories_with_parents: Vec<Address> = connection
.query(Query::SingleQuery(QueryPart::Matches(EntryQuery { .query(Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Exact(HIER_HAS_ATTR.into()), attribute: QueryComponent::Exact(HIER_HAS_ATTR.into()),
value: QueryComponent::Any, value: QueryComponent::Any,
@ -141,7 +141,7 @@ pub fn fetch_or_create_dir(
} }
let matching_directories = connection let matching_directories = connection
.query(Query::SingleQuery(QueryPart::Matches(EntryQuery { .query(Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Exact(LABEL_ATTR.into()), attribute: QueryComponent::Exact(LABEL_ATTR.into()),
value: QueryComponent::Exact(directory.as_ref().clone().into()), value: QueryComponent::Exact(directory.as_ref().clone().into()),
@ -151,7 +151,7 @@ pub fn fetch_or_create_dir(
let parent_has: Vec<Address> = match parent.clone() { let parent_has: Vec<Address> = match parent.clone() {
Some(parent) => connection Some(parent) => connection
.query(Query::SingleQuery(QueryPart::Matches(EntryQuery { .query(Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Exact(parent), entity: QueryComponent::Exact(parent),
attribute: QueryComponent::Exact(HIER_HAS_ATTR.into()), attribute: QueryComponent::Exact(HIER_HAS_ATTR.into()),
value: QueryComponent::Any, value: QueryComponent::Any,

View File

@ -1,18 +1,12 @@
use crate::addressing::Address; use crate::addressing::Address;
use crate::database::entry::EntryValue; use crate::database::entry::EntryValue;
use crate::database::inner::schema::data;
use diesel::expression::grouped::Grouped;
use diesel::expression::operators::{And, Not, Or};
use diesel::sql_types::Bool;
use diesel::sqlite::Sqlite;
use diesel::{BoxableExpression, ExpressionMethods, IntoSql, TextExpressionMethods};
use nonempty::NonEmpty; use nonempty::NonEmpty;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attribute(String); pub struct Attribute(pub String);
impl From<&str> for Attribute { impl From<&str> for Attribute {
fn from(str: &str) -> Self { fn from(str: &str) -> Self {
@ -20,7 +14,7 @@ impl From<&str> for Attribute {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum QueryComponent<T> pub enum QueryComponent<T>
where where
T: TryFrom<lexpr::Value>, T: TryFrom<lexpr::Value>,
@ -31,8 +25,8 @@ where
Any, Any,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct EntryQuery { pub struct PatternQuery {
pub entity: QueryComponent<Address>, pub entity: QueryComponent<Address>,
pub attribute: QueryComponent<Attribute>, pub attribute: QueryComponent<Attribute>,
pub value: QueryComponent<EntryValue>, pub value: QueryComponent<EntryValue>,
@ -94,26 +88,26 @@ impl TryFrom<lexpr::Value> for EntryValue {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum QueryPart { pub enum QueryPart {
Matches(EntryQuery), Matches(PatternQuery),
Type(String), Type(String),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum QueryQualifier { pub enum QueryQualifier {
And, And,
Or, Or,
Not, Not,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct MultiQuery { pub struct MultiQuery {
pub qualifier: QueryQualifier, pub qualifier: QueryQualifier,
pub queries: NonEmpty<Box<Query>>, pub queries: NonEmpty<Box<Query>>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Query { pub enum Query {
SingleQuery(QueryPart), SingleQuery(QueryPart),
MultiQuery(MultiQuery), MultiQuery(MultiQuery),
@ -130,8 +124,6 @@ impl std::fmt::Display for QueryParseError {
impl std::error::Error for QueryParseError {} impl std::error::Error for QueryParseError {}
type Predicate = dyn BoxableExpression<data::table, Sqlite, SqlType = Bool>;
impl TryFrom<&lexpr::Value> for Query { impl TryFrom<&lexpr::Value> for Query {
type Error = QueryParseError; type Error = QueryParseError;
@ -204,7 +196,7 @@ impl TryFrom<&lexpr::Value> for Query {
let entity = parse_component::<Address>(entity)?; let entity = parse_component::<Address>(entity)?;
let attribute = parse_component::<Attribute>(attribute)?; let attribute = parse_component::<Attribute>(attribute)?;
let value = parse_component::<EntryValue>(value)?; let value = parse_component::<EntryValue>(value)?;
Ok(Query::SingleQuery(QueryPart::Matches(EntryQuery { Ok(Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity, entity,
attribute, attribute,
value, value,
@ -303,208 +295,55 @@ impl FromStr for Query {
} }
} }
impl Query {
pub(crate) fn to_sqlite_predicates(&self) -> Result<Box<Predicate>, QueryParseError> {
match self {
Query::SingleQuery(qp) => match qp {
QueryPart::Matches(eq) => {
let mut subqueries: Vec<Box<Predicate>> = vec![];
match &eq.entity {
QueryComponent::Exact(q_entity) => {
subqueries.push(Box::new(data::entity.eq(q_entity.encode().map_err(
|e| QueryParseError(format!("failed producing sql: {e}")),
)?)))
}
QueryComponent::In(q_entities) => {
let entities: Result<Vec<_>, _> =
q_entities.iter().map(|t| t.encode()).collect();
subqueries.push(Box::new(data::entity.eq_any(entities.map_err(
|e| QueryParseError(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| {
QueryParseError(format!("failed producing sql: {e}"))
})?,
))),
},
QueryComponent::In(q_values) => {
let first = q_values.first().ok_or_else(|| {
QueryParseError(
"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(QueryParseError(format!("IN queries must not combine numeric and string values! ({v} is not a number)")))
}
})
.collect::<Result<Vec<f64>, QueryParseError>>()?,
),
)),
_ => subqueries.push(Box::new(
data::value_str.eq_any(
q_values
.iter()
.map(|v| {
if let EntryValue::Number(_) = v {
Err(QueryParseError(format!("IN queries must not combine numeric and string values! (Found {v})")))
} else {
v.to_string().map_err(|e| QueryParseError(format!("failed producing sql: {e}")))
}
})
.collect::<Result<Vec<String>, QueryParseError>>()?,
),
)),
}
}
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::<Bool>())),
1 => Ok(subqueries.remove(0)),
_ => {
let mut result: Box<And<Box<Predicate>, Box<Predicate>>> =
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<Vec<Box<Predicate>>, QueryParseError> = mq
.queries
.iter()
.map(|sq| sq.to_sqlite_predicates())
.collect();
let mut subqueries: Vec<Box<Predicate>> = subqueries?;
match subqueries.len() {
0 => Ok(Box::new(true.into_sql::<Bool>())),
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<And<Box<Predicate>, Box<Predicate>>> =
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(QueryParseError("NOT only takes one subquery.".into()))
}
},
}
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use anyhow::{anyhow, Result}; use anyhow::{Result};
#[test] #[test]
fn test_matches() -> Result<()> { fn test_matches() -> Result<()> {
let query = "(matches ? ? ?)".parse::<Query>()?; let query = "(matches ? ? ?)".parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::Any value: QueryComponent::Any
})) }))
); );
query.to_sqlite_predicates()?;
let address = Address::Url(String::from("https://upendproject.net")); let address = Address::Url(String::from("https://upendproject.net"));
let query = format!("(matches @{address} ? ?)").parse::<Query>()?; let query = format!("(matches @{address} ? ?)").parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Exact(address), entity: QueryComponent::Exact(address),
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::Any value: QueryComponent::Any
})) }))
); );
query.to_sqlite_predicates()?;
let query = r#"(matches ? "FOO" ?)"#.parse::<Query>()?; let query = r#"(matches ? "FOO" ?)"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Exact("FOO".into()), attribute: QueryComponent::Exact("FOO".into()),
value: QueryComponent::Any value: QueryComponent::Any
})) }))
); );
query.to_sqlite_predicates()?;
let value = EntryValue::Number(1337.93); let value = EntryValue::Number(1337.93);
let query = "(matches ? ? 1337.93)".parse::<Query>()?; let query = "(matches ? ? 1337.93)".parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::Exact(value) value: QueryComponent::Exact(value)
})) }))
); );
query.to_sqlite_predicates()?;
Ok(()) Ok(())
} }
@ -514,37 +353,34 @@ mod test {
let query = r#"(matches ? (in "FOO" "BAR") ?)"#.parse::<Query>()?; let query = r#"(matches ? (in "FOO" "BAR") ?)"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::In(vec!("FOO".into(), "BAR".into())), attribute: QueryComponent::In(vec!("FOO".into(), "BAR".into())),
value: QueryComponent::Any value: QueryComponent::Any
})) }))
); );
query.to_sqlite_predicates()?;
let values: Vec<EntryValue> = vec!["FOO".into(), "BAR".into()]; let values: Vec<EntryValue> = vec!["FOO".into(), "BAR".into()];
let query = r#"(matches ? ? (in "FOO" "BAR"))"#.parse::<Query>()?; let query = r#"(matches ? ? (in "FOO" "BAR"))"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::In(values) value: QueryComponent::In(values)
})) }))
); );
query.to_sqlite_predicates()?;
let values = vec![EntryValue::Number(1337.93), EntryValue::Number(1968.12)]; let values = vec![EntryValue::Number(1337.93), EntryValue::Number(1968.12)];
let query = r#"(matches ? ? (in 1337.93 1968.12))"#.parse::<Query>()?; let query = r#"(matches ? ? (in 1337.93 1968.12))"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::In(values) value: QueryComponent::In(values)
})) }))
); );
query.to_sqlite_predicates()?;
// Invalid queries // Invalid queries
let values: Vec<EntryValue> = vec!["FOO".into(), EntryValue::Number(1337.93)]; let values: Vec<EntryValue> = vec!["FOO".into(), EntryValue::Number(1337.93)];
@ -552,32 +388,24 @@ mod test {
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::In(values) value: QueryComponent::In(values)
})) }))
); );
query
.to_sqlite_predicates()
.err()
.ok_or(anyhow!("Failed to reject mixed query."))?;
let values = vec![EntryValue::Number(1337.93), "FOO".into()]; let values = vec![EntryValue::Number(1337.93), "FOO".into()];
let query = r#"(matches ? ? (in 1337.93 "FOO"))"#.parse::<Query>()?; let query = r#"(matches ? ? (in 1337.93 "FOO"))"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::In(values) value: QueryComponent::In(values)
})) }))
); );
query
.to_sqlite_predicates()
.err()
.ok_or(anyhow!("Failed to reject mixed query."))?;
Ok(()) Ok(())
} }
@ -587,35 +415,32 @@ mod test {
let query = r#"(matches (contains "foo") ? ?)"#.parse::<Query>()?; let query = r#"(matches (contains "foo") ? ?)"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Contains("foo".to_string()), entity: QueryComponent::Contains("foo".to_string()),
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::Any value: QueryComponent::Any
})) }))
); );
query.to_sqlite_predicates()?;
let query = r#"(matches ? (contains "foo") ?)"#.parse::<Query>()?; let query = r#"(matches ? (contains "foo") ?)"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Contains("foo".to_string()), attribute: QueryComponent::Contains("foo".to_string()),
value: QueryComponent::Any, value: QueryComponent::Any,
})) }))
); );
query.to_sqlite_predicates()?;
let query = r#"(matches ? ? (contains "foo"))"#.parse::<Query>()?; let query = r#"(matches ? ? (contains "foo"))"#.parse::<Query>()?;
assert_eq!( assert_eq!(
query, query,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(PatternQuery {
entity: QueryComponent::Any, entity: QueryComponent::Any,
attribute: QueryComponent::Any, attribute: QueryComponent::Any,
value: QueryComponent::Contains("foo".to_string()) value: QueryComponent::Contains("foo".to_string())
})) }))
); );
query.to_sqlite_predicates()?;
Ok(()) Ok(())
} }

View File

@ -4,6 +4,7 @@
mod macros; mod macros;
pub mod constants; pub mod constants;
pub mod engine;
pub mod entry; pub mod entry;
pub mod hierarchies; pub mod hierarchies;
pub mod inner; pub mod inner;
@ -13,6 +14,7 @@ use crate::addressing::{Address, Addressable};
use crate::database::constants::{ use crate::database::constants::{
IS_OF_TYPE_ATTR, LABEL_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_INVARIANT, IS_OF_TYPE_ATTR, LABEL_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_INVARIANT,
}; };
use crate::database::engine::execute;
use crate::database::entry::{Entry, EntryValue, ImmutableEntry}; use crate::database::entry::{Entry, EntryValue, ImmutableEntry};
use crate::database::inner::models; use crate::database::inner::models;
use crate::database::inner::schema::data; use crate::database::inner::schema::data;
@ -21,7 +23,6 @@ use crate::util::hash::Hash;
use crate::util::LoggerSink; use crate::util::LoggerSink;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use diesel::debug_query;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, PooledConnection}; use diesel::r2d2::{self, ConnectionManager, PooledConnection};
use diesel::result::{DatabaseErrorKind, Error}; use diesel::result::{DatabaseErrorKind, Error};
@ -345,17 +346,10 @@ impl UpEndConnection {
} }
pub fn query(&self, query: Query) -> Result<Vec<Entry>> { pub fn query(&self, query: Query) -> Result<Vec<Entry>> {
use crate::database::inner::schema::data::dsl::*;
trace!("Querying: {:?}", query); trace!("Querying: {:?}", query);
let db_query = data.filter(query.to_sqlite_predicates()?); let entries = execute(&self.conn, query)?;
let entries = entries
trace!("DB query: {}", debug_query(&db_query));
let matches = db_query.load::<models::Entry>(&self.conn)?;
let entries = matches
.iter() .iter()
.map(Entry::try_from) .map(Entry::try_from)
.filter_map(Result::ok) .filter_map(Result::ok)