use std::fmt::Debug;
use crate::{Database, DatabaseError, DatabaseValue, Row};
#[derive(Debug, Clone, Copy)]
pub enum SortDirection {
Asc,
Desc,
}
#[derive(Debug)]
pub struct Sort {
pub expression: Box<dyn Expression>,
pub direction: SortDirection,
}
impl Expression for Sort {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Sort(self)
}
}
#[derive(Debug, Clone)]
pub struct Join<'a> {
pub table_name: &'a str,
pub on: &'a str,
pub left: bool,
}
impl Expression for Join<'_> {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Join(self)
}
}
pub enum ExpressionType<'a> {
Eq(&'a Eq),
Gt(&'a Gt),
In(&'a In<'a>),
Lt(&'a Lt),
Or(&'a Or),
And(&'a And),
Gte(&'a Gte),
Lte(&'a Lte),
Join(&'a Join<'a>),
Sort(&'a Sort),
NotIn(&'a NotIn<'a>),
NotEq(&'a NotEq),
InList(&'a InList),
Literal(&'a Literal),
Coalesce(&'a Coalesce),
Identifier(&'a Identifier),
SelectQuery(&'a SelectQuery<'a>),
DatabaseValue(&'a DatabaseValue),
}
pub trait Expression: Send + Sync + Debug {
fn expression_type(&self) -> ExpressionType<'_>;
fn params(&self) -> Option<Vec<&DatabaseValue>> {
self.values().map(|x| {
x.into_iter()
.filter(|value| {
!value.is_null()
&& !matches!(value, DatabaseValue::Now | DatabaseValue::NowPlus(_))
})
.collect::<Vec<_>>()
})
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
None
}
fn is_null(&self) -> bool {
false
}
}
#[derive(Debug)]
pub struct Literal {
pub value: String,
}
impl From<&str> for Literal {
fn from(val: &str) -> Self {
Self {
value: val.to_string(),
}
}
}
impl From<&String> for Literal {
fn from(val: &String) -> Self {
Self { value: val.clone() }
}
}
impl From<String> for Literal {
fn from(val: String) -> Self {
Self { value: val }
}
}
impl From<Literal> for Box<dyn Expression> {
fn from(val: Literal) -> Self {
Box::new(val)
}
}
impl Expression for Literal {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Literal(self)
}
}
#[must_use]
pub fn literal(value: &str) -> Literal {
Literal {
value: value.to_string(),
}
}
#[derive(Debug)]
pub struct Identifier {
pub value: String,
}
impl From<&str> for Identifier {
fn from(val: &str) -> Self {
Self {
value: val.to_string(),
}
}
}
impl From<String> for Identifier {
fn from(val: String) -> Self {
Self { value: val }
}
}
impl From<Identifier> for Box<dyn Expression> {
fn from(val: Identifier) -> Self {
Box::new(val)
}
}
impl Expression for Identifier {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Identifier(self)
}
}
#[must_use]
pub fn identifier(value: &str) -> Identifier {
Identifier {
value: value.to_string(),
}
}
impl Expression for DatabaseValue {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::DatabaseValue(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
Some(vec![self])
}
fn is_null(&self) -> bool {
#[cfg(all(not(feature = "decimal"), not(feature = "uuid")))]
{
matches!(
self,
Self::Null
| Self::BoolOpt(None)
| Self::Real64Opt(None)
| Self::Real32Opt(None)
| Self::StringOpt(None)
| Self::Int64Opt(None)
| Self::UInt64Opt(None)
)
}
#[cfg(all(feature = "decimal", not(feature = "uuid")))]
{
matches!(
self,
Self::Null
| Self::BoolOpt(None)
| Self::Real64Opt(None)
| Self::Real32Opt(None)
| Self::StringOpt(None)
| Self::Int64Opt(None)
| Self::UInt64Opt(None)
| Self::DecimalOpt(None)
)
}
#[cfg(all(not(feature = "decimal"), feature = "uuid"))]
{
matches!(
self,
Self::Null
| Self::BoolOpt(None)
| Self::Real64Opt(None)
| Self::Real32Opt(None)
| Self::StringOpt(None)
| Self::Int64Opt(None)
| Self::UInt64Opt(None)
| Self::UuidOpt(None)
)
}
#[cfg(all(feature = "decimal", feature = "uuid"))]
{
matches!(
self,
Self::Null
| Self::BoolOpt(None)
| Self::Real64Opt(None)
| Self::Real32Opt(None)
| Self::StringOpt(None)
| Self::Int64Opt(None)
| Self::UInt64Opt(None)
| Self::DecimalOpt(None)
| Self::UuidOpt(None)
)
}
}
}
impl<T: Into<DatabaseValue>> From<T> for Box<dyn Expression> {
fn from(value: T) -> Self {
Box::new(value.into())
}
}
pub trait BooleanExpression: Expression {}
#[derive(Debug)]
pub struct And {
pub(crate) conditions: Vec<Box<dyn BooleanExpression>>,
}
impl BooleanExpression for And {}
impl Expression for And {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::And(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let values = self
.conditions
.iter()
.filter_map(|x| x.values())
.collect::<Vec<_>>()
.concat();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
#[derive(Debug)]
pub struct Or {
pub conditions: Vec<Box<dyn BooleanExpression>>,
}
impl BooleanExpression for Or {}
impl Expression for Or {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Or(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let values = self
.conditions
.iter()
.filter_map(|x| x.values())
.collect::<Vec<_>>()
.concat();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
#[derive(Debug)]
pub struct NotEq {
pub left: Identifier,
pub right: Box<dyn Expression>,
}
impl BooleanExpression for NotEq {}
impl Expression for NotEq {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::NotEq(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
self.right.values()
}
}
#[derive(Debug)]
pub struct Eq {
pub left: Identifier,
pub right: Box<dyn Expression>,
}
impl BooleanExpression for Eq {}
impl Expression for Eq {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Eq(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
self.right.values()
}
}
#[derive(Debug)]
pub struct Gt {
pub left: Identifier,
pub right: Box<dyn Expression>,
}
impl BooleanExpression for Gt {}
impl Expression for Gt {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Gt(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
self.right.values()
}
}
#[derive(Debug)]
pub struct Gte {
pub left: Identifier,
pub right: Box<dyn Expression>,
}
impl BooleanExpression for Gte {}
impl Expression for Gte {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Gte(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
self.right.values()
}
}
#[derive(Debug)]
pub struct Lt {
pub left: Identifier,
pub right: Box<dyn Expression>,
}
impl BooleanExpression for Lt {}
impl Expression for Lt {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Lt(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
self.right.values()
}
}
#[derive(Debug)]
pub struct Lte {
pub left: Identifier,
pub right: Box<dyn Expression>,
}
impl BooleanExpression for Lte {}
impl Expression for Lte {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Lte(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
self.right.values()
}
}
#[derive(Debug)]
pub struct In<'a> {
pub left: Identifier,
pub values: Box<dyn List + 'a>,
}
impl BooleanExpression for In<'_> {}
impl Expression for In<'_> {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::In(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let values = [
self.left.values().unwrap_or_default(),
self.values.values().unwrap_or_default(),
]
.concat();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
#[derive(Debug)]
pub struct NotIn<'a> {
pub left: Identifier,
pub values: Box<dyn List + 'a>,
}
impl BooleanExpression for NotIn<'_> {}
impl Expression for NotIn<'_> {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::NotIn(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let values = [
self.left.values().unwrap_or_default(),
self.values.values().unwrap_or_default(),
]
.concat();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
#[must_use]
pub fn sort<T>(expression: T, direction: SortDirection) -> Sort
where
T: Into<Box<dyn Expression>>,
{
Sort {
expression: expression.into(),
direction,
}
}
#[must_use]
pub fn where_eq<L, R>(left: L, right: R) -> Eq
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
Eq {
left: left.into(),
right: right.into(),
}
}
#[must_use]
pub fn where_not_eq<L, R>(left: L, right: R) -> NotEq
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
NotEq {
left: left.into(),
right: right.into(),
}
}
#[must_use]
pub fn where_gt<L, R>(left: L, right: R) -> Gt
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
Gt {
left: left.into(),
right: right.into(),
}
}
#[must_use]
pub fn where_gte<L, R>(left: L, right: R) -> Gte
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
Gte {
left: left.into(),
right: right.into(),
}
}
#[must_use]
pub fn where_lt<L, R>(left: L, right: R) -> Lt
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
Lt {
left: left.into(),
right: right.into(),
}
}
#[must_use]
pub fn where_lte<L, R>(left: L, right: R) -> Lte
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
Lte {
left: left.into(),
right: right.into(),
}
}
#[must_use]
pub fn where_and(conditions: Vec<Box<dyn BooleanExpression>>) -> And {
And { conditions }
}
#[must_use]
pub fn where_or(conditions: Vec<Box<dyn BooleanExpression>>) -> Or {
Or { conditions }
}
#[must_use]
pub const fn join<'a>(table_name: &'a str, on: &'a str) -> Join<'a> {
Join {
table_name,
on,
left: false,
}
}
#[must_use]
pub const fn left_join<'a>(table_name: &'a str, on: &'a str) -> Join<'a> {
Join {
table_name,
on,
left: true,
}
}
#[derive(Debug)]
pub struct Coalesce {
pub values: Vec<Box<dyn Expression>>,
}
impl List for Coalesce {}
impl Expression for Coalesce {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::Coalesce(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let values = self
.values
.iter()
.flat_map(|x| x.values().unwrap_or_default())
.collect::<Vec<_>>();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
#[must_use]
pub fn coalesce(values: Vec<Box<dyn Expression>>) -> Coalesce {
Coalesce { values }
}
#[derive(Debug)]
pub struct InList {
pub values: Vec<Box<dyn Expression>>,
}
impl List for InList {}
impl Expression for InList {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::InList(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let values = self
.values
.iter()
.flat_map(|x| x.values().unwrap_or_default())
.collect::<Vec<_>>();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
pub trait List: Expression {}
impl<T> From<Vec<T>> for Box<dyn List>
where
T: Into<Box<dyn Expression>> + Send + Sync,
{
fn from(val: Vec<T>) -> Self {
Box::new(InList {
values: val.into_iter().map(std::convert::Into::into).collect(),
})
}
}
#[must_use]
pub fn where_in<'a, L, V>(left: L, values: V) -> In<'a>
where
L: Into<Identifier>,
V: Into<Box<dyn List + 'a>>,
{
In {
left: left.into(),
values: values.into(),
}
}
#[must_use]
pub fn where_not_in<'a, L, V>(left: L, values: V) -> NotIn<'a>
where
L: Into<Identifier>,
V: Into<Box<dyn List + 'a>>,
{
NotIn {
left: left.into(),
values: values.into(),
}
}
#[macro_export]
macro_rules! boxed {
() => (
Vec::new()
);
($($x:expr),+ $(,)?) => (
vec![$(Box::new($x)),+]
);
}
#[allow(clippy::module_name_repetitions)]
pub trait FilterableQuery
where
Self: Sized,
{
#[must_use]
fn filters(self, filters: Vec<Box<dyn BooleanExpression>>) -> Self {
let mut this = self;
for filter in filters {
this = this.filter(filter);
}
this
}
#[must_use]
fn filter(self, filter: Box<dyn BooleanExpression>) -> Self;
#[must_use]
fn filter_if_some<T: BooleanExpression + 'static>(self, filter: Option<T>) -> Self {
if let Some(filter) = filter {
self.filter(Box::new(filter))
} else {
self
}
}
#[must_use]
fn where_in<L, V>(self, left: L, values: V) -> Self
where
L: Into<Identifier>,
V: Into<Box<dyn List>>,
{
self.filter(Box::new(where_in(left, values)))
}
#[must_use]
fn where_not_in<L, V>(self, left: L, values: V) -> Self
where
L: Into<Identifier>,
V: Into<Box<dyn List>>,
{
self.filter(Box::new(where_not_in(left, values)))
}
#[must_use]
fn where_and(self, conditions: Vec<Box<dyn BooleanExpression>>) -> Self {
self.filter(Box::new(where_and(conditions)))
}
#[must_use]
fn where_or(self, conditions: Vec<Box<dyn BooleanExpression>>) -> Self {
self.filter(Box::new(where_or(conditions)))
}
#[must_use]
fn where_eq<L, R>(self, left: L, right: R) -> Self
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
self.filter(Box::new(where_eq(left, right)))
}
#[must_use]
fn where_not_eq<L, R>(self, left: L, right: R) -> Self
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
self.filter(Box::new(where_not_eq(left, right)))
}
#[must_use]
fn where_gt<L, R>(self, left: L, right: R) -> Self
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
self.filter(Box::new(where_gt(left, right)))
}
#[must_use]
fn where_gte<L, R>(self, left: L, right: R) -> Self
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
self.filter(Box::new(where_gte(left, right)))
}
#[must_use]
fn where_lt<L, R>(self, left: L, right: R) -> Self
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
self.filter(Box::new(where_lt(left, right)))
}
#[must_use]
fn where_lte<L, R>(self, left: L, right: R) -> Self
where
L: Into<Identifier>,
R: Into<Box<dyn Expression>>,
{
self.filter(Box::new(where_lte(left, right)))
}
}
impl<'a> From<SelectQuery<'a>> for Box<dyn List + 'a> {
fn from(val: SelectQuery<'a>) -> Self {
Box::new(val)
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct SelectQuery<'a> {
pub table_name: &'a str,
pub distinct: bool,
pub columns: &'a [&'a str],
pub filters: Option<Vec<Box<dyn BooleanExpression>>>,
pub joins: Option<Vec<Join<'a>>>,
pub sorts: Option<Vec<Sort>>,
pub limit: Option<usize>,
}
impl List for SelectQuery<'_> {}
impl Expression for SelectQuery<'_> {
fn expression_type(&self) -> ExpressionType<'_> {
ExpressionType::SelectQuery(self)
}
fn values(&self) -> Option<Vec<&DatabaseValue>> {
let joins_values = self
.joins
.as_ref()
.map(|x| {
x.iter()
.flat_map(|j| j.values().unwrap_or_default())
.collect::<Vec<_>>()
})
.unwrap_or_default();
let filters_values = self
.filters
.as_ref()
.map(|x| {
x.iter()
.flat_map(|j| j.values().unwrap_or_default())
.collect::<Vec<_>>()
})
.unwrap_or_default();
let sorts_values = self
.sorts
.as_ref()
.map(|x| {
x.iter()
.flat_map(|j| j.values().unwrap_or_default())
.collect::<Vec<_>>()
})
.unwrap_or_default();
let values: Vec<_> = [joins_values, filters_values, sorts_values].concat();
if values.is_empty() {
None
} else {
Some(values)
}
}
}
#[must_use]
pub fn select(table_name: &str) -> SelectQuery<'_> {
SelectQuery {
table_name,
distinct: false,
columns: &["*"],
filters: None,
joins: None,
sorts: None,
limit: None,
}
}
impl FilterableQuery for SelectQuery<'_> {
fn filter(mut self, filter: Box<dyn BooleanExpression>) -> Self {
if let Some(filters) = &mut self.filters {
filters.push(filter);
} else {
self.filters.replace(vec![filter]);
}
self
}
}
impl<'a> SelectQuery<'a> {
#[must_use]
pub const fn distinct(mut self) -> Self {
self.distinct = true;
self
}
#[must_use]
pub const fn columns(mut self, columns: &'a [&'a str]) -> Self {
self.columns = columns;
self
}
#[must_use]
pub fn joins(mut self, joins: Vec<Join<'a>>) -> Self {
for join in joins {
if let Some(joins) = &mut self.joins {
joins.push(join);
} else {
self.joins.replace(vec![join]);
}
}
self
}
#[must_use]
pub fn join(mut self, table_name: &'a str, on: &'a str) -> Self {
if let Some(joins) = &mut self.joins {
joins.push(join(table_name, on));
} else {
self.joins.replace(vec![join(table_name, on)]);
}
self
}
#[must_use]
pub fn left_joins(mut self, left_joins: Vec<Join<'a>>) -> Self {
for left_join in left_joins {
if let Some(left_joins) = &mut self.joins {
left_joins.push(left_join);
} else {
self.joins.replace(vec![left_join]);
}
}
self
}
#[must_use]
pub fn left_join(mut self, table_name: &'a str, on: &'a str) -> Self {
if let Some(left_joins) = &mut self.joins {
left_joins.push(left_join(table_name, on));
} else {
self.joins.replace(vec![left_join(table_name, on)]);
}
self
}
#[must_use]
pub fn sorts(mut self, sorts: Vec<Sort>) -> Self {
for sort in sorts {
if let Some(sorts) = &mut self.sorts {
sorts.push(sort);
} else {
self.sorts.replace(vec![sort]);
}
}
self
}
#[must_use]
pub fn sort<T>(mut self, expression: T, direction: SortDirection) -> Self
where
T: Into<Identifier>,
{
if let Some(sorts) = &mut self.sorts {
sorts.push(sort(expression.into(), direction));
} else {
self.sorts.replace(vec![sort(expression.into(), direction)]);
}
self
}
#[must_use]
pub const fn limit(mut self, limit: usize) -> Self {
self.limit.replace(limit);
self
}
pub async fn execute(self, db: &dyn Database) -> Result<Vec<Row>, DatabaseError> {
db.query(&self).await
}
pub async fn execute_first(self, db: &dyn Database) -> Result<Option<Row>, DatabaseError> {
let this = if self.limit.is_none() {
self.limit(1)
} else {
self
};
db.query_first(&this).await
}
}
pub struct UpsertMultiStatement<'a> {
pub table_name: &'a str,
pub values: Vec<Vec<(&'a str, Box<dyn Expression>)>>,
pub unique: Option<Vec<Box<dyn Expression>>>,
}
#[must_use]
pub fn upsert_multi(table_name: &str) -> UpsertMultiStatement<'_> {
UpsertMultiStatement {
table_name,
values: vec![],
unique: None,
}
}
impl<'a> UpsertMultiStatement<'a> {
pub fn values<T: Into<Box<dyn Expression>>>(
&mut self,
values: Vec<Vec<(&'a str, T)>>,
) -> &mut Self {
self.values.extend(values.into_iter().map(|values| {
values
.into_iter()
.map(|(key, value)| (key, value.into()))
.collect()
}));
self
}
pub fn unique(&mut self, unique: Vec<Box<dyn Expression>>) -> &mut Self {
self.unique.replace(unique);
self
}
pub async fn execute(&self, db: &dyn Database) -> Result<Vec<Row>, DatabaseError> {
db.exec_upsert_multi(self).await
}
}
pub struct InsertStatement<'a> {
pub table_name: &'a str,
pub values: Vec<(&'a str, Box<dyn Expression>)>,
}
#[must_use]
pub fn insert(table_name: &str) -> InsertStatement<'_> {
InsertStatement {
table_name,
values: vec![],
}
}
impl<'a> InsertStatement<'a> {
#[must_use]
pub fn values<T: Into<Box<dyn Expression>>>(mut self, values: Vec<(&'a str, T)>) -> Self {
for value in values {
self.values.push((value.0, value.1.into()));
}
self
}
#[must_use]
pub fn value<T: Into<Box<dyn Expression>>>(mut self, name: &'a str, value: T) -> Self {
self.values.push((name, value.into()));
self
}
pub async fn execute(&self, db: &dyn Database) -> Result<Row, DatabaseError> {
db.exec_insert(self).await
}
}
pub struct UpdateStatement<'a> {
pub table_name: &'a str,
pub values: Vec<(&'a str, Box<dyn Expression>)>,
pub filters: Option<Vec<Box<dyn BooleanExpression>>>,
pub unique: Option<&'a [&'a str]>,
pub limit: Option<usize>,
}
#[must_use]
pub fn update(table_name: &str) -> UpdateStatement<'_> {
UpdateStatement {
table_name,
values: vec![],
filters: None,
unique: None,
limit: None,
}
}
impl FilterableQuery for UpdateStatement<'_> {
fn filter(mut self, filter: Box<dyn BooleanExpression>) -> Self {
if let Some(filters) = &mut self.filters {
filters.push(filter);
} else {
self.filters.replace(vec![filter]);
}
self
}
}
impl<'a> UpdateStatement<'a> {
#[must_use]
pub fn values<T: Into<Box<dyn Expression>>>(mut self, values: Vec<(&'a str, T)>) -> Self {
for value in values {
self.values.push((value.0, value.1.into()));
}
self
}
#[must_use]
pub fn value<T: Into<Box<dyn Expression>>>(mut self, name: &'a str, value: T) -> Self {
self.values.push((name, value.into()));
self
}
#[must_use]
pub const fn unique(mut self, unique: &'a [&'a str]) -> Self {
self.unique.replace(unique);
self
}
#[must_use]
pub const fn limit(mut self, limit: usize) -> Self {
self.limit.replace(limit);
self
}
pub async fn execute(&self, db: &dyn Database) -> Result<Vec<Row>, DatabaseError> {
db.exec_update(self).await
}
pub async fn execute_first(&self, db: &dyn Database) -> Result<Option<Row>, DatabaseError> {
db.exec_update_first(self).await
}
}
pub struct UpsertStatement<'a> {
pub table_name: &'a str,
pub values: Vec<(&'a str, Box<dyn Expression>)>,
pub filters: Option<Vec<Box<dyn BooleanExpression>>>,
pub unique: Option<&'a [&'a str]>,
pub limit: Option<usize>,
}
#[must_use]
pub fn upsert(table_name: &str) -> UpsertStatement<'_> {
UpsertStatement {
table_name,
values: vec![],
filters: None,
unique: None,
limit: None,
}
}
impl FilterableQuery for UpsertStatement<'_> {
fn filter(mut self, filter: Box<dyn BooleanExpression>) -> Self {
if let Some(filters) = &mut self.filters {
filters.push(filter);
} else {
self.filters.replace(vec![filter]);
}
self
}
}
impl<'a> UpsertStatement<'a> {
#[must_use]
pub fn values<T: Into<Box<dyn Expression>>>(mut self, values: Vec<(&'a str, T)>) -> Self {
for value in values {
self.values.push((value.0, value.1.into()));
}
self
}
#[must_use]
pub fn value<T: Into<Box<dyn Expression>>>(mut self, name: &'a str, value: T) -> Self {
self.values.push((name, value.into()));
self
}
#[must_use]
pub fn value_opt<T: Into<Box<dyn Expression>>>(
mut self,
name: &'a str,
value: Option<T>,
) -> Self {
if let Some(value) = value {
self.values.push((name, value.into()));
}
self
}
#[must_use]
pub const fn unique(mut self, unique: &'a [&'a str]) -> Self {
self.unique.replace(unique);
self
}
#[must_use]
pub const fn limit(mut self, limit: usize) -> Self {
self.limit.replace(limit);
self
}
pub async fn execute(self, db: &dyn Database) -> Result<Vec<Row>, DatabaseError> {
if self.values.is_empty() {
return db.query(&self.into()).await;
}
db.exec_upsert(&self).await
}
pub async fn execute_first(self, db: &dyn Database) -> Result<Row, DatabaseError> {
if self.values.is_empty() {
return db
.query_first(&self.into())
.await?
.ok_or(DatabaseError::NoRow);
}
db.exec_upsert_first(&self).await
}
}
impl<'a> From<UpsertStatement<'a>> for SelectQuery<'a> {
fn from(value: UpsertStatement<'a>) -> Self {
Self {
table_name: value.table_name,
distinct: false,
columns: &["*"],
filters: value.filters,
joins: None,
sorts: None,
limit: value.limit,
}
}
}
pub struct DeleteStatement<'a> {
pub table_name: &'a str,
pub filters: Option<Vec<Box<dyn BooleanExpression>>>,
pub limit: Option<usize>,
}
#[must_use]
pub fn delete(table_name: &str) -> DeleteStatement<'_> {
DeleteStatement {
table_name,
filters: None,
limit: None,
}
}
impl FilterableQuery for DeleteStatement<'_> {
fn filter(mut self, filter: Box<dyn BooleanExpression>) -> Self {
if let Some(filters) = &mut self.filters {
filters.push(filter);
} else {
self.filters.replace(vec![filter]);
}
self
}
}
impl DeleteStatement<'_> {
#[must_use]
pub const fn limit(mut self, limit: usize) -> Self {
self.limit.replace(limit);
self
}
pub async fn execute(&self, db: &dyn Database) -> Result<Vec<Row>, DatabaseError> {
db.exec_delete(self).await
}
pub async fn execute_first(&self, db: &dyn Database) -> Result<Option<Row>, DatabaseError> {
db.exec_delete_first(self).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{DatabaseValue, sql_interval::SqlInterval};
mod expression_params_tests {
use super::*;
#[test_log::test]
fn test_params_filters_null_values() {
let val = DatabaseValue::Null;
let params = val.params();
assert!(params.is_some());
assert!(params.unwrap().is_empty());
}
#[test_log::test]
fn test_params_filters_now() {
let val = DatabaseValue::Now;
let params = val.params();
assert!(params.is_some());
assert!(params.unwrap().is_empty());
}
#[test_log::test]
fn test_params_filters_now_plus() {
let val = DatabaseValue::NowPlus(SqlInterval::from_days(1));
let params = val.params();
assert!(params.is_some());
assert!(params.unwrap().is_empty());
}
#[test_log::test]
fn test_params_includes_regular_values() {
let val = DatabaseValue::Int64(42);
let params = val.params();
assert!(params.is_some());
let params = params.unwrap();
assert_eq!(params.len(), 1);
assert_eq!(params[0], &DatabaseValue::Int64(42));
}
#[test_log::test]
fn test_params_includes_string_values() {
let val = DatabaseValue::String("test".to_string());
let params = val.params();
assert!(params.is_some());
let params = params.unwrap();
assert_eq!(params.len(), 1);
}
}
mod and_expression_tests {
use super::*;
#[test_log::test]
fn test_and_with_no_conditions_returns_none() {
let and_expr = And { conditions: vec![] };
assert!(and_expr.values().is_none());
}
#[test_log::test]
fn test_and_collects_values_from_conditions() {
let eq1 = where_eq("col1", DatabaseValue::Int64(1));
let eq2 = where_eq("col2", DatabaseValue::String("test".to_string()));
let and_expr = where_and(vec![Box::new(eq1), Box::new(eq2)]);
let values = and_expr.values();
assert!(values.is_some());
let values = values.unwrap();
assert_eq!(values.len(), 2);
}
#[test_log::test]
fn test_and_params_filters_non_bindable() {
let eq1 = where_eq("col1", DatabaseValue::Now);
let eq2 = where_eq("col2", DatabaseValue::Int64(42));
let and_expr = where_and(vec![Box::new(eq1), Box::new(eq2)]);
let params = and_expr.params();
assert!(params.is_some());
let params = params.unwrap();
assert_eq!(params.len(), 1);
assert_eq!(params[0], &DatabaseValue::Int64(42));
}
}
mod or_expression_tests {
use super::*;
#[test_log::test]
fn test_or_with_no_conditions_returns_none() {
let or_expr = Or { conditions: vec![] };
assert!(or_expr.values().is_none());
}
#[test_log::test]
fn test_or_collects_values_from_conditions() {
let eq1 = where_eq("status", DatabaseValue::String("active".to_string()));
let eq2 = where_eq("status", DatabaseValue::String("pending".to_string()));
let or_expr = where_or(vec![Box::new(eq1), Box::new(eq2)]);
let values = or_expr.values();
assert!(values.is_some());
let values = values.unwrap();
assert_eq!(values.len(), 2);
}
}
mod in_expression_tests {
use super::*;
#[test_log::test]
fn test_in_values_collection() {
let in_expr = where_in(
"id",
vec![
DatabaseValue::Int64(1),
DatabaseValue::Int64(2),
DatabaseValue::Int64(3),
],
);
let values = in_expr.values();
assert!(values.is_some());
let values = values.unwrap();
assert_eq!(values.len(), 3);
}
#[test_log::test]
fn test_not_in_values_collection() {
let not_in = where_not_in(
"status",
vec![
DatabaseValue::String("deleted".to_string()),
DatabaseValue::String("archived".to_string()),
],
);
let values = not_in.values();
assert!(values.is_some());
let values = values.unwrap();
assert_eq!(values.len(), 2);
}
}
mod coalesce_tests {
use super::*;
#[test_log::test]
fn test_coalesce_empty_returns_none() {
let coal = coalesce(vec![]);
assert!(coal.values().is_none());
}
#[test_log::test]
fn test_coalesce_collects_values() {
let coal = coalesce(vec![
Box::new(DatabaseValue::StringOpt(None)),
Box::new(DatabaseValue::String("default".to_string())),
]);
let values = coal.values();
assert!(values.is_some());
let values = values.unwrap();
assert_eq!(values.len(), 2);
}
}
mod inlist_tests {
use super::*;
#[test_log::test]
fn test_inlist_empty_returns_none() {
let list = InList { values: vec![] };
assert!(list.values().is_none());
}
#[test_log::test]
fn test_inlist_collects_values() {
let list = InList {
values: vec![
Box::new(DatabaseValue::Int64(1)),
Box::new(DatabaseValue::Int64(2)),
],
};
let values = list.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 2);
}
}
mod comparison_expression_tests {
use super::*;
#[test_log::test]
fn test_eq_values() {
let eq = where_eq("col", DatabaseValue::Int64(42));
let values = eq.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
#[test_log::test]
fn test_not_eq_values() {
let neq = where_not_eq("col", DatabaseValue::String("test".to_string()));
let values = neq.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
#[test_log::test]
fn test_gt_values() {
let gt = where_gt("age", DatabaseValue::Int32(18));
let values = gt.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
#[test_log::test]
fn test_gte_values() {
let gte = where_gte("score", DatabaseValue::Int32(100));
let values = gte.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
#[test_log::test]
fn test_lt_values() {
let lt = where_lt("quantity", DatabaseValue::Int32(10));
let values = lt.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
#[test_log::test]
fn test_lte_values() {
let lte = where_lte("price", DatabaseValue::Real64(99.99));
let values = lte.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
}
mod select_query_tests {
use super::*;
#[test_log::test]
fn test_select_query_no_filters_returns_none() {
let query = select("users");
assert!(query.values().is_none());
}
#[test_log::test]
fn test_select_query_with_filter_values() {
let query = select("users").where_eq("id", DatabaseValue::Int64(1));
let values = query.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 1);
}
#[test_log::test]
fn test_select_query_with_multiple_filters() {
let query = select("users")
.where_eq("active", DatabaseValue::Bool(true))
.where_gt("age", DatabaseValue::Int32(18));
let values = query.values();
assert!(values.is_some());
assert_eq!(values.unwrap().len(), 2);
}
}
mod literal_tests {
use super::*;
#[test_log::test]
fn test_literal_from_str() {
let lit: Literal = "COUNT(*)".into();
assert_eq!(lit.value, "COUNT(*)");
}
#[test_log::test]
fn test_literal_from_string() {
let lit: Literal = String::from("SUM(amount)").into();
assert_eq!(lit.value, "SUM(amount)");
}
#[test_log::test]
fn test_literal_function() {
let lit = literal("NOW()");
assert_eq!(lit.value, "NOW()");
}
}
mod identifier_tests {
use super::*;
#[test_log::test]
fn test_identifier_from_str() {
let id: Identifier = "user_id".into();
assert_eq!(id.value, "user_id");
}
#[test_log::test]
fn test_identifier_from_string() {
let id: Identifier = String::from("column_name").into();
assert_eq!(id.value, "column_name");
}
#[test_log::test]
fn test_identifier_function() {
let id = identifier("table.column");
assert_eq!(id.value, "table.column");
}
}
mod join_tests {
use super::*;
#[test_log::test]
fn test_inner_join_creation() {
let j = join("orders", "orders.user_id = users.id");
assert_eq!(j.table_name, "orders");
assert_eq!(j.on, "orders.user_id = users.id");
assert!(!j.left);
}
#[test_log::test]
fn test_left_join_creation() {
let j = left_join("addresses", "users.address_id = addresses.id");
assert_eq!(j.table_name, "addresses");
assert!(j.left);
}
}
mod sort_tests {
use super::*;
#[test_log::test]
fn test_sort_ascending() {
let s = sort(identifier("name"), SortDirection::Asc);
assert!(matches!(s.direction, SortDirection::Asc));
}
#[test_log::test]
fn test_sort_descending() {
let s = sort(identifier("created_at"), SortDirection::Desc);
assert!(matches!(s.direction, SortDirection::Desc));
}
}
mod boxed_macro_tests {
use super::*;
#[test_log::test]
fn test_boxed_empty() {
let v: Vec<Box<dyn BooleanExpression>> = boxed![];
assert!(v.is_empty());
}
#[test_log::test]
fn test_boxed_single() {
let v: Vec<Box<dyn BooleanExpression>> =
boxed![where_eq("id", DatabaseValue::Int64(1))];
assert_eq!(v.len(), 1);
}
#[test_log::test]
fn test_boxed_multiple() {
let v: Vec<Box<dyn BooleanExpression>> = boxed![
where_eq("id", DatabaseValue::Int64(1)),
where_gt("age", DatabaseValue::Int32(18)),
];
assert_eq!(v.len(), 2);
}
}
mod filterable_query_tests {
use super::*;
#[test_log::test]
fn test_filter_if_some_with_value() {
let query =
select("users").filter_if_some(Some(where_eq("id", DatabaseValue::Int64(1))));
assert!(query.filters.is_some());
assert_eq!(query.filters.unwrap().len(), 1);
}
#[test_log::test]
fn test_filter_if_some_with_none() {
let query = select("users").filter_if_some::<Eq>(None);
assert!(query.filters.is_none());
}
#[test_log::test]
fn test_filters_adds_multiple() {
let conditions: Vec<Box<dyn BooleanExpression>> = vec![
Box::new(where_eq("a", DatabaseValue::Int64(1))),
Box::new(where_eq("b", DatabaseValue::Int64(2))),
];
let query = select("table").filters(conditions);
assert!(query.filters.is_some());
assert_eq!(query.filters.unwrap().len(), 2);
}
}
}