use super::{validate::validate_value, ModelSchema, QueryError, SqlValue};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Op {
Eq,
Ne,
Lt,
Lte,
Gt,
Gte,
In,
NotIn,
Like,
NotLike,
ILike,
NotILike,
Between,
IsNull,
IsDistinctFrom,
IsNotDistinctFrom,
JsonContains,
JsonContainedBy,
JsonHasKey,
JsonHasAnyKey,
JsonHasAllKeys,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Filter {
pub column: &'static str,
pub op: Op,
pub value: SqlValue,
}
#[derive(Debug, Clone, PartialEq)]
pub enum WhereExpr {
Predicate(Filter),
And(Vec<WhereExpr>),
Or(Vec<WhereExpr>),
Not(Box<WhereExpr>),
}
impl WhereExpr {
#[must_use]
pub fn is_empty(&self) -> bool {
matches!(self, Self::And(items) if items.is_empty())
}
#[must_use]
pub fn and_predicates(filters: Vec<Filter>) -> Self {
Self::And(filters.into_iter().map(Self::Predicate).collect())
}
pub fn push_and(&mut self, child: Self) {
match self {
Self::And(items) => items.push(child),
_ => {
let prev = std::mem::replace(self, Self::And(Vec::new()));
if let Self::And(items) = self {
items.push(prev);
items.push(child);
}
}
}
}
#[must_use]
pub fn as_flat_and(&self) -> Option<Vec<&Filter>> {
match self {
Self::Predicate(f) => Some(vec![f]),
Self::And(items) => {
let mut out = Vec::with_capacity(items.len());
for item in items {
match item {
Self::Predicate(f) => out.push(f),
_ => return None,
}
}
Some(out)
}
Self::Or(_) | Self::Not(_) => None,
}
}
pub fn validate(&self, model: &'static ModelSchema) -> Result<(), QueryError> {
match self {
Self::Predicate(f) => {
if model.field_by_column(f.column).is_none() {
return Err(QueryError::UnknownField {
model: model.name,
field: f.column.to_owned(),
});
}
Ok(())
}
Self::And(items) | Self::Or(items) => {
for child in items {
child.validate(model)?;
}
Ok(())
}
Self::Not(child) => child.validate(model),
}
}
}
impl Default for WhereExpr {
fn default() -> Self {
Self::And(Vec::new())
}
}
impl From<Filter> for WhereExpr {
fn from(f: Filter) -> Self {
Self::Predicate(f)
}
}
#[derive(Debug, Clone)]
pub struct SelectQuery {
pub model: &'static ModelSchema,
pub where_clause: WhereExpr,
pub search: Option<SearchClause>,
pub joins: Vec<Join>,
pub order_by: Vec<OrderClause>,
pub limit: Option<i64>,
pub offset: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OrderClause {
pub column: &'static str,
pub desc: bool,
}
#[derive(Debug, Clone)]
pub struct Join {
pub target: &'static ModelSchema,
pub on_local: &'static str,
pub on_remote: &'static str,
pub alias: &'static str,
pub project: Vec<&'static str>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SearchClause {
pub columns: Vec<&'static str>,
pub query: String,
}
#[derive(Debug, Clone)]
pub enum ConflictClause {
DoNothing,
DoUpdate {
target: Vec<&'static str>,
update_columns: Vec<&'static str>,
},
}
#[derive(Debug, Clone)]
pub struct InsertQuery {
pub model: &'static ModelSchema,
pub columns: Vec<&'static str>,
pub values: Vec<SqlValue>,
pub returning: Vec<&'static str>,
pub on_conflict: Option<ConflictClause>,
}
impl InsertQuery {
pub fn validate(&self) -> Result<(), QueryError> {
for (column, value) in self.columns.iter().zip(self.values.iter()) {
let field =
self.model
.field_by_column(column)
.ok_or_else(|| QueryError::UnknownField {
model: self.model.name,
field: (*column).to_owned(),
})?;
validate_value(self.model.name, field, value)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct BulkInsertQuery {
pub model: &'static ModelSchema,
pub columns: Vec<&'static str>,
pub rows: Vec<Vec<SqlValue>>,
pub returning: Vec<&'static str>,
pub on_conflict: Option<ConflictClause>,
}
impl BulkInsertQuery {
pub fn validate(&self) -> Result<(), QueryError> {
for row in &self.rows {
for (column, value) in self.columns.iter().zip(row.iter()) {
let field =
self.model
.field_by_column(column)
.ok_or_else(|| QueryError::UnknownField {
model: self.model.name,
field: (*column).to_owned(),
})?;
validate_value(self.model.name, field, value)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Assignment {
pub column: &'static str,
pub value: SqlValue,
}
#[derive(Debug, Clone)]
pub struct UpdateQuery {
pub model: &'static ModelSchema,
pub set: Vec<Assignment>,
pub where_clause: WhereExpr,
}
impl UpdateQuery {
pub fn validate(&self) -> Result<(), QueryError> {
for assignment in &self.set {
let field = self
.model
.field_by_column(assignment.column)
.ok_or_else(|| QueryError::UnknownField {
model: self.model.name,
field: assignment.column.to_owned(),
})?;
validate_value(self.model.name, field, &assignment.value)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct DeleteQuery {
pub model: &'static ModelSchema,
pub where_clause: WhereExpr,
}
#[derive(Debug, Clone)]
pub struct CountQuery {
pub model: &'static ModelSchema,
pub where_clause: WhereExpr,
pub search: Option<SearchClause>,
}
#[derive(Debug, Clone)]
pub struct BulkUpdateQuery {
pub model: &'static ModelSchema,
pub update_columns: Vec<&'static str>,
pub rows: Vec<Vec<SqlValue>>,
}
#[derive(Debug, Clone)]
pub enum AggregateExpr {
Count(Option<&'static str>),
Sum(&'static str),
Avg(&'static str),
Max(&'static str),
Min(&'static str),
}
#[derive(Debug, Clone)]
pub struct AggregateQuery {
pub model: &'static ModelSchema,
pub where_clause: WhereExpr,
pub group_by: Vec<&'static str>,
pub aggregates: Vec<(&'static str, AggregateExpr)>,
pub having: Option<WhereExpr>,
pub order_by: Vec<OrderClause>,
pub limit: Option<i64>,
pub offset: Option<i64>,
}