use crate::error::{Error, Result};
use crate::model::Model;
use crate::query_builder::args::QueryBuilderArgs;
use crate::query_builder::{Placeholder, util};
use sql::{Expr, OrderBy, ToSql};
use crate::join::{JoinDescription, criteria, select_columns};
use sql::{Select, query::Where};
use sqlx::{Executor, IntoArguments};
use std::marker::PhantomData;
pub trait DatabaseMetadata {
fn dialect() -> sql::Dialect;
fn placeholder() -> Placeholder;
}
#[cfg(feature = "postgres")]
impl DatabaseMetadata for sqlx::postgres::Postgres {
fn dialect() -> sql::Dialect {
sql::Dialect::Postgres
}
fn placeholder() -> Placeholder {
Placeholder::dollar_sign()
}
}
#[cfg(feature = "sqlite")]
impl DatabaseMetadata for sqlx::sqlite::Sqlite {
fn dialect() -> sql::Dialect {
sql::Dialect::Sqlite
}
fn placeholder() -> Placeholder {
Placeholder::question_mark()
}
}
pub struct SelectQueryBuilder<'args, DB, Model>
where
DB: sqlx::Database,
{
pub query: Select,
arguments: QueryBuilderArgs<'args, DB>,
model: PhantomData<Model>,
placeholder: Placeholder,
}
impl<'args, DB, M> SelectQueryBuilder<'args, DB, M>
where
M: Sized + Send + Sync + Unpin + for<'r> sqlx::FromRow<'r, DB::Row> + 'static + Model<DB>,
DB: sqlx::Database + DatabaseMetadata,
DB::Arguments<'args>: IntoArguments<'args, DB>,
{
pub async fn fetch_all<'executor, E>(self, db: E) -> Result<Vec<M>>
where
E: Executor<'executor, Database = DB>,
{
let (text, args) = self.into_query_and_args()?;
let z: &str = &text;
util::query_as_with_recast_lifetime::<DB, M>(z, args)
.fetch_all(db)
.await
.map_err(Error::from)
}
pub async fn fetch_one<'executor, E>(mut self, db: E) -> Result<M>
where
E: Executor<'executor, Database = DB>,
{
if self.query.limit.is_none() {
self.query.limit = Some(1);
}
let (text, args) = self.into_query_and_args()?;
let z: &str = &text;
util::query_as_with_recast_lifetime::<DB, M>(z, args)
.fetch_one(db)
.await
.map_err(Error::from)
}
pub async fn fetch_optional<'executor, E>(mut self, db: E) -> Result<Option<M>>
where
E: Executor<'executor, Database = DB>,
{
if self.query.limit.is_none() {
self.query.limit = Some(1);
}
let (text, args) = self.into_query_and_args()?;
let z: &str = &text;
util::query_as_with_recast_lifetime::<DB, M>(z, args)
.fetch_optional(db)
.await
.map_err(Error::from)
}
pub fn with(mut self, name: &str, query: &str) -> Self {
self.query = self.query.with_raw(name, query);
self
}
pub fn select(mut self, column: impl Into<String>) -> Self {
self.query = self.query.select_raw(column.into());
self
}
pub fn where_(mut self, clause: &'static str) -> Self {
self.query = self.query.where_raw(clause);
self
}
pub fn where_bind<T>(mut self, clause: &'static str, value: T) -> Self
where
T: 'args + Send + sqlx::Type<DB> + sqlx::Encode<'args, DB>,
{
self.query = self.query.where_raw(clause);
self.arguments.add(value);
self
}
pub fn dangerous_where(mut self, clause: &str) -> Self {
self.query = self.query.where_raw(clause);
self
}
pub fn join(mut self, join_description: JoinDescription) -> Self {
match &join_description {
JoinDescription::ManyToOne {
columns,
foreign_table,
field,
foreign_key,
local_column,
} => {
let join = sql::query::Join {
typ: sql::query::JoinType::Left,
table: sql::query::JoinTable::Table {
schema: None,
table: foreign_table.to_string(),
},
alias: Some(field.to_string()),
criteria: criteria(M::table_name(), local_column, field, foreign_key),
};
self.query.join.push(join);
self.query.columns.extend(select_columns(columns, field))
}
}
self
}
#[doc(hidden)]
#[deprecated(note = "Please use `where_` instead")]
pub fn filter(self, clause: &'static str) -> Self {
self.where_(clause)
}
pub fn having(mut self, clause: &str) -> Self {
self.query = self.query.having(Where::Expr(Expr::Raw(clause.to_string())));
self
}
pub fn group_by(mut self, clause: &str) -> Self {
self.query = self.query.group_by(clause);
self
}
pub fn order_by(mut self, order: OrderBy) -> Self {
self.query = self.query.order_by(order);
self
}
pub fn order_asc(mut self, clause: &str) -> Self {
self.query = self.query.order_asc(clause);
self
}
pub fn order_desc(mut self, clause: &str) -> Self {
self.query = self.query.order_desc(clause);
self
}
pub fn limit(mut self, limit: usize) -> Self {
self.query = self.query.limit(limit);
self
}
pub fn offset(mut self, offset: usize) -> Self {
self.query = self.query.offset(offset);
self
}
pub fn bind<T>(mut self, value: T) -> Self
where
T: 'args + Send + sqlx::Type<DB> + sqlx::Encode<'args, DB>,
{
self.arguments.add(value);
self
}
pub fn into_query_and_args(mut self) -> Result<(String, QueryBuilderArgs<'args, DB>)> {
let q = self.query.to_sql(DB::dialect());
let args = self.arguments;
let (q, placeholder_count) = util::replace_placeholders(&q, &mut self.placeholder)?;
if placeholder_count != args.len() {
return Err(Error::OrmliteError(format!(
"Failing to build query. {} placeholders were found in the query, but \
{} arguments were provided.",
placeholder_count,
args.len(),
)));
}
Ok((q, args))
}
}
impl<'args, DB: sqlx::Database + DatabaseMetadata, M: Model<DB>> Default for SelectQueryBuilder<'args, DB, M> {
fn default() -> Self {
Self {
query: Select::default().from(M::table_name()),
arguments: QueryBuilderArgs::default(),
model: PhantomData,
placeholder: DB::placeholder(),
}
}
}