use core::marker::PhantomData;
use crate::compile::compile;
use crate::dialect::Dialect;
use crate::value::{IntoBind, Value};
use crate::where_::{Conj, Predicate, WhereBuilder};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Order {
Asc,
Desc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JoinKind {
Inner,
Left,
Right,
FullOuter,
Cross,
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinCond {
On(String, &'static str, String),
OnVal(String, &'static str, Value),
OnRaw(String, Vec<Value>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Join {
pub kind: JoinKind,
pub table: String,
pub on: Vec<JoinCond>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Having {
Col {
col: String,
op: String,
val: Value,
},
Raw {
sql: String,
binds: Vec<Value>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct Cte<D: Dialect> {
pub name: String,
pub recursive: bool,
pub query: QueryBuilder<D>,
}
pub struct JoinClause<D: Dialect> {
conds: Vec<JoinCond>,
_marker: PhantomData<D>,
}
impl<D: Dialect> Default for JoinClause<D> {
fn default() -> Self {
Self::new()
}
}
impl<D: Dialect> JoinClause<D> {
pub fn new() -> Self {
Self {
conds: Vec::new(),
_marker: PhantomData,
}
}
fn into_conds(self) -> Vec<JoinCond> {
self.conds
}
pub fn on(mut self, col: &str, op: &'static str, col2: &str) -> Self {
self.conds
.push(JoinCond::On(col.to_owned(), op, col2.to_owned()));
self
}
pub fn on_val(mut self, col: &str, op: &'static str, val: impl IntoBind) -> Self {
self.conds
.push(JoinCond::OnVal(col.to_owned(), op, val.into_bind()));
self
}
pub fn on_raw(mut self, sql: &str, binds: Vec<Value>) -> Self {
self.conds.push(JoinCond::OnRaw(sql.to_owned(), binds));
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConflictAction {
DoNothing,
Merge,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OnConflict {
pub targets: Vec<String>,
pub action: ConflictAction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AggFn {
Count,
Sum,
Avg,
Min,
Max,
}
impl AggFn {
pub fn as_str(&self) -> &'static str {
match self {
AggFn::Count => "COUNT",
AggFn::Sum => "SUM",
AggFn::Avg => "AVG",
AggFn::Min => "MIN",
AggFn::Max => "MAX",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SelectExpr {
Agg {
func: AggFn,
col: String,
alias: Option<String>,
},
ColAs {
col: String,
alias: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockStrength {
Update,
Share,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockWait {
SkipLocked,
NoWait,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Lock {
pub strength: LockStrength,
pub wait: Option<LockWait>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Method {
#[default]
Select,
Insert,
Update,
Delete,
}
#[derive(Debug, Clone, PartialEq)]
pub struct QueryBuilder<D: Dialect> {
pub(crate) table: String,
pub(crate) db: Option<String>,
pub(crate) select_cols: Vec<String>,
pub(crate) select_exprs: Vec<SelectExpr>,
pub(crate) select_raw: Vec<(String, Vec<Value>)>,
pub(crate) select_subqueries: Vec<(String, Box<QueryBuilder<D>>)>,
pub(crate) distinct: bool,
pub(crate) distinct_on: Vec<String>,
pub(crate) wheres: Vec<Predicate<D>>,
pub(crate) method: Method,
pub(crate) set: Vec<(String, Value)>,
pub(crate) insert_rows: Vec<Vec<(String, Value)>>,
pub(crate) joins: Vec<Join>,
pub(crate) groups: Vec<String>,
pub(crate) group_by_raw: Option<(String, Vec<Value>)>,
pub(crate) havings: Vec<Having>,
pub(crate) orders: Vec<(String, Order)>,
pub(crate) order_by_raw: Option<(String, Vec<Value>)>,
pub(crate) limit: Option<i64>,
pub(crate) offset: Option<i64>,
pub(crate) ctes: Vec<Cte<D>>,
pub(crate) unions: Vec<(bool, QueryBuilder<D>)>,
pub(crate) on_conflict: Option<OnConflict>,
pub(crate) returning: Vec<String>,
pub(crate) lock: Option<Lock>,
_marker: PhantomData<D>,
}
impl<D: Dialect> QueryBuilder<D> {
pub fn table(name: &str) -> Self {
Self {
table: name.to_owned(),
db: None,
select_cols: Vec::new(),
select_exprs: Vec::new(),
select_raw: Vec::new(),
select_subqueries: Vec::new(),
distinct: false,
distinct_on: Vec::new(),
wheres: Vec::new(),
method: Method::Select,
set: Vec::new(),
insert_rows: Vec::new(),
joins: Vec::new(),
groups: Vec::new(),
group_by_raw: None,
havings: Vec::new(),
orders: Vec::new(),
order_by_raw: None,
limit: None,
offset: None,
ctes: Vec::new(),
unions: Vec::new(),
on_conflict: None,
returning: Vec::new(),
lock: None,
_marker: PhantomData,
}
}
pub fn db(mut self, name: &str) -> Self {
self.db = Some(name.to_owned());
self
}
pub fn select<I, S>(mut self, cols: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.select_cols = cols.into_iter().map(|c| c.as_ref().to_owned()).collect();
self
}
pub fn select_raw(mut self, sql: &str, binds: Option<Vec<Value>>) -> Self {
self.select_raw
.push((sql.to_owned(), binds.unwrap_or_default()));
self
}
pub fn select_subquery(mut self, alias: &str, sub: QueryBuilder<D>) -> Self {
self.select_subqueries
.push((alias.to_owned(), Box::new(sub)));
self
}
fn push_agg(mut self, func: AggFn, col: &str, alias: Option<&str>) -> Self {
self.select_exprs.push(SelectExpr::Agg {
func,
col: col.to_owned(),
alias: alias.map(|a| a.to_owned()),
});
self
}
pub fn select_count(self, col: &str) -> Self {
self.push_agg(AggFn::Count, col, None)
}
pub fn select_count_as(self, col: &str, alias: &str) -> Self {
self.push_agg(AggFn::Count, col, Some(alias))
}
pub fn select_sum(self, col: &str) -> Self {
self.push_agg(AggFn::Sum, col, None)
}
pub fn select_sum_as(self, col: &str, alias: &str) -> Self {
self.push_agg(AggFn::Sum, col, Some(alias))
}
pub fn select_avg(self, col: &str) -> Self {
self.push_agg(AggFn::Avg, col, None)
}
pub fn select_avg_as(self, col: &str, alias: &str) -> Self {
self.push_agg(AggFn::Avg, col, Some(alias))
}
pub fn select_min(self, col: &str) -> Self {
self.push_agg(AggFn::Min, col, None)
}
pub fn select_min_as(self, col: &str, alias: &str) -> Self {
self.push_agg(AggFn::Min, col, Some(alias))
}
pub fn select_max(self, col: &str) -> Self {
self.push_agg(AggFn::Max, col, None)
}
pub fn select_max_as(self, col: &str, alias: &str) -> Self {
self.push_agg(AggFn::Max, col, Some(alias))
}
pub fn select_as(mut self, col: &str, alias: &str) -> Self {
self.select_exprs.push(SelectExpr::ColAs {
col: col.to_owned(),
alias: alias.to_owned(),
});
self
}
pub fn distinct(mut self) -> Self {
self.distinct = true;
self
}
pub fn distinct_on<I, S>(mut self, cols: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.distinct_on = cols.into_iter().map(|c| c.as_ref().to_owned()).collect();
self.distinct = true;
self
}
pub fn where_ilike(mut self, col: &str, val: impl IntoBind) -> Self {
self.wheres.push(Predicate::ILike {
col: col.to_owned(),
val: val.into_bind(),
});
self
}
pub fn where_jsonb_contains(mut self, col: &str, val: impl IntoBind) -> Self {
self.wheres.push(Predicate::JsonContains {
col: col.to_owned(),
val: val.into_bind(),
});
self
}
fn binary(mut self, col: &str, op: &'static str, val: impl IntoBind) -> Self {
self.wheres.push(Predicate::Binary {
col: col.to_owned(),
op,
val: val.into_bind(),
});
self
}
pub fn where_eq(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "=", val)
}
pub fn where_ne(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "!=", val)
}
pub fn where_gt(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, ">", val)
}
pub fn where_gte(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, ">=", val)
}
pub fn where_lt(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "<", val)
}
pub fn where_lte(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "<=", val)
}
pub fn where_like(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "LIKE", val)
}
fn in_(mut self, col: &str, neg: bool, vals: impl IntoIterator<Item = impl IntoBind>) -> Self {
self.wheres.push(Predicate::In {
col: col.to_owned(),
neg,
vals: vals.into_iter().map(IntoBind::into_bind).collect(),
});
self
}
pub fn where_in(self, col: &str, vals: impl IntoIterator<Item = impl IntoBind>) -> Self {
self.in_(col, false, vals)
}
pub fn where_not_in(self, col: &str, vals: impl IntoIterator<Item = impl IntoBind>) -> Self {
self.in_(col, true, vals)
}
fn null(mut self, col: &str, neg: bool) -> Self {
self.wheres.push(Predicate::Null {
col: col.to_owned(),
neg,
});
self
}
pub fn where_null(self, col: &str) -> Self {
self.null(col, false)
}
pub fn where_not_null(self, col: &str) -> Self {
self.null(col, true)
}
pub fn where_between(mut self, col: &str, lo: impl IntoBind, hi: impl IntoBind) -> Self {
self.wheres.push(Predicate::Between {
col: col.to_owned(),
lo: lo.into_bind(),
hi: hi.into_bind(),
});
self
}
pub fn where_raw(mut self, sql: &str, binds: Vec<Value>) -> Self {
self.wheres.push(Predicate::Raw {
sql: sql.to_owned(),
binds,
});
self
}
pub fn where_column(mut self, lhs: &str, op: &'static str, rhs: &str) -> Self {
self.wheres.push(Predicate::Column {
lhs: lhs.to_owned(),
op,
rhs: rhs.to_owned(),
});
self
}
pub fn where_exists(mut self, sub: QueryBuilder<D>) -> Self {
self.wheres.push(Predicate::Exists {
neg: false,
sub: Box::new(sub),
});
self
}
pub fn where_not_exists(mut self, sub: QueryBuilder<D>) -> Self {
self.wheres.push(Predicate::Exists {
neg: true,
sub: Box::new(sub),
});
self
}
pub fn where_in_subquery(mut self, col: &str, sub: QueryBuilder<D>) -> Self {
self.wheres.push(Predicate::InSubquery {
col: col.to_owned(),
neg: false,
sub: Box::new(sub),
});
self
}
pub fn where_not_in_subquery(mut self, col: &str, sub: QueryBuilder<D>) -> Self {
self.wheres.push(Predicate::InSubquery {
col: col.to_owned(),
neg: true,
sub: Box::new(sub),
});
self
}
fn group(
mut self,
outer_conj: Conj,
f: impl FnOnce(WhereBuilder<D>) -> WhereBuilder<D>,
) -> Self {
let preds = f(WhereBuilder::new()).into_preds();
self.wheres.push(Predicate::Group { outer_conj, preds });
self
}
pub fn and_where(self, f: impl FnOnce(WhereBuilder<D>) -> WhereBuilder<D>) -> Self {
self.group(Conj::And, f)
}
pub fn or_where(self, f: impl FnOnce(WhereBuilder<D>) -> WhereBuilder<D>) -> Self {
self.group(Conj::Or, f)
}
pub fn insert<K, V, I>(mut self, row: I) -> Self
where
K: AsRef<str>,
V: IntoBind,
I: IntoIterator<Item = (K, V)>,
{
self.method = Method::Insert;
self.set = row
.into_iter()
.map(|(k, v)| (k.as_ref().to_owned(), v.into_bind()))
.collect();
self
}
pub fn insert_many<K, V, R, Rows>(mut self, rows: Rows) -> Self
where
K: AsRef<str>,
V: IntoBind,
R: IntoIterator<Item = (K, V)>,
Rows: IntoIterator<Item = R>,
{
self.method = Method::Insert;
self.insert_rows = rows
.into_iter()
.map(|row| {
row.into_iter()
.map(|(k, v)| (k.as_ref().to_owned(), v.into_bind()))
.collect()
})
.collect();
self
}
pub fn update<K, V, I>(mut self, set: I) -> Self
where
K: AsRef<str>,
V: IntoBind,
I: IntoIterator<Item = (K, V)>,
{
self.method = Method::Update;
self.set = set
.into_iter()
.map(|(k, v)| (k.as_ref().to_owned(), v.into_bind()))
.collect();
self
}
pub fn delete(mut self) -> Self {
self.method = Method::Delete;
self
}
pub fn on_conflict_do_nothing<I, S>(mut self, targets: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.on_conflict = Some(OnConflict {
targets: targets.into_iter().map(|c| c.as_ref().to_owned()).collect(),
action: ConflictAction::DoNothing,
});
self
}
pub fn on_conflict_merge<I, S>(mut self, targets: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.on_conflict = Some(OnConflict {
targets: targets.into_iter().map(|c| c.as_ref().to_owned()).collect(),
action: ConflictAction::Merge,
});
self
}
pub fn returning<I, S>(mut self, cols: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.returning = cols.into_iter().map(|c| c.as_ref().to_owned()).collect();
self
}
pub fn group_by<I, S>(mut self, cols: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.groups
.extend(cols.into_iter().map(|c| c.as_ref().to_owned()));
self
}
pub fn group_by_raw(mut self, sql: &str, binds: Vec<Value>) -> Self {
self.group_by_raw = Some((sql.to_owned(), binds));
self
}
pub fn order_by_raw(mut self, sql: &str, binds: Vec<Value>) -> Self {
self.order_by_raw = Some((sql.to_owned(), binds));
self
}
pub fn order_by(mut self, col: &str, ord: Order) -> Self {
self.orders.push((col.to_owned(), ord));
self
}
pub fn order_by_asc(self, col: &str) -> Self {
self.order_by(col, Order::Asc)
}
pub fn order_by_desc(self, col: &str) -> Self {
self.order_by(col, Order::Desc)
}
pub fn limit(mut self, n: i64) -> Self {
self.limit = Some(n);
self
}
pub fn offset(mut self, n: i64) -> Self {
self.offset = Some(n);
self
}
pub fn for_update(mut self) -> Self {
let wait = self.lock.and_then(|l| l.wait);
self.lock = Some(Lock {
strength: LockStrength::Update,
wait,
});
self
}
pub fn for_share(mut self) -> Self {
let wait = self.lock.and_then(|l| l.wait);
self.lock = Some(Lock {
strength: LockStrength::Share,
wait,
});
self
}
pub fn skip_locked(mut self) -> Self {
let strength = self
.lock
.map(|l| l.strength)
.unwrap_or(LockStrength::Update);
self.lock = Some(Lock {
strength,
wait: Some(LockWait::SkipLocked),
});
self
}
pub fn no_wait(mut self) -> Self {
let strength = self
.lock
.map(|l| l.strength)
.unwrap_or(LockStrength::Update);
self.lock = Some(Lock {
strength,
wait: Some(LockWait::NoWait),
});
self
}
fn push_join(
mut self,
kind: JoinKind,
table: &str,
f: impl FnOnce(JoinClause<D>) -> JoinClause<D>,
) -> Self {
let on = f(JoinClause::new()).into_conds();
self.joins.push(Join {
kind,
table: table.to_owned(),
on,
});
self
}
pub fn join(self, table: &str, f: impl FnOnce(JoinClause<D>) -> JoinClause<D>) -> Self {
self.push_join(JoinKind::Inner, table, f)
}
pub fn left_join(self, table: &str, f: impl FnOnce(JoinClause<D>) -> JoinClause<D>) -> Self {
self.push_join(JoinKind::Left, table, f)
}
pub fn right_join(self, table: &str, f: impl FnOnce(JoinClause<D>) -> JoinClause<D>) -> Self {
self.push_join(JoinKind::Right, table, f)
}
pub fn full_outer_join(
self,
table: &str,
f: impl FnOnce(JoinClause<D>) -> JoinClause<D>,
) -> Self {
self.push_join(JoinKind::FullOuter, table, f)
}
pub fn cross_join(mut self, table: &str) -> Self {
self.joins.push(Join {
kind: JoinKind::Cross,
table: table.to_owned(),
on: Vec::new(),
});
self
}
pub fn having(mut self, col: &str, op: &str, val: impl IntoBind) -> Self {
self.havings.push(Having::Col {
col: col.to_owned(),
op: op.to_owned(),
val: val.into_bind(),
});
self
}
pub fn having_raw(mut self, sql: &str, binds: Vec<Value>) -> Self {
self.havings.push(Having::Raw {
sql: sql.to_owned(),
binds,
});
self
}
pub fn with(mut self, name: &str, query: QueryBuilder<D>) -> Self {
self.ctes.push(Cte {
name: name.to_owned(),
recursive: false,
query,
});
self
}
pub fn with_recursive(mut self, name: &str, query: QueryBuilder<D>) -> Self {
self.ctes.push(Cte {
name: name.to_owned(),
recursive: true,
query,
});
self
}
pub fn union(mut self, query: QueryBuilder<D>) -> Self {
self.unions.push((false, query));
self
}
pub fn union_all(mut self, query: QueryBuilder<D>) -> Self {
self.unions.push((true, query));
self
}
pub fn when(self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
if cond {
f(self)
} else {
self
}
}
pub fn when_else(
self,
cond: bool,
if_true: impl FnOnce(Self) -> Self,
if_false: impl FnOnce(Self) -> Self,
) -> Self {
if cond {
if_true(self)
} else {
if_false(self)
}
}
pub fn paginate(self, page: i64, per_page: i64) -> Self {
self.limit(per_page).offset((page - 1).max(0) * per_page)
}
pub fn to_sql(&self) -> (String, Vec<Value>) {
compile(self)
}
}