qians_xql 0.2.9

SQL query builder
Documentation
#[cfg(feature = "sqlx")]
use std::{future::Future, pin::Pin};

#[cfg(feature = "sqlx")]
use sqlx::{Database, Executor, FromRow};

#[cfg(feature = "mysql")]
use sqlx::MySql;
#[cfg(feature = "postgres")]
use sqlx::Postgres;
#[cfg(feature = "sqlite")]
use sqlx::Sqlite;

#[cfg(feature = "sqlx")]
use crate::{build::Dialect, build::ToSql, exec::bind::Bind, stmt::Stmt, value::Value};

pub mod bind;

#[cfg(any(feature = "postgres", feature = "mysql", feature = "sqlite"))]
fn quote(buff: &mut String, val: &str, q: char) {
    buff.push(q);
    for ch in val.chars() {
        if ch == q {
            buff.push(q);
        }
        buff.push(ch);
    }
    buff.push(q);
}

#[cfg(feature = "postgres")]
impl Dialect for Postgres {
    fn quote_literal(val: &str, buff: &mut String) {
        quote(buff, val, '\'')
    }

    fn quote_ident(val: &str, buff: &mut String) {
        quote(buff, val, '"')
    }

    fn bind_param<'a>(n: usize, val: Value<'a>, buff: &mut String) -> Value<'a> {
        buff.push('$');
        buff.push_str(n.to_string().as_str());
        val
    }
}

#[cfg(feature = "mysql")]
impl Dialect for MySql {
    fn quote_literal(val: &str, buff: &mut String) {
        quote(buff, val, '\'')
    }

    fn quote_ident(val: &str, buff: &mut String) {
        quote(buff, val, '`')
    }

    fn bind_param<'a>(_: usize, val: Value<'a>, buff: &mut String) -> Value<'a> {
        buff.push('?');
        val
    }
}

#[cfg(feature = "sqlite")]
impl Dialect for Sqlite {
    fn quote_literal(val: &str, buff: &mut String) {
        quote(buff, val, '\'')
    }

    fn quote_ident(val: &str, buff: &mut String) {
        quote(buff, val, '"')
    }

    fn bind_param<'a>(_: usize, val: Value<'a>, buff: &mut String) -> Value<'a> {
        buff.push('?');
        val
    }
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub trait Backend: Database + Bind {
    fn fetch_one<'a, 'v: 'a, 'c, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Self::Row, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>;

    #[allow(clippy::type_complexity)]
    fn fetch_optional<'a, 'v: 'a, 'c, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Option<Self::Row>, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>;

    #[allow(clippy::type_complexity)]
    fn fetch_all<'a, 'v: 'a, 'c, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Vec<Self::Row>, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>;

    fn fetch_one_as<'a, 'v: 'a, 'c, O, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<O, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>,
        O: Send + Unpin + for<'r> FromRow<'r, Self::Row>;

    fn fetch_optional_as<'a, 'v: 'a, 'c, O, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Option<O>, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>,
        O: Send + Unpin + for<'r> FromRow<'r, Self::Row>;

    fn fetch_all_as<'a, 'v: 'a, 'c, O, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Vec<O>, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>,
        O: Send + Unpin + for<'r> FromRow<'r, Self::Row>;

    fn fetch_one_scalar<'a, 'v: 'a, 'c, O, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<O, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>,
        O: Send + Unpin,
        (O,): Send + Unpin + for<'r> FromRow<'r, Self::Row>;

    fn fetch_optional_scalar<'a, 'v: 'a, 'c, O, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Option<O>, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>,
        O: Send + Unpin,
        (O,): Send + Unpin + for<'r> FromRow<'r, Self::Row>;

    fn fetch_all_scalar<'a, 'v: 'a, 'c, O, E>(
        executor: E,
        query: String,
        args: Vec<Value<'v>>,
    ) -> Pin<Box<dyn 'a + Future<Output = Result<Vec<O>, sqlx::Error>> + Send>>
    where
        E: 'a + Executor<'c, Database = Self>,
        O: Send + Unpin,
        (O,): Send + Unpin + for<'r> FromRow<'r, Self::Row>;
}

#[cfg(any(feature = "postgres", feature = "mysql", feature = "sqlite"))]
macro_rules! gen_backend_methods {
    () => {
        fn fetch_one<'a, 'v: 'a, 'c, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Self::Row, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query(query.as_str()), Self::bind)?
                    .fetch_one(executor)
                    .await
            })
        }

        fn fetch_optional<'a, 'v: 'a, 'c, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Option<Self::Row>, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query(query.as_str()), Self::bind)?
                    .fetch_optional(executor)
                    .await
            })
        }

        fn fetch_all<'a, 'v: 'a, 'c, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Vec<Self::Row>, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query(query.as_str()), Self::bind)?
                    .fetch_all(executor)
                    .await
            })
        }

        fn fetch_one_as<'a, 'v: 'a, 'c, O, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<O, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
            O: Send + Unpin + for<'r> FromRow<'r, Self::Row>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query_as(query.as_str()), Self::bind)?
                    .fetch_one(executor)
                    .await
            })
        }

        fn fetch_optional_as<'a, 'v: 'a, 'c, O, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Option<O>, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
            O: Send + Unpin + for<'r> FromRow<'r, Self::Row>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query_as(query.as_str()), Self::bind)?
                    .fetch_optional(executor)
                    .await
            })
        }

        fn fetch_all_as<'a, 'v: 'a, 'c, O, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Vec<O>, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
            O: Send + Unpin + for<'r> FromRow<'r, Self::Row>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query_as(query.as_str()), Self::bind)?
                    .fetch_all(executor)
                    .await
            })
        }

        fn fetch_one_scalar<'a, 'v: 'a, 'c, O, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<O, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
            O: Send + Unpin,
            (O,): Send + Unpin + for<'r> FromRow<'r, Self::Row>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query_scalar(query.as_str()), Self::bind)?
                    .fetch_one(executor)
                    .await
            })
        }

        fn fetch_optional_scalar<'a, 'v: 'a, 'c, O, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Option<O>, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
            O: Send + Unpin,
            (O,): Send + Unpin + for<'r> FromRow<'r, Self::Row>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query_scalar(query.as_str()), Self::bind)?
                    .fetch_optional(executor)
                    .await
            })
        }

        fn fetch_all_scalar<'a, 'v: 'a, 'c, O, E>(
            executor: E,
            query: String,
            args: Vec<Value<'v>>,
        ) -> Pin<Box<dyn 'a + Future<Output = Result<Vec<O>, sqlx::Error>> + Send>>
        where
            E: 'a + Executor<'c, Database = Self>,
            O: Send + Unpin,
            (O,): Send + Unpin + for<'r> FromRow<'r, Self::Row>,
        {
            Box::pin(async move {
                args.into_iter()
                    .try_fold(sqlx::query_scalar(query.as_str()), Self::bind)?
                    .fetch_all(executor)
                    .await
            })
        }
    };
}

#[cfg(feature = "postgres")]
#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))]
impl Backend for Postgres {
    gen_backend_methods!();
}

#[cfg(feature = "mysql")]
#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))]
impl Backend for MySql {
    gen_backend_methods!();
}

#[cfg(feature = "sqlite")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
impl Backend for Sqlite {
    gen_backend_methods!();
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_one<'c, 'v, DB, E, S>(stmt: S, executor: E) -> Result<DB::Row, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    DB: Backend + Dialect,
    E: Executor<'c, Database = DB>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_one(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_optional<'c, 'v, DB, E, S>(
    stmt: S,
    executor: E,
) -> Result<Option<DB::Row>, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    DB: Backend + Dialect,
    E: Executor<'c, Database = DB>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_optional(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_all<'c, 'v, DB, E, S>(stmt: S, executor: E) -> Result<Vec<DB::Row>, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    DB: Backend + Dialect,
    E: Executor<'c, Database = DB>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_all(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_one_as<'c, 'v, O, E, S>(stmt: S, executor: E) -> Result<O, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    E: Executor<'c>,
    E::Database: Backend + Dialect,
    O: Send + Unpin + for<'r> FromRow<'r, <E::Database as Database>::Row>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_one_as(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_optional_as<'c, 'v, O, E, S>(
    stmt: S,
    executor: E,
) -> Result<Option<O>, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    E: Executor<'c>,
    E::Database: Backend + Dialect,
    O: Send + Unpin + for<'r> FromRow<'r, <E::Database as Database>::Row>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_optional_as(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_all_as<'c, 'v, O, E, S>(stmt: S, executor: E) -> Result<Vec<O>, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    E: Executor<'c>,
    E::Database: Backend + Dialect,
    O: Send + Unpin + for<'r> FromRow<'r, <E::Database as Database>::Row>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_all_as(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_one_scalar<'c, 'v, O, E, S>(stmt: S, executor: E) -> Result<O, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    E: Executor<'c>,
    E::Database: Backend + Dialect,
    O: Send + Unpin,
    (O,): for<'r> FromRow<'r, <E::Database as Database>::Row>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_one_scalar(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_optional_scalar<'c, 'v, O, E, S>(
    stmt: S,
    executor: E,
) -> Result<Option<O>, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    E: Executor<'c>,
    E::Database: Backend + Dialect,
    O: Send + Unpin,
    (O,): for<'r> FromRow<'r, <E::Database as Database>::Row>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_optional_scalar(executor, sql, args).await
}

#[cfg(feature = "sqlx")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqlx")))]
pub async fn fetch_all_scalar<'c, 'v, O, E, S>(stmt: S, executor: E) -> Result<Vec<O>, sqlx::Error>
where
    S: Into<Stmt<'v>>,
    E: Executor<'c>,
    E::Database: Backend + Dialect,
    O: Send + Unpin,
    (O,): for<'r> FromRow<'r, <E::Database as Database>::Row>,
{
    let (sql, args) = stmt.into().to_sql::<E::Database>();
    E::Database::fetch_all_scalar(executor, sql, args).await
}