#[cfg(feature = "postgres")]
use sqlx::postgres::{PgArguments, PgRow};
#[cfg(feature = "postgres")]
use sqlx::query::Query;
use super::ExecError;
use crate::core::{SelectQuery, SqlValue};
use crate::sql::Pool;
#[cfg(feature = "postgres")]
use super::{bind_match, bind_query};
#[cfg(feature = "mysql")]
use super::{bind_match_mysql, bind_query_my};
#[cfg(feature = "sqlite")]
use super::{bind_match_sqlite, bind_query_sqlite};
#[cfg(feature = "postgres")]
fn pg_cell_to_sqlvalue(row: &PgRow, i: usize) -> SqlValue {
use sqlx::Row as _;
if let Ok(v) = row.try_get::<i64, _>(i) {
SqlValue::I64(v)
} else if let Ok(v) = row.try_get::<i32, _>(i) {
SqlValue::I32(v)
} else if let Ok(v) = row.try_get::<f64, _>(i) {
SqlValue::F64(v)
} else if let Ok(v) = row.try_get::<bool, _>(i) {
SqlValue::Bool(v)
} else if let Ok(v) = row.try_get::<String, _>(i) {
SqlValue::String(v)
} else if let Ok(v) = row.try_get::<serde_json::Value, _>(i) {
SqlValue::Json(v)
} else {
SqlValue::Null
}
}
#[cfg(feature = "mysql")]
fn my_cell_to_sqlvalue(row: &sqlx::mysql::MySqlRow, i: usize) -> SqlValue {
use sqlx::Row as _;
if let Ok(v) = row.try_get::<i64, _>(i) {
SqlValue::I64(v)
} else if let Ok(v) = row.try_get::<i32, _>(i) {
SqlValue::I32(v)
} else if let Ok(v) = row.try_get::<f64, _>(i) {
SqlValue::F64(v)
} else if let Ok(v) = row.try_get::<bool, _>(i) {
SqlValue::Bool(v)
} else if let Ok(v) = row.try_get::<String, _>(i) {
SqlValue::String(v)
} else {
SqlValue::Null
}
}
#[cfg(feature = "sqlite")]
fn sqlite_cell_to_sqlvalue(row: &sqlx::sqlite::SqliteRow, i: usize) -> SqlValue {
use sqlx::Row as _;
if let Ok(v) = row.try_get::<i64, _>(i) {
SqlValue::I64(v)
} else if let Ok(v) = row.try_get::<i32, _>(i) {
SqlValue::I32(v)
} else if let Ok(v) = row.try_get::<f64, _>(i) {
SqlValue::F64(v)
} else if let Ok(v) = row.try_get::<bool, _>(i) {
SqlValue::Bool(v)
} else if let Ok(v) = row.try_get::<String, _>(i) {
SqlValue::String(v)
} else {
SqlValue::Null
}
}
pub async fn fetch_values_dict(
pool: &Pool,
query: &SelectQuery,
) -> Result<Vec<std::collections::HashMap<String, SqlValue>>, ExecError> {
let stmt = pool.dialect().compile_select(query)?;
match pool {
#[cfg(feature = "postgres")]
Pool::Postgres(pg) => {
let mut q: Query<'_, sqlx::Postgres, PgArguments> = sqlx::query(&stmt.sql);
for v in stmt.params {
q = bind_query(q, v);
}
let rows = q.fetch_all(pg).await?;
let mut out = Vec::with_capacity(rows.len());
for row in &rows {
use sqlx::Column as _;
use sqlx::Row as _;
let mut map = std::collections::HashMap::new();
for (i, col) in row.columns().iter().enumerate() {
map.insert(col.name().to_owned(), pg_cell_to_sqlvalue(row, i));
}
out.push(map);
}
Ok(out)
}
#[cfg(feature = "mysql")]
Pool::Mysql(my) => {
let mut q: sqlx::query::Query<'_, sqlx::MySql, sqlx::mysql::MySqlArguments> =
sqlx::query(&stmt.sql);
for v in stmt.params {
q = bind_query_my(q, v);
}
let rows = q.fetch_all(my).await?;
let mut out = Vec::with_capacity(rows.len());
for row in &rows {
use sqlx::Column as _;
use sqlx::Row as _;
let mut map = std::collections::HashMap::new();
for (i, col) in row.columns().iter().enumerate() {
map.insert(col.name().to_owned(), my_cell_to_sqlvalue(row, i));
}
out.push(map);
}
Ok(out)
}
#[cfg(feature = "sqlite")]
Pool::Sqlite(sq) => {
let mut q: sqlx::query::Query<'_, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'_>> =
sqlx::query(&stmt.sql);
for v in stmt.params {
q = bind_query_sqlite(q, v);
}
let rows = q.fetch_all(sq).await?;
let mut out = Vec::with_capacity(rows.len());
for row in &rows {
use sqlx::Column as _;
use sqlx::Row as _;
let mut map = std::collections::HashMap::new();
for (i, col) in row.columns().iter().enumerate() {
map.insert(col.name().to_owned(), sqlite_cell_to_sqlvalue(row, i));
}
out.push(map);
}
Ok(out)
}
}
}
pub async fn fetch_values_list(
pool: &Pool,
query: &SelectQuery,
) -> Result<Vec<Vec<SqlValue>>, ExecError> {
let stmt = pool.dialect().compile_select(query)?;
match pool {
#[cfg(feature = "postgres")]
Pool::Postgres(pg) => {
let mut q: Query<'_, sqlx::Postgres, PgArguments> = sqlx::query(&stmt.sql);
for v in stmt.params {
q = bind_query(q, v);
}
let rows = q.fetch_all(pg).await?;
let mut out = Vec::with_capacity(rows.len());
for row in &rows {
use sqlx::Row as _;
let n = row.columns().len();
let mut v = Vec::with_capacity(n);
for i in 0..n {
v.push(pg_cell_to_sqlvalue(row, i));
}
out.push(v);
}
Ok(out)
}
#[cfg(feature = "mysql")]
Pool::Mysql(my) => {
let mut q: sqlx::query::Query<'_, sqlx::MySql, sqlx::mysql::MySqlArguments> =
sqlx::query(&stmt.sql);
for v in stmt.params {
q = bind_query_my(q, v);
}
let rows = q.fetch_all(my).await?;
let mut out = Vec::with_capacity(rows.len());
for row in &rows {
use sqlx::Row as _;
let n = row.columns().len();
let mut v = Vec::with_capacity(n);
for i in 0..n {
v.push(my_cell_to_sqlvalue(row, i));
}
out.push(v);
}
Ok(out)
}
#[cfg(feature = "sqlite")]
Pool::Sqlite(sq) => {
let mut q: sqlx::query::Query<'_, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'_>> =
sqlx::query(&stmt.sql);
for v in stmt.params {
q = bind_query_sqlite(q, v);
}
let rows = q.fetch_all(sq).await?;
let mut out = Vec::with_capacity(rows.len());
for row in &rows {
use sqlx::Row as _;
let n = row.columns().len();
let mut v = Vec::with_capacity(n);
for i in 0..n {
v.push(sqlite_cell_to_sqlvalue(row, i));
}
out.push(v);
}
Ok(out)
}
}
}
#[cfg(feature = "postgres")]
pub trait MaybePgScalar:
for<'r> sqlx::Decode<'r, sqlx::Postgres> + sqlx::Type<sqlx::Postgres>
{
}
#[cfg(feature = "postgres")]
impl<T> MaybePgScalar for T where
T: for<'r> sqlx::Decode<'r, sqlx::Postgres> + sqlx::Type<sqlx::Postgres>
{
}
#[cfg(not(feature = "postgres"))]
pub trait MaybePgScalar {}
#[cfg(not(feature = "postgres"))]
impl<T> MaybePgScalar for T {}
#[cfg(feature = "mysql")]
pub trait MaybeMyScalar: for<'r> sqlx::Decode<'r, sqlx::MySql> + sqlx::Type<sqlx::MySql> {}
#[cfg(feature = "mysql")]
impl<T> MaybeMyScalar for T where T: for<'r> sqlx::Decode<'r, sqlx::MySql> + sqlx::Type<sqlx::MySql> {}
#[cfg(not(feature = "mysql"))]
pub trait MaybeMyScalar {}
#[cfg(not(feature = "mysql"))]
impl<T> MaybeMyScalar for T {}
#[cfg(feature = "sqlite")]
pub trait MaybeSqliteScalar:
for<'r> sqlx::Decode<'r, sqlx::Sqlite> + sqlx::Type<sqlx::Sqlite>
{
}
#[cfg(feature = "sqlite")]
impl<T> MaybeSqliteScalar for T where
T: for<'r> sqlx::Decode<'r, sqlx::Sqlite> + sqlx::Type<sqlx::Sqlite>
{
}
#[cfg(not(feature = "sqlite"))]
pub trait MaybeSqliteScalar {}
#[cfg(not(feature = "sqlite"))]
impl<T> MaybeSqliteScalar for T {}
pub async fn fetch_values_flat<U>(pool: &Pool, query: &SelectQuery) -> Result<Vec<U>, ExecError>
where
U: MaybePgScalar + MaybeMyScalar + MaybeSqliteScalar + Send + Unpin,
{
let stmt = pool.dialect().compile_select(query)?;
match pool {
#[cfg(feature = "postgres")]
Pool::Postgres(pg) => {
let mut q: sqlx::query::QueryScalar<'_, sqlx::Postgres, U, PgArguments> =
sqlx::query_scalar(&stmt.sql);
for v in stmt.params {
q = bind_query_scalar_pg(q, v);
}
Ok(q.fetch_all(pg).await?)
}
#[cfg(feature = "mysql")]
Pool::Mysql(my) => {
let mut q: sqlx::query::QueryScalar<'_, sqlx::MySql, U, sqlx::mysql::MySqlArguments> =
sqlx::query_scalar(&stmt.sql);
for v in stmt.params {
q = bind_query_scalar_my(q, v);
}
Ok(q.fetch_all(my).await?)
}
#[cfg(feature = "sqlite")]
Pool::Sqlite(sq) => {
let mut q: sqlx::query::QueryScalar<
'_,
sqlx::Sqlite,
U,
sqlx::sqlite::SqliteArguments<'_>,
> = sqlx::query_scalar(&stmt.sql);
for v in stmt.params {
q = bind_query_scalar_sqlite(q, v);
}
Ok(q.fetch_all(sq).await?)
}
}
}
#[cfg(feature = "postgres")]
fn bind_query_scalar_pg<U>(
q: sqlx::query::QueryScalar<'_, sqlx::Postgres, U, PgArguments>,
value: SqlValue,
) -> sqlx::query::QueryScalar<'_, sqlx::Postgres, U, PgArguments> {
bind_match!(q, value)
}
#[cfg(feature = "mysql")]
fn bind_query_scalar_my<U>(
q: sqlx::query::QueryScalar<'_, sqlx::MySql, U, sqlx::mysql::MySqlArguments>,
value: SqlValue,
) -> sqlx::query::QueryScalar<'_, sqlx::MySql, U, sqlx::mysql::MySqlArguments> {
bind_match_mysql!(q, value)
}
#[cfg(feature = "sqlite")]
fn bind_query_scalar_sqlite<'a, U>(
q: sqlx::query::QueryScalar<'a, sqlx::Sqlite, U, sqlx::sqlite::SqliteArguments<'a>>,
value: SqlValue,
) -> sqlx::query::QueryScalar<'a, sqlx::Sqlite, U, sqlx::sqlite::SqliteArguments<'a>> {
bind_match_sqlite!(q, value)
}
impl<T: crate::core::Model> crate::query::ValuesQuerySet<T> {
pub async fn fetch(
self,
pool: &Pool,
) -> Result<Vec<std::collections::HashMap<String, SqlValue>>, ExecError> {
let q = self.compile()?;
fetch_values_dict(pool, &q).await
}
}
impl<T: crate::core::Model> crate::query::ValuesListQuerySet<T> {
pub async fn fetch(self, pool: &Pool) -> Result<Vec<Vec<SqlValue>>, ExecError> {
let q = self.compile()?;
fetch_values_list(pool, &q).await
}
}
impl<T: crate::core::Model> crate::query::ValuesFlatQuerySet<T> {
pub async fn fetch<U>(self, pool: &Pool) -> Result<Vec<U>, ExecError>
where
U: MaybePgScalar + MaybeMyScalar + MaybeSqliteScalar + Send + Unpin,
{
let q = self.compile()?;
fetch_values_flat::<U>(pool, &q).await
}
}