From 70c4a3a23e96077a184da06cbff21a5b6cd96ce6 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 15 Dec 2022 21:24:10 +0800 Subject: [PATCH 01/11] Select into tuple --- sea-orm-macros/src/derives/active_enum.rs | 5 + src/database/mock.rs | 15 + src/entity/active_enum.rs | 7 + src/executor/query.rs | 452 +++++++++++++++++++++- src/executor/select.rs | 129 +++++- tests/common/features/json_vec.rs | 5 + 6 files changed, 606 insertions(+), 7 deletions(-) diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index 5f2d9a655..aa5fc7d87 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -356,6 +356,11 @@ impl ActiveEnum { let value = <::Value as sea_orm::TryGetable>::try_get(res, pre, col)?; ::try_from_value(&value).map_err(sea_orm::TryGetError::DbErr) } + + fn try_get_by_index(res: &sea_orm::QueryResult, idx: usize) -> std::result::Result { + let value = <::Value as sea_orm::TryGetable>::try_get_by_index(res, idx)?; + ::try_from_value(&value).map_err(sea_orm::TryGetError::DbErr) + } } #[automatically_derived] diff --git a/src/database/mock.rs b/src/database/mock.rs index 4a3640abd..207abd7f9 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -211,6 +211,21 @@ impl MockRow { T::try_from(self.values.get(col).unwrap().clone()).map_err(|e| DbErr::Type(e.to_string())) } + pub fn try_get_by_index(&self, idx: usize) -> Result + where + T: ValueType, + { + let (_, value) = self + .values + .iter() + .nth(idx) + .ok_or(DbErr::Query(RuntimeErr::Internal(format!( + "Column at index {} not found", + idx + ))))?; + T::try_from(value.clone()).map_err(|e| DbErr::Type(e.to_string())) + } + /// An iterator over the keys and values of a mock row pub fn into_column_value_tuples(self) -> impl Iterator { self.values.into_iter() diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index c6a8fefff..6fdf676a1 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -158,6 +158,13 @@ where .map(|value| T::try_from_value(&value).map_err(TryGetError::DbErr)) .collect() } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + ::try_get_by_index(res, idx)? + .into_iter() + .map(|value| T::try_from_value(&value).map_err(TryGetError::DbErr)) + .collect() + } } #[cfg(test)] diff --git a/src/executor/query.rs b/src/executor/query.rs index 025674de8..fb9cec065 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -21,10 +21,13 @@ pub(crate) enum QueryResultRow { Mock(crate::MockRow), } -/// Constrain any type trying to get a Row in a database +/// An interface to get a value from the query result pub trait TryGetable: Sized { - /// Ensure the type implements this method + /// Get a value from the query result with prefixed column name fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; + + /// Get a value from the query result based on the order in the select expressions + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result; } /// An error from trying to get a row from a Model @@ -58,6 +61,13 @@ impl QueryResult { Ok(T::try_get(self, pre, col)?) } + pub fn try_get_by_index(&self, idx: usize) -> Result + where + T: TryGetable, + { + Ok(T::try_get_by_index(self, idx)?) + } + /// Perform query operations on multiple Columns pub fn try_get_many(&self, pre: &str, cols: &[String]) -> Result where @@ -65,6 +75,13 @@ impl QueryResult { { Ok(T::try_get_many(self, pre, cols)?) } + + pub fn try_get_many_by_index(&self) -> Result + where + T: TryGetableMany, + { + Ok(T::try_get_many_by_index(self)?) + } } #[allow(unused_variables)] @@ -95,6 +112,14 @@ impl TryGetable for Option { Err(e) => Err(e), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match T::try_get_by_index(res, idx) { + Ok(v) => Ok(Some(v)), + Err(TryGetError::Null(_)) => Ok(None), + Err(e) => Err(e), + } + } } macro_rules! try_getable_all { @@ -135,6 +160,40 @@ macro_rules! try_getable_all { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + TryGetError::Null(idx.to_string()) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } }; } @@ -174,6 +233,37 @@ macro_rules! try_getable_unsigned { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(_) => { + panic!("{} unsupported by sqlx-postgres", stringify!($type)) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } }; } @@ -210,6 +300,34 @@ macro_rules! try_getable_mysql { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(_) => { + panic!("{} unsupported by sqlx-postgres", stringify!($type)) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(_) => { + panic!("{} unsupported by sqlx-sqlite", stringify!($type)) + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } }; } @@ -257,6 +375,44 @@ macro_rules! try_getable_date_time { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use chrono::{DateTime, Utc}; + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .map(|v| v.into()) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use chrono::{DateTime, Utc}; + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .map(|v| v.into()) + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } }; } @@ -357,6 +513,50 @@ impl TryGetable for Decimal { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use sqlx::Row; + let val: Option = row + .try_get(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; + match val { + Some(v) => Decimal::try_from(v).map_err(|e| { + TryGetError::DbErr(DbErr::TryIntoErr { + from: "f64", + into: "Decimal", + source: Box::new(e), + }) + }), + None => Err(err_null_idx_col(idx)), + } + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } #[cfg(feature = "with-bigdecimal")] @@ -409,6 +609,50 @@ impl TryGetable for BigDecimal { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use sqlx::Row; + let val: Option = row + .try_get(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; + match val { + Some(v) => BigDecimal::try_from(v).map_err(|e| { + TryGetError::DbErr(DbErr::TryIntoErr { + from: "f64", + into: "BigDecimal", + source: Box::new(e), + }) + }), + None => Err(err_null_idx_col(idx)), + } + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } #[cfg(feature = "with-uuid")] @@ -454,6 +698,48 @@ impl TryGetable for u32 { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::postgres::types::Oid; + // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; + // Instead, `u32` was wrapped by a `sqlx::Oid`. + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .map(|oid| oid.0) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use sqlx::Row; + row.try_get::, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } +} + +fn err_null_idx_col(idx: usize) -> TryGetError { + TryGetError::Null(format!("column at index {}", idx)) } #[cfg(feature = "postgres-array")] @@ -469,7 +755,7 @@ mod postgres_array { let column = format!("{}{}", pre, col); match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { + QueryResultRow::SqlxMySql(_) => { panic!("{} unsupported by sqlx-mysql", stringify!($type)) } #[cfg(feature = "sqlx-postgres")] @@ -492,6 +778,33 @@ mod postgres_array { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(_) => { + panic!("{} unsupported by sqlx-mysql", stringify!($type)) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(_) => { + panic!("{} unsupported by sqlx-sqlite", stringify!($type)) + } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } }; } @@ -553,7 +866,7 @@ mod postgres_array { let column = format!("{}{}", pre, col); match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { + QueryResultRow::SqlxMySql(_) => { panic!("{} unsupported by sqlx-mysql", stringify!($type)) } #[cfg(feature = "sqlx-postgres")] @@ -580,16 +893,50 @@ mod postgres_array { _ => unreachable!(), } } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(_) => { + panic!("{} unsupported by sqlx-mysql", stringify!($type)) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::postgres::types::Oid; + // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; + // Instead, `u32` was wrapped by a `sqlx::Oid`. + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .map(|oids| oids.into_iter().map(|oid| oid.0).collect()) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(_) => { + panic!("{} unsupported by sqlx-sqlite", stringify!($type)) + } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } } // TryGetableMany // -/// Perform a query on multiple columns +/// An interface to get a tuple value from the query result pub trait TryGetableMany: Sized { - /// THe method to perform a query on multiple columns + /// Get a tuple value from the query result with prefixed column name fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; + /// Get a tuple value from the query result based on the order in the select expressions + fn try_get_many_by_index(res: &QueryResult) -> Result; + /// ``` /// # use sea_orm::{error::*, tests_cfg::*, *}; /// # @@ -663,6 +1010,10 @@ where try_get_many_with_slice_len_of(1, cols)?; T::try_get(res, pre, &cols[0]) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + T::try_get_by_index(res, 0) + } } impl TryGetableMany for (T,) @@ -672,6 +1023,10 @@ where fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { T::try_get_many(res, pre, cols).map(|r| (r,)) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + T::try_get_many_by_index(res).map(|r| (r,)) + } } impl TryGetableMany for (A, B) @@ -686,6 +1041,10 @@ where B::try_get(res, pre, &cols[1])?, )) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + Ok((A::try_get_by_index(res, 0)?, B::try_get_by_index(res, 1)?)) + } } impl TryGetableMany for (A, B, C) @@ -702,6 +1061,14 @@ where C::try_get(res, pre, &cols[2])?, )) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + Ok(( + A::try_get_by_index(res, 0)?, + B::try_get_by_index(res, 1)?, + C::try_get_by_index(res, 2)?, + )) + } } impl TryGetableMany for (A, B, C, D) @@ -720,6 +1087,15 @@ where D::try_get(res, pre, &cols[3])?, )) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + Ok(( + A::try_get_by_index(res, 0)?, + B::try_get_by_index(res, 1)?, + C::try_get_by_index(res, 2)?, + D::try_get_by_index(res, 3)?, + )) + } } impl TryGetableMany for (A, B, C, D, E) @@ -740,6 +1116,16 @@ where E::try_get(res, pre, &cols[4])?, )) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + Ok(( + A::try_get_by_index(res, 0)?, + B::try_get_by_index(res, 1)?, + C::try_get_by_index(res, 2)?, + D::try_get_by_index(res, 3)?, + E::try_get_by_index(res, 4)?, + )) + } } impl TryGetableMany for (A, B, C, D, E, F) @@ -762,6 +1148,17 @@ where F::try_get(res, pre, &cols[5])?, )) } + + fn try_get_many_by_index(res: &QueryResult) -> Result { + Ok(( + A::try_get_by_index(res, 0)?, + B::try_get_by_index(res, 1)?, + C::try_get_by_index(res, 2)?, + D::try_get_by_index(res, 3)?, + E::try_get_by_index(res, 4)?, + F::try_get_by_index(res, 5)?, + )) + } } fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> { @@ -825,6 +1222,45 @@ where _ => unreachable!(), } } + + fn try_get_from_json_by_index(res: &QueryResult, idx: usize) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) + } + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => { + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => { + use sqlx::Row; + row.try_get::>, _>(idx) + .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) + } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => row + .try_get_by_index::(idx) + .map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }) + .and_then(|json| { + serde_json::from_value(json) + .map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } } #[cfg(feature = "with-json")] @@ -835,6 +1271,10 @@ where fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { T::try_get_from_json(res, pre, col) } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + T::try_get_from_json_by_index(res, idx) + } } // TryFromU64 // diff --git a/src/executor/select.rs b/src/executor/select.rs index a31b2a831..df24e9389 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -52,6 +52,14 @@ where model: PhantomData, } +#[derive(Debug)] +pub struct SelectGetableTuple +where + T: TryGetableMany, +{ + model: PhantomData, +} + /// Defines a type to get a Model #[derive(Debug)] pub struct SelectModel @@ -84,6 +92,17 @@ where } } +impl SelectorTrait for SelectGetableTuple +where + T: TryGetableMany, +{ + type Item = T; + + fn from_raw_query_result(res: QueryResult) -> Result { + T::try_get_many_by_index(&res).map_err(Into::into) + } +} + impl SelectorTrait for SelectModel where M: FromQueryResult + Sized, @@ -253,6 +272,104 @@ where Selector::>::with_columns(self.query) } + /// ``` + /// # use sea_orm::{error::*, tests_cfg::*, *}; + /// # + /// # #[smol_potat::main] + /// # #[cfg(all(feature = "mock", feature = "macros"))] + /// # pub async fn main() -> Result<(), DbErr> { + /// # + /// # let db = MockDatabase::new(DbBackend::Postgres) + /// # .append_query_results(vec![vec![ + /// # maplit::btreemap! { + /// # "cake_name" => Into::::into("Chocolate Forest"), + /// # }, + /// # maplit::btreemap! { + /// # "cake_name" => Into::::into("New York Cheese"), + /// # }, + /// # ]]) + /// # .into_connection(); + /// # + /// use sea_orm::{entity::*, query::*, tests_cfg::cake}; + /// + /// let res: Vec = cake::Entity::find() + /// .select_only() + /// .column(cake::Column::Name) + /// .into_tuple() + /// .all(&db) + /// .await?; + /// + /// assert_eq!( + /// res, + /// vec!["Chocolate Forest".to_owned(), "New York Cheese".to_owned()] + /// ); + /// + /// assert_eq!( + /// db.into_transaction_log(), + /// vec![Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."name" FROM "cake""#, + /// vec![] + /// )] + /// ); + /// # + /// # Ok(()) + /// # } + /// ``` + /// + /// ``` + /// # use sea_orm::{error::*, tests_cfg::*, *}; + /// # + /// # #[smol_potat::main] + /// # #[cfg(all(feature = "mock", feature = "macros"))] + /// # pub async fn main() -> Result<(), DbErr> { + /// # + /// # let db = MockDatabase::new(DbBackend::Postgres) + /// # .append_query_results(vec![vec![ + /// # maplit::btreemap! { + /// # "cake_name" => Into::::into("Chocolate Forest"), + /// # "num_of_cakes" => Into::::into(2i64), + /// # }, + /// # ]]) + /// # .into_connection(); + /// # + /// use sea_orm::{entity::*, query::*, tests_cfg::cake}; + /// + /// let res: Vec<(String, i64)> = cake::Entity::find() + /// .select_only() + /// .column(cake::Column::Name) + /// .column(cake::Column::Id) + /// .group_by(cake::Column::Name) + /// .into_tuple() + /// .all(&db) + /// .await?; + /// + /// assert_eq!(res, vec![("Chocolate Forest".to_owned(), 2i64)]); + /// + /// assert_eq!( + /// db.into_transaction_log(), + /// vec![Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// vec![ + /// r#"SELECT "cake"."name", "cake"."id""#, + /// r#"FROM "cake" GROUP BY "cake"."name""#, + /// ] + /// .join(" ") + /// .as_str(), + /// vec![] + /// )] + /// ); + /// # + /// # Ok(()) + /// # } + /// ``` + pub fn into_tuple(self) -> Selector> + where + T: TryGetableMany, + { + Selector::>::into_tuple(self.query) + } + /// Get one Model from the SELECT query pub async fn one<'a, C>(self, db: &C) -> Result, DbErr> where @@ -402,7 +519,7 @@ impl Selector where S: SelectorTrait, { - /// Create `Selector` from Statment and columns. Executing this `Selector` + /// Create `Selector` from Statement and columns. Executing this `Selector` /// will return a type `T` which implement `TryGetableMany`. pub fn with_columns(query: SelectStatement) -> Selector> where @@ -418,6 +535,16 @@ where } } + pub fn into_tuple(query: SelectStatement) -> Selector> + where + T: TryGetableMany, + { + Selector { + query, + selector: SelectGetableTuple { model: PhantomData }, + } + } + fn into_selector_raw(self, db: &C) -> SelectorRaw where C: ConnectionTrait, diff --git a/tests/common/features/json_vec.rs b/tests/common/features/json_vec.rs index 8e9aca5bd..82cfec5ad 100644 --- a/tests/common/features/json_vec.rs +++ b/tests/common/features/json_vec.rs @@ -29,6 +29,11 @@ impl TryGetable for StringVec { let json_str: String = res.try_get(pre, col).map_err(TryGetError::DbErr)?; serde_json::from_str(&json_str).map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + let json_str: String = res.try_get_by_index(idx).map_err(TryGetError::DbErr)?; + serde_json::from_str(&json_str).map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) + } } impl sea_query::ValueType for StringVec { From 0be16bad16a53821e16555fdf024f86dc672aa9c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 15 Dec 2022 21:24:18 +0800 Subject: [PATCH 02/11] Add tests --- tests/byte_primary_key_tests.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/byte_primary_key_tests.rs b/tests/byte_primary_key_tests.rs index 0ed5e5a61..01b768505 100644 --- a/tests/byte_primary_key_tests.rs +++ b/tests/byte_primary_key_tests.rs @@ -76,5 +76,23 @@ pub async fn create_and_update(db: &DatabaseConnection) -> Result<(), DbErr> { }) ); + assert_eq!( + Entity::find() + .filter(Column::Id.eq(vec![1_u8, 2_u8, 3_u8])) // annotate it as Vec explicitly + .into_values::<_, Column>() + .one(db) + .await?, + Some((vec![1_u8, 2_u8, 3_u8], "First Row (Updated)".to_owned(),)) + ); + + assert_eq!( + Entity::find() + .filter(Column::Id.eq(vec![1_u8, 2_u8, 3_u8])) // annotate it as Vec explicitly + .into_tuple() + .one(db) + .await?, + Some((vec![1_u8, 2_u8, 3_u8], "First Row (Updated)".to_owned(),)) + ); + Ok(()) } From 3cd834d40cfa6f125a5be60edbbb706dca3d61cb Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 20 Dec 2022 15:43:03 +0800 Subject: [PATCH 03/11] docs --- src/database/mock.rs | 3 ++- src/executor/query.rs | 11 +++++++---- src/executor/select.rs | 4 +++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/database/mock.rs b/src/database/mock.rs index 207abd7f9..da12e858a 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -203,7 +203,7 @@ impl MockDatabaseTrait for MockDatabase { } impl MockRow { - /// Try to get the values of a [MockRow] and fail gracefully on error + /// Get a value from the [MockRow] pub fn try_get(&self, col: &str) -> Result where T: ValueType, @@ -211,6 +211,7 @@ impl MockRow { T::try_from(self.values.get(col).unwrap().clone()).map_err(|e| DbErr::Type(e.to_string())) } + /// Get a value from the [MockRow] based on the order of column name pub fn try_get_by_index(&self, idx: usize) -> Result where T: ValueType, diff --git a/src/executor/query.rs b/src/executor/query.rs index fb9cec065..5806d2035 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -53,7 +53,7 @@ impl From for DbErr { // QueryResult // impl QueryResult { - /// Get a Row from a Column + /// Get a value from the query result with prefixed column name pub fn try_get(&self, pre: &str, col: &str) -> Result where T: TryGetable, @@ -61,6 +61,7 @@ impl QueryResult { Ok(T::try_get(self, pre, col)?) } + /// Get a value from the query result based on the order in the select expressions pub fn try_get_by_index(&self, idx: usize) -> Result where T: TryGetable, @@ -68,7 +69,7 @@ impl QueryResult { Ok(T::try_get_by_index(self, idx)?) } - /// Perform query operations on multiple Columns + /// Get a tuple value from the query result with prefixed column name pub fn try_get_many(&self, pre: &str, cols: &[String]) -> Result where T: TryGetableMany, @@ -76,6 +77,7 @@ impl QueryResult { Ok(T::try_get_many(self, pre, cols)?) } + /// Get a tuple value from the query result based on the order in the select expressions pub fn try_get_many_by_index(&self) -> Result where T: TryGetableMany, @@ -1175,13 +1177,13 @@ fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), Try // TryGetableFromJson // -/// Perform a query on multiple columns +/// An interface to get a JSON from the query result #[cfg(feature = "with-json")] pub trait TryGetableFromJson: Sized where for<'de> Self: serde::Deserialize<'de>, { - /// Ensure the type implements this method + /// Get a JSON from the query result with prefixed column name #[allow(unused_variables, unreachable_code)] fn try_get_from_json(res: &QueryResult, pre: &str, col: &str) -> Result { let column = format!("{}{}", pre, col); @@ -1223,6 +1225,7 @@ where } } + /// Get a JSON from the query result based on the order in the select expressions fn try_get_from_json_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] diff --git a/src/executor/select.rs b/src/executor/select.rs index df24e9389..bcb5cb22b 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -41,7 +41,7 @@ pub trait SelectorTrait { fn from_raw_query_result(res: QueryResult) -> Result; } -/// Perform an operation on an entity that can yield a Value +/// Get tuple from query result based on a list of column identifiers #[derive(Debug)] pub struct SelectGetableValue where @@ -52,6 +52,7 @@ where model: PhantomData, } +/// Get tuple from query result based on column index #[derive(Debug)] pub struct SelectGetableTuple where @@ -535,6 +536,7 @@ where } } + /// Get tuple from query result based on column index pub fn into_tuple(query: SelectStatement) -> Selector> where T: TryGetableMany, From 5291c7f551a0e8313b384a64fdaafcb70ddb124b Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 20 Dec 2022 15:56:37 +0800 Subject: [PATCH 04/11] clippy --- src/executor/query.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/executor/query.rs b/src/executor/query.rs index 5806d2035..ba682c327 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -163,6 +163,7 @@ macro_rules! try_getable_all { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -236,6 +237,7 @@ macro_rules! try_getable_unsigned { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -303,6 +305,7 @@ macro_rules! try_getable_mysql { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -378,6 +381,7 @@ macro_rules! try_getable_date_time { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -516,6 +520,7 @@ impl TryGetable for Decimal { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -612,6 +617,7 @@ impl TryGetable for BigDecimal { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -701,6 +707,7 @@ impl TryGetable for u32 { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -740,6 +747,7 @@ impl TryGetable for u32 { } } +#[allow(dead_code)] fn err_null_idx_col(idx: usize) -> TryGetError { TryGetError::Null(format!("column at index {}", idx)) } @@ -781,6 +789,7 @@ mod postgres_array { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -896,6 +905,7 @@ mod postgres_array { } } + #[allow(unused_variables)] fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -1226,6 +1236,7 @@ where } /// Get a JSON from the query result based on the order in the select expressions + #[allow(unused_variables)] fn try_get_from_json_by_index(res: &QueryResult, idx: usize) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] From 1135c117c3ed08b65569ce9f3160194d92040fb5 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 20 Dec 2022 15:56:42 +0800 Subject: [PATCH 05/11] fixup --- tests/common/features/event_trigger.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/common/features/event_trigger.rs b/tests/common/features/event_trigger.rs index cea0a8063..34b034625 100644 --- a/tests/common/features/event_trigger.rs +++ b/tests/common/features/event_trigger.rs @@ -43,6 +43,11 @@ impl TryGetable for Events { let vec: Vec = res.try_get(pre, col).map_err(TryGetError::DbErr)?; Ok(Events(vec.into_iter().map(Event).collect())) } + + fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + let vec: Vec = res.try_get_by_index(idx).map_err(TryGetError::DbErr)?; + Ok(Events(vec.into_iter().map(Event).collect())) + } } impl ValueType for Events { From a11c57d337593e61c98090eb2af6fa021848662e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 9 Jan 2023 14:02:23 +0800 Subject: [PATCH 06/11] Introduce ColIdx --- src/database/mock.rs | 35 ++- src/entity/active_enum.rs | 11 +- src/executor/query.rs | 584 +++++++++----------------------------- 3 files changed, 160 insertions(+), 470 deletions(-) diff --git a/src/database/mock.rs b/src/database/mock.rs index da12e858a..744fa2346 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -204,27 +204,26 @@ impl MockDatabaseTrait for MockDatabase { impl MockRow { /// Get a value from the [MockRow] - pub fn try_get(&self, col: &str) -> Result + pub fn try_get_by(&self, index: I) -> Result where T: ValueType, { - T::try_from(self.values.get(col).unwrap().clone()).map_err(|e| DbErr::Type(e.to_string())) - } - - /// Get a value from the [MockRow] based on the order of column name - pub fn try_get_by_index(&self, idx: usize) -> Result - where - T: ValueType, - { - let (_, value) = self - .values - .iter() - .nth(idx) - .ok_or(DbErr::Query(RuntimeErr::Internal(format!( - "Column at index {} not found", - idx - ))))?; - T::try_from(value.clone()).map_err(|e| DbErr::Type(e.to_string())) + if let Some(index) = index.as_str() { + T::try_from(self.values.get(index).unwrap().clone()) + .map_err(|e| DbErr::Type(e.to_string())) + } else if let Some(index) = index.as_usize() { + let (_, value) = + self.values + .iter() + .nth(*index) + .ok_or(DbErr::Query(RuntimeErr::Internal(format!( + "Column at index {} not found", + index + ))))?; + T::try_from(value.clone()).map_err(|e| DbErr::Type(e.to_string())) + } else { + unreachable!("Missing ColIdx implementation for MockRow"); + } } /// An iterator over the keys and values of a mock row diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 6fdf676a1..229f6197c 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -152,15 +152,8 @@ where T: ActiveEnum, T::ValueVec: TryGetable, { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - ::try_get(res, pre, col)? - .into_iter() - .map(|value| T::try_from_value(&value).map_err(TryGetError::DbErr)) - .collect() - } - - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - ::try_get_by_index(res, idx)? + fn try_get_by(res: &QueryResult, index: I) -> Result { + ::try_get_by(res, index)? .into_iter() .map(|value| T::try_from_value(&value).map_err(TryGetError::DbErr)) .collect() diff --git a/src/executor/query.rs b/src/executor/query.rs index ba682c327..479dfbb13 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -23,11 +23,19 @@ pub(crate) enum QueryResultRow { /// An interface to get a value from the query result pub trait TryGetable: Sized { + /// Get a value from the query result with an ColIdx + fn try_get_by(res: &QueryResult, index: I) -> Result; + /// Get a value from the query result with prefixed column name - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { + let index = format!("{}{}", pre, col); + Self::try_get_by(res, index.as_str()) + } /// Get a value from the query result based on the order in the select expressions - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result; + fn try_get_by_index(res: &QueryResult, index: usize) -> Result { + Self::try_get_by(res, index) + } } /// An error from trying to get a row from a Model @@ -107,20 +115,93 @@ impl fmt::Debug for QueryResultRow { // TryGetable // impl TryGetable for Option { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - match T::try_get(res, pre, col) { + fn try_get_by(res: &QueryResult, index: I) -> Result { + match T::try_get_by(res, index) { Ok(v) => Ok(Some(v)), Err(TryGetError::Null(_)) => Ok(None), Err(e) => Err(e), } } +} - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - match T::try_get_by_index(res, idx) { - Ok(v) => Ok(Some(v)), - Err(TryGetError::Null(_)) => Ok(None), - Err(e) => Err(e), - } +pub trait ColIdx: std::fmt::Debug { + #[cfg(feature = "sqlx-mysql")] + type SqlxMySqlIndex: sqlx::ColumnIndex; + #[cfg(feature = "sqlx-postgres")] + type SqlxPostgresIndex: sqlx::ColumnIndex; + #[cfg(feature = "sqlx-sqlite")] + type SqlxSqliteIndex: sqlx::ColumnIndex; + + #[cfg(feature = "sqlx-mysql")] + fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex; + #[cfg(feature = "sqlx-postgres")] + fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex; + #[cfg(feature = "sqlx-sqlite")] + fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex; + + fn as_str(&self) -> Option<&str>; + fn as_usize(&self) -> Option<&usize>; +} + +impl ColIdx for &str { + #[cfg(feature = "sqlx-mysql")] + type SqlxMySqlIndex = Self; + #[cfg(feature = "sqlx-postgres")] + type SqlxPostgresIndex = Self; + #[cfg(feature = "sqlx-sqlite")] + type SqlxSqliteIndex = Self; + + #[cfg(feature = "sqlx-mysql")] + fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex { + self + } + #[cfg(feature = "sqlx-postgres")] + fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex { + self + } + #[cfg(feature = "sqlx-sqlite")] + fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex { + self + } + + #[inline] + fn as_str(&self) -> Option<&str> { + Some(self) + } + #[inline] + fn as_usize(&self) -> Option<&usize> { + None + } +} + +impl ColIdx for usize { + #[cfg(feature = "sqlx-mysql")] + type SqlxMySqlIndex = Self; + #[cfg(feature = "sqlx-postgres")] + type SqlxPostgresIndex = Self; + #[cfg(feature = "sqlx-sqlite")] + type SqlxSqliteIndex = Self; + + #[cfg(feature = "sqlx-mysql")] + fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex { + *self + } + #[cfg(feature = "sqlx-postgres")] + fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex { + *self + } + #[cfg(feature = "sqlx-sqlite")] + fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex { + *self + } + + #[inline] + fn as_str(&self) -> Option<&str> { + None + } + #[inline] + fn as_usize(&self) -> Option<&usize> { + Some(self) } } @@ -128,70 +209,34 @@ macro_rules! try_getable_all { ( $type: ty ) => { #[allow(unused_variables)] impl TryGetable for $type { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); - TryGetError::Null(idx.to_string()) + err_null_idx_col(idx) }), #[allow(unreachable_patterns)] _ => unreachable!(), @@ -204,46 +249,12 @@ macro_rules! try_getable_all { macro_rules! try_getable_unsigned { ( $type: ty ) => { impl TryGetable for $type { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - #[allow(unused_variables)] - let column = format!("{}{}", pre, col); + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(_) => { - panic!("{} unsupported by sqlx-postgres", stringify!($type)) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -254,13 +265,13 @@ macro_rules! try_getable_unsigned { #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -275,43 +286,12 @@ macro_rules! try_getable_unsigned { macro_rules! try_getable_mysql { ( $type: ty ) => { impl TryGetable for $type { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - #[allow(unused_variables)] - let column = format!("{}{}", pre, col); - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(_) => { - panic!("{} unsupported by sqlx-postgres", stringify!($type)) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -325,7 +305,7 @@ macro_rules! try_getable_mysql { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -341,54 +321,13 @@ macro_rules! try_getable_mysql { macro_rules! try_getable_date_time { ( $type: ty ) => { impl TryGetable for $type { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - #[allow(unused_variables)] - let column = format!("{}{}", pre, col); + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use chrono::{DateTime, Utc}; use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - .map(|v| v.into()) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use chrono::{DateTime, Utc}; - use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - .map(|v| v.into()) - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use chrono::{DateTime, Utc}; - use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) .map(|v| v.into()) @@ -396,7 +335,7 @@ macro_rules! try_getable_date_time { #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -404,14 +343,14 @@ macro_rules! try_getable_date_time { QueryResultRow::SqlxSqlite(row) => { use chrono::{DateTime, Utc}; use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) .map(|v| v.into()) } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -475,65 +414,19 @@ use rust_decimal::Decimal; #[cfg(feature = "with-rust_decimal")] impl TryGetable for Decimal { #[allow(unused_variables)] - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - let val: Option = row - .try_get(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; - match val { - Some(v) => Decimal::try_from(v).map_err(|e| { - TryGetError::DbErr(DbErr::TryIntoErr { - from: "f64", - into: "Decimal", - source: Box::new(e), - }) - }), - None => Err(TryGetError::Null(column)), - } - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -541,7 +434,7 @@ impl TryGetable for Decimal { QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; let val: Option = row - .try_get(idx) + .try_get((idx.as_sqlx_sqlite_index())) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; match val { Some(v) => Decimal::try_from(v).map_err(|e| { @@ -556,7 +449,7 @@ impl TryGetable for Decimal { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -572,65 +465,19 @@ use bigdecimal::BigDecimal; #[cfg(feature = "with-bigdecimal")] impl TryGetable for BigDecimal { #[allow(unused_variables)] - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - let val: Option = row - .try_get(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; - match val { - Some(v) => BigDecimal::try_from(v).map_err(|e| { - TryGetError::DbErr(DbErr::TryIntoErr { - from: "f64", - into: "BigDecimal", - source: Box::new(e), - }) - }), - None => Err(TryGetError::Null(column)), - } - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>((idx.as_sqlx_mysql_index())) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>((idx.as_sqlx_postgres_index())) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -638,7 +485,7 @@ impl TryGetable for BigDecimal { QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; let val: Option = row - .try_get(idx) + .try_get((idx.as_sqlx_sqlite_index())) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; match val { Some(v) => BigDecimal::try_from(v).map_err(|e| { @@ -653,7 +500,7 @@ impl TryGetable for BigDecimal { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -667,53 +514,12 @@ impl TryGetable for BigDecimal { try_getable_all!(uuid::Uuid); impl TryGetable for u32 { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - #[allow(unused_variables)] - let column = format!("{}{}", pre, col); - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::postgres::types::Oid; - // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; - // Instead, `u32` was wrapped by a `sqlx::Oid`. - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - .map(|oid| oid.0) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "mock")] - #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -723,7 +529,7 @@ impl TryGetable for u32 { // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; // Instead, `u32` was wrapped by a `sqlx::Oid`. use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) .map(|oid| oid.0) @@ -731,13 +537,13 @@ impl TryGetable for u32 { #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - row.try_get::, _>(idx) + row.try_get::, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -748,8 +554,8 @@ impl TryGetable for u32 { } #[allow(dead_code)] -fn err_null_idx_col(idx: usize) -> TryGetError { - TryGetError::Null(format!("column at index {}", idx)) +fn err_null_idx_col(idx: I) -> TryGetError { + TryGetError::Null(format!("{:?}", idx)) } #[cfg(feature = "postgres-array")] @@ -760,37 +566,7 @@ mod postgres_array { macro_rules! try_getable_postgres_array { ( $type: ty ) => { impl TryGetable for Vec<$type> { - #[allow(unused_variables)] - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(_) => { - panic!("{} unsupported by sqlx-mysql", stringify!($type)) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } - #[cfg(feature = "mock")] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(_) => { @@ -799,7 +575,7 @@ mod postgres_array { #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>((idx.as_sqlx_postgres_index())) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -808,7 +584,8 @@ mod postgres_array { panic!("{} unsupported by sqlx-sqlite", stringify!($type)) } #[cfg(feature = "mock")] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -872,9 +649,7 @@ mod postgres_array { try_getable_postgres_array!(uuid::Uuid); impl TryGetable for Vec { - #[allow(unused_variables)] - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(_) => { @@ -886,39 +661,7 @@ mod postgres_array { // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; // Instead, `u32` was wrapped by a `sqlx::Oid`. use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column))) - .map(|oids| oids.into_iter().map(|oid| oid.0).collect()) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } - #[cfg(feature = "mock")] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - #[allow(unused_variables)] - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(_) => { - panic!("{} unsupported by sqlx-mysql", stringify!($type)) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::postgres::types::Oid; - // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; - // Instead, `u32` was wrapped by a `sqlx::Oid`. - use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>((idx.as_sqlx_postgres_index())) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) .map(|oids| oids.into_iter().map(|oid| oid.0).collect()) @@ -928,7 +671,8 @@ mod postgres_array { panic!("{} unsupported by sqlx-sqlite", stringify!($type)) } #[cfg(feature = "mock")] - QueryResultRow::Mock(row) => row.try_get_by_index(idx).map_err(|e| { + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -1195,74 +939,32 @@ where { /// Get a JSON from the query result with prefixed column name #[allow(unused_variables, unreachable_code)] - fn try_get_from_json(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + fn try_get_from_json(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column)).map(|json| json.0)) - } - #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column)).map(|json| json.0)) - } - #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::>, _>(column.as_str()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(TryGetError::Null(column)).map(|json| json.0)) - } - #[cfg(feature = "mock")] - QueryResultRow::Mock(row) => row - .try_get::(column.as_str()) - .map_err(|e| { - debug_print!("{:#?}", e.to_string()); - TryGetError::Null(column) - }) - .and_then(|json| { - serde_json::from_value(json) - .map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) - }), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - /// Get a JSON from the query result based on the order in the select expressions - #[allow(unused_variables)] - fn try_get_from_json_by_index(res: &QueryResult, idx: usize) -> Result { - match &res.row { - #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - row.try_get::>, _>(idx) + row.try_get::>, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row - .try_get_by_index::(idx) + .try_get_by::(idx) .map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) @@ -1282,12 +984,8 @@ impl TryGetable for T where T: TryGetableFromJson, { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - T::try_get_from_json(res, pre, col) - } - - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - T::try_get_from_json_by_index(res, idx) + fn try_get_by(res: &QueryResult, index: I) -> Result { + T::try_get_from_json(res, index) } } From 566dac06611b4fd686ec4b31a778eb757130410a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 9 Jan 2023 14:09:01 +0800 Subject: [PATCH 07/11] Docs --- src/executor/query.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/executor/query.rs b/src/executor/query.rs index 479dfbb13..b42ee2baa 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -124,22 +124,31 @@ impl TryGetable for Option { } } +/// Column Index, used by [`TryGetable`] pub trait ColIdx: std::fmt::Debug { #[cfg(feature = "sqlx-mysql")] + /// Type shenanigans type SqlxMySqlIndex: sqlx::ColumnIndex; #[cfg(feature = "sqlx-postgres")] + /// Type shenanigans type SqlxPostgresIndex: sqlx::ColumnIndex; #[cfg(feature = "sqlx-sqlite")] + /// Type shenanigans type SqlxSqliteIndex: sqlx::ColumnIndex; #[cfg(feature = "sqlx-mysql")] + /// Basically a no-op; only to satisfy a trait bound fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex; #[cfg(feature = "sqlx-postgres")] + /// Basically a no-op; only to satisfy a trait bound fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex; #[cfg(feature = "sqlx-sqlite")] + /// Basically a no-op; only to satisfy a trait bound fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex; + /// Self must be `&str`, return `None` otherwise fn as_str(&self) -> Option<&str>; + /// Self must be `usize`, return `None` otherwise fn as_usize(&self) -> Option<&usize>; } @@ -152,14 +161,17 @@ impl ColIdx for &str { type SqlxSqliteIndex = Self; #[cfg(feature = "sqlx-mysql")] + #[inline] fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex { self } #[cfg(feature = "sqlx-postgres")] + #[inline] fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex { self } #[cfg(feature = "sqlx-sqlite")] + #[inline] fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex { self } @@ -183,14 +195,17 @@ impl ColIdx for usize { type SqlxSqliteIndex = Self; #[cfg(feature = "sqlx-mysql")] + #[inline] fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex { *self } #[cfg(feature = "sqlx-postgres")] + #[inline] fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex { *self } #[cfg(feature = "sqlx-sqlite")] + #[inline] fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex { *self } @@ -434,7 +449,7 @@ impl TryGetable for Decimal { QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; let val: Option = row - .try_get((idx.as_sqlx_sqlite_index())) + .try_get(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; match val { Some(v) => Decimal::try_from(v).map_err(|e| { @@ -470,14 +485,14 @@ impl TryGetable for BigDecimal { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>((idx.as_sqlx_mysql_index())) + row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::, _>((idx.as_sqlx_postgres_index())) + row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -485,7 +500,7 @@ impl TryGetable for BigDecimal { QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; let val: Option = row - .try_get((idx.as_sqlx_sqlite_index())) + .try_get(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; match val { Some(v) => BigDecimal::try_from(v).map_err(|e| { @@ -575,7 +590,7 @@ mod postgres_array { #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::>, _>((idx.as_sqlx_postgres_index())) + row.try_get::>, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) } @@ -661,7 +676,7 @@ mod postgres_array { // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; // Instead, `u32` was wrapped by a `sqlx::Oid`. use sqlx::Row; - row.try_get::>, _>((idx.as_sqlx_postgres_index())) + row.try_get::>, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) .map(|oids| oids.into_iter().map(|oid| oid.0).collect()) From f0634c24514c8a16d761a305be00b1ca39c4968c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 9 Jan 2023 14:26:11 +0800 Subject: [PATCH 08/11] Well clippy --- src/database/transaction.rs | 2 +- src/executor/query.rs | 50 +++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/database/transaction.rs b/src/database/transaction.rs index fa8d57755..549cee3a8 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -275,7 +275,7 @@ impl DatabaseTransaction { if let Err(sqlx::Error::RowNotFound) = err { Ok(None) } else { - err.map_err(|e| sqlx_error_to_query_err(e)) + err.map_err(sqlx_error_to_query_err) } } } diff --git a/src/executor/query.rs b/src/executor/query.rs index b42ee2baa..73b07e65c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -222,8 +222,8 @@ impl ColIdx for usize { macro_rules! try_getable_all { ( $type: ty ) => { - #[allow(unused_variables)] impl TryGetable for $type { + #[allow(unused_variables)] fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -231,21 +231,21 @@ macro_rules! try_getable_all { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "mock")] #[allow(unused_variables)] @@ -264,6 +264,7 @@ macro_rules! try_getable_all { macro_rules! try_getable_unsigned { ( $type: ty ) => { impl TryGetable for $type { + #[allow(unused_variables)] fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -271,7 +272,7 @@ macro_rules! try_getable_unsigned { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(_) => { @@ -282,7 +283,7 @@ macro_rules! try_getable_unsigned { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "mock")] #[allow(unused_variables)] @@ -301,6 +302,7 @@ macro_rules! try_getable_unsigned { macro_rules! try_getable_mysql { ( $type: ty ) => { impl TryGetable for $type { + #[allow(unused_variables)] fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -308,7 +310,7 @@ macro_rules! try_getable_mysql { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(_) => { @@ -336,6 +338,7 @@ macro_rules! try_getable_mysql { macro_rules! try_getable_date_time { ( $type: ty ) => { impl TryGetable for $type { + #[allow(unused_variables)] fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -344,7 +347,7 @@ macro_rules! try_getable_date_time { use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|v| v.into()) } #[cfg(feature = "sqlx-postgres")] @@ -352,7 +355,7 @@ macro_rules! try_getable_date_time { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { @@ -360,7 +363,7 @@ macro_rules! try_getable_date_time { use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|v| v.into()) } #[cfg(feature = "mock")] @@ -436,14 +439,14 @@ impl TryGetable for Decimal { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { @@ -487,14 +490,14 @@ impl TryGetable for BigDecimal { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { @@ -529,6 +532,7 @@ impl TryGetable for BigDecimal { try_getable_all!(uuid::Uuid); impl TryGetable for u32 { + #[allow(unused_variables)] fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -536,7 +540,7 @@ impl TryGetable for u32 { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { @@ -546,7 +550,7 @@ impl TryGetable for u32 { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|oid| oid.0) } #[cfg(feature = "sqlx-sqlite")] @@ -554,7 +558,7 @@ impl TryGetable for u32 { use sqlx::Row; row.try_get::, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "mock")] #[allow(unused_variables)] @@ -580,6 +584,7 @@ mod postgres_array { #[allow(unused_macros)] macro_rules! try_getable_postgres_array { ( $type: ty ) => { + #[allow(unused_variables)] impl TryGetable for Vec<$type> { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { @@ -592,7 +597,7 @@ mod postgres_array { use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(_) => { @@ -664,6 +669,7 @@ mod postgres_array { try_getable_postgres_array!(uuid::Uuid); impl TryGetable for Vec { + #[allow(unused_variables)] fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] @@ -678,7 +684,7 @@ mod postgres_array { use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx))) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|oids| oids.into_iter().map(|oid| oid.0).collect()) } #[cfg(feature = "sqlx-sqlite")] @@ -961,21 +967,21 @@ where use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_mysql_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_postgres_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_sqlite_index()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or(err_null_idx_col(idx)).map(|json| json.0)) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)) } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row From 5c93253877da129a34402aa74a2f4bf2bffda4a8 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 9 Jan 2023 14:46:19 +0800 Subject: [PATCH 09/11] Tests --- sea-orm-macros/src/derives/active_enum.rs | 9 ++------- src/executor/query.rs | 11 ++++++++++- tests/common/features/event_trigger.rs | 9 ++------- tests/common/features/json_vec.rs | 9 ++------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index aa5fc7d87..21f8b7ea4 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -352,13 +352,8 @@ impl ActiveEnum { #[automatically_derived] impl sea_orm::TryGetable for #ident { - fn try_get(res: &sea_orm::QueryResult, pre: &str, col: &str) -> std::result::Result { - let value = <::Value as sea_orm::TryGetable>::try_get(res, pre, col)?; - ::try_from_value(&value).map_err(sea_orm::TryGetError::DbErr) - } - - fn try_get_by_index(res: &sea_orm::QueryResult, idx: usize) -> std::result::Result { - let value = <::Value as sea_orm::TryGetable>::try_get_by_index(res, idx)?; + fn try_get_by(res: &sea_orm::QueryResult, idx: I) -> std::result::Result { + let value = <::Value as sea_orm::TryGetable>::try_get_by(res, idx)?; ::try_from_value(&value).map_err(sea_orm::TryGetError::DbErr) } } diff --git a/src/executor/query.rs b/src/executor/query.rs index 73b07e65c..69c101ea8 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -61,6 +61,15 @@ impl From for DbErr { // QueryResult // impl QueryResult { + /// Get a value from the query result with an ColIdx + pub fn try_get_by(&self, index: I) -> Result + where + T: TryGetable, + I: ColIdx, + { + Ok(T::try_get_by(self, index)?) + } + /// Get a value from the query result with prefixed column name pub fn try_get(&self, pre: &str, col: &str) -> Result where @@ -125,7 +134,7 @@ impl TryGetable for Option { } /// Column Index, used by [`TryGetable`] -pub trait ColIdx: std::fmt::Debug { +pub trait ColIdx: std::fmt::Debug + Copy { #[cfg(feature = "sqlx-mysql")] /// Type shenanigans type SqlxMySqlIndex: sqlx::ColumnIndex; diff --git a/tests/common/features/event_trigger.rs b/tests/common/features/event_trigger.rs index 34b034625..de6895327 100644 --- a/tests/common/features/event_trigger.rs +++ b/tests/common/features/event_trigger.rs @@ -39,13 +39,8 @@ impl From for Value { } impl TryGetable for Events { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let vec: Vec = res.try_get(pre, col).map_err(TryGetError::DbErr)?; - Ok(Events(vec.into_iter().map(Event).collect())) - } - - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - let vec: Vec = res.try_get_by_index(idx).map_err(TryGetError::DbErr)?; + fn try_get_by(res: &QueryResult, idx: I) -> Result { + let vec: Vec = res.try_get_by(idx).map_err(TryGetError::DbErr)?; Ok(Events(vec.into_iter().map(Event).collect())) } } diff --git a/tests/common/features/json_vec.rs b/tests/common/features/json_vec.rs index 82cfec5ad..8e2fdcc60 100644 --- a/tests/common/features/json_vec.rs +++ b/tests/common/features/json_vec.rs @@ -25,13 +25,8 @@ impl From for Value { } impl TryGetable for StringVec { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let json_str: String = res.try_get(pre, col).map_err(TryGetError::DbErr)?; - serde_json::from_str(&json_str).map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) - } - - fn try_get_by_index(res: &QueryResult, idx: usize) -> Result { - let json_str: String = res.try_get_by_index(idx).map_err(TryGetError::DbErr)?; + fn try_get_by(res: &QueryResult, idx: I) -> Result { + let json_str: String = res.try_get_by(idx).map_err(TryGetError::DbErr)?; serde_json::from_str(&json_str).map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) } } From d14e4a2160742db893ea4279923a8cdcb8f97523 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 9 Jan 2023 15:20:01 +0800 Subject: [PATCH 10/11] Fix again --- src/database/mock.rs | 20 +++++++++----------- src/executor/query.rs | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/database/mock.rs b/src/database/mock.rs index 744fa2346..734ed39c0 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -204,7 +204,7 @@ impl MockDatabaseTrait for MockDatabase { impl MockRow { /// Get a value from the [MockRow] - pub fn try_get_by(&self, index: I) -> Result + pub fn try_get(&self, index: I) -> Result where T: ValueType, { @@ -212,14 +212,12 @@ impl MockRow { T::try_from(self.values.get(index).unwrap().clone()) .map_err(|e| DbErr::Type(e.to_string())) } else if let Some(index) = index.as_usize() { - let (_, value) = - self.values - .iter() - .nth(*index) - .ok_or(DbErr::Query(RuntimeErr::Internal(format!( - "Column at index {} not found", - index - ))))?; + let (_, value) = self.values.iter().nth(*index).ok_or_else(|| { + DbErr::Query(RuntimeErr::Internal(format!( + "Column at index {} not found", + index + ))) + })?; T::try_from(value.clone()).map_err(|e| DbErr::Type(e.to_string())) } else { unreachable!("Missing ColIdx implementation for MockRow"); @@ -701,10 +699,10 @@ mod tests { ); let mocked_row = row.into_mock_row(); - let a_id = mocked_row.try_get::("A_id"); + let a_id = mocked_row.try_get::("A_id"); assert!(a_id.is_ok()); assert_eq!(1, a_id.unwrap()); - let b_id = mocked_row.try_get::("B_id"); + let b_id = mocked_row.try_get::("B_id"); assert!(b_id.is_ok()); assert_eq!(2, b_id.unwrap()); } diff --git a/src/executor/query.rs b/src/executor/query.rs index 69c101ea8..bbe585f9c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -258,7 +258,7 @@ macro_rules! try_getable_all { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -296,7 +296,7 @@ macro_rules! try_getable_unsigned { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -331,7 +331,7 @@ macro_rules! try_getable_mysql { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -377,7 +377,7 @@ macro_rules! try_getable_date_time { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -476,7 +476,7 @@ impl TryGetable for Decimal { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -527,7 +527,7 @@ impl TryGetable for BigDecimal { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -571,7 +571,7 @@ impl TryGetable for u32 { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -614,7 +614,7 @@ mod postgres_array { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -702,7 +702,7 @@ mod postgres_array { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get_by(idx).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }), @@ -994,7 +994,7 @@ where } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row - .try_get_by::(idx) + .try_get::(idx) .map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) From 3ae44a0abd36cf897ea991e60212485d862ba2c2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 9 Jan 2023 15:46:41 +0800 Subject: [PATCH 11/11] One more tweak --- src/executor/query.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/executor/query.rs b/src/executor/query.rs index bbe585f9c..c5e20513a 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -257,7 +257,6 @@ macro_rules! try_getable_all { .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "mock")] - #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) @@ -295,7 +294,6 @@ macro_rules! try_getable_unsigned { .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) } #[cfg(feature = "mock")] - #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) @@ -330,7 +328,6 @@ macro_rules! try_getable_mysql { panic!("{} unsupported by sqlx-sqlite", stringify!($type)) } #[cfg(feature = "mock")] - #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) @@ -376,7 +373,6 @@ macro_rules! try_getable_date_time { .map(|v| v.into()) } #[cfg(feature = "mock")] - #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx)