use crate::error::{Error, Result};
use crate::query_builder::args::QueryBuilderArgs;
use crate::query_builder::{util, Placeholder};
use crate::model::Model;
use sqlmo::ToSql;
use sqlx::database::HasArguments;
use sqlx::{Executor, IntoArguments};
use std::marker::PhantomData;
use crate::join::JoinDescription;
use sqlmo::{Select, query::Where};
pub use sqlmo::query::Direction;
pub trait DatabaseMetadata {
fn dialect() -> sqlmo::Dialect;
fn placeholder() -> Placeholder;
}
#[cfg(feature = "postgres")]
impl DatabaseMetadata for sqlx::postgres::Postgres {
fn dialect() -> sqlmo::Dialect {
sqlmo::Dialect::Postgres
}
fn placeholder() -> Placeholder {
Placeholder::dollar_sign()
}
}
#[cfg(feature = "sqlite")]
impl DatabaseMetadata for sqlx::sqlite::Sqlite {
fn dialect() -> sqlmo::Dialect {
sqlmo::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>,
gen: 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 as HasArguments<'args>>::Arguments: 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>(self, db: E) -> Result<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_one(db)
.await
.map_err(Error::from)
}
pub async fn fetch_optional<'executor, E>(self, db: E) -> Result<Option<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_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 {
self.query = self.query.join(join_description.to_join_clause(M::table_name()));
self.query.columns.extend(join_description.select_clause());
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::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, clause: &str, direction: Direction) -> Self {
self.query = self.query.order_by(clause, direction);
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.gen)?;
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,
gen: DB::placeholder(),
}
}
}