commit
0deedddf28
10
.github/workflows/rust.yml
vendored
10
.github/workflows/rust.yml
vendored
@ -288,6 +288,7 @@ jobs:
|
||||
name: Examples
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
path: [basic, actix_example, actix4_example, axum_example, rocket_example]
|
||||
@ -312,6 +313,7 @@ jobs:
|
||||
if: ${{ (needs.init.outputs.run-partial == 'true' && needs.init.outputs.run-issues == 'true') }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
path: [86, 249, 262, 319, 324]
|
||||
@ -350,6 +352,7 @@ jobs:
|
||||
env:
|
||||
DATABASE_URL: "sqlite::memory:"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runtime: [async-std, actix, tokio]
|
||||
tls: [native-tls, rustls]
|
||||
@ -392,6 +395,7 @@ jobs:
|
||||
env:
|
||||
DATABASE_URL: "mysql://root:@localhost"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [8.0, 5.7]
|
||||
runtime: [async-std, actix, tokio]
|
||||
@ -452,8 +456,9 @@ jobs:
|
||||
env:
|
||||
DATABASE_URL: "mysql://root:@localhost"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [10.6]
|
||||
version: [10.6, 10.5, 10.4]
|
||||
runtime: [async-std, actix, tokio]
|
||||
tls: [native-tls]
|
||||
services:
|
||||
@ -512,8 +517,9 @@ jobs:
|
||||
env:
|
||||
DATABASE_URL: "postgres://root:root@localhost"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [13.3, 12.7, 11.12, 10.17, 9.6.22]
|
||||
version: [13, 12, 11, 10, 9]
|
||||
runtime: [tokio]
|
||||
tls: [native-tls]
|
||||
services:
|
||||
|
@ -45,6 +45,12 @@ pub trait ConnectionTrait<'a>: Sync {
|
||||
T: Send,
|
||||
E: std::error::Error + Send;
|
||||
|
||||
/// Check if the connection supports `RETURNING` syntax on insert and update
|
||||
fn support_returning(&self) -> bool {
|
||||
let db_backend = self.get_database_backend();
|
||||
db_backend.support_returning()
|
||||
}
|
||||
|
||||
/// Check if the connection is a test connection for the Mock database
|
||||
fn is_mock_connection(&self) -> bool {
|
||||
false
|
||||
|
@ -267,6 +267,11 @@ impl DbBackend {
|
||||
Self::Sqlite => Box::new(SqliteQueryBuilder),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the database supports `RETURNING` syntax on insert and update
|
||||
pub fn support_returning(&self) -> bool {
|
||||
matches!(self, Self::Postgres)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
11
src/docs.rs
11
src/docs.rs
@ -3,7 +3,12 @@
|
||||
//! Relying on [SQLx](https://github.com/launchbadge/sqlx), SeaORM is a new library with async support from day 1.
|
||||
//!
|
||||
//! ```
|
||||
//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*, DatabaseConnection, DbBackend, MockDatabase, Transaction, IntoMockRow};
|
||||
//! # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
//! #
|
||||
//! # #[smol_potat::main]
|
||||
//! # #[cfg(feature = "mock")]
|
||||
//! # pub async fn main() -> Result<(), DbErr> {
|
||||
//! #
|
||||
//! # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
//! # .append_query_results(vec![
|
||||
//! # vec![cake::Model {
|
||||
@ -19,7 +24,7 @@
|
||||
//! # .into_mock_row()],
|
||||
//! # ])
|
||||
//! # .into_connection();
|
||||
//! # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
//! #
|
||||
//! // execute multiple queries in parallel
|
||||
//! let cakes_and_fruits: (Vec<cake::Model>, Vec<fruit::Model>) =
|
||||
//! futures::try_join!(Cake::find().all(&db), Fruit::find().all(&db))?;
|
||||
@ -53,7 +58,7 @@
|
||||
//! # ]
|
||||
//! # );
|
||||
//! # Ok(())
|
||||
//! # });
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! 2. Dynamic
|
||||
|
@ -19,7 +19,8 @@ pub struct MockDatabaseConnector;
|
||||
/// Defines a connection for the [MockDatabase]
|
||||
#[derive(Debug)]
|
||||
pub struct MockDatabaseConnection {
|
||||
counter: AtomicUsize,
|
||||
execute_counter: AtomicUsize,
|
||||
query_counter: AtomicUsize,
|
||||
mocker: Mutex<Box<dyn MockDatabaseTrait>>,
|
||||
}
|
||||
|
||||
@ -100,7 +101,8 @@ impl MockDatabaseConnection {
|
||||
M: MockDatabaseTrait,
|
||||
{
|
||||
Self {
|
||||
counter: AtomicUsize::new(0),
|
||||
execute_counter: AtomicUsize::new(0),
|
||||
query_counter: AtomicUsize::new(0),
|
||||
mocker: Mutex::new(Box::new(m)),
|
||||
}
|
||||
}
|
||||
@ -117,14 +119,14 @@ impl MockDatabaseConnection {
|
||||
/// Execute the SQL statement in the [MockDatabase]
|
||||
pub fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> {
|
||||
debug_print!("{}", statement);
|
||||
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
let counter = self.execute_counter.fetch_add(1, Ordering::SeqCst);
|
||||
self.mocker.lock().unwrap().execute(counter, statement)
|
||||
}
|
||||
|
||||
/// Return one [QueryResult] if the query was successful
|
||||
pub fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> {
|
||||
debug_print!("{}", statement);
|
||||
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
let counter = self.query_counter.fetch_add(1, Ordering::SeqCst);
|
||||
let result = self.mocker.lock().unwrap().query(counter, statement)?;
|
||||
Ok(result.into_iter().next())
|
||||
}
|
||||
@ -132,7 +134,7 @@ impl MockDatabaseConnection {
|
||||
/// Return all [QueryResult]s if the query was successful
|
||||
pub fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
|
||||
debug_print!("{}", statement);
|
||||
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
let counter = self.query_counter.fetch_add(1, Ordering::SeqCst);
|
||||
self.mocker.lock().unwrap().query(counter, statement)
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,113 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
}
|
||||
|
||||
/// Perform an `INSERT` operation on the ActiveModel
|
||||
///
|
||||
/// # Example (Postgres)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 15,
|
||||
/// # name: "Apple Pie".to_owned(),
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// let apple = cake::ActiveModel {
|
||||
/// name: Set("Apple Pie".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// apple.insert(&db).await?,
|
||||
/// cake::Model {
|
||||
/// id: 15,
|
||||
/// name: "Apple Pie".to_owned(),
|
||||
/// }
|
||||
/// .into_active_model()
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#,
|
||||
/// vec!["Apple Pie".into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example (MySQL)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::MySql)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 15,
|
||||
/// # name: "Apple Pie".to_owned(),
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 15,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// let apple = cake::ActiveModel {
|
||||
/// name: Set("Apple Pie".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// apple.insert(&db).await?,
|
||||
/// cake::Model {
|
||||
/// id: 15,
|
||||
/// name: "Apple Pie".to_owned(),
|
||||
/// }
|
||||
/// .into_active_model()
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![
|
||||
/// Transaction::from_sql_and_values(
|
||||
/// DbBackend::MySql,
|
||||
/// r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
|
||||
/// vec!["Apple Pie".into()]
|
||||
/// ),
|
||||
/// Transaction::from_sql_and_values(
|
||||
/// DbBackend::MySql,
|
||||
/// r#"SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = ? LIMIT ?"#,
|
||||
/// vec![15.into(), 1u64.into()]
|
||||
/// )
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
async fn insert<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
|
||||
where
|
||||
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
|
||||
@ -147,20 +254,126 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
C: ConnectionTrait<'a>,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_save(self, true)?;
|
||||
let res = <Self::Entity as EntityTrait>::insert(am).exec(db).await?;
|
||||
let found = <Self::Entity as EntityTrait>::find_by_id(res.last_insert_id)
|
||||
.one(db)
|
||||
let am = <Self::Entity as EntityTrait>::insert(am)
|
||||
.exec_with_returning(db)
|
||||
.await?;
|
||||
let am = match found {
|
||||
Some(model) => model.into_active_model(),
|
||||
None => return Err(DbErr::Exec("Failed to find inserted item".to_owned())),
|
||||
};
|
||||
ActiveModelBehavior::after_save(am, true)
|
||||
}
|
||||
|
||||
/// Perform the `UPDATE` operation on an ActiveModel
|
||||
///
|
||||
/// # Example (Postgres)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![fruit::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Orange".to_owned(),
|
||||
/// # cake_id: None,
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
/// let orange = fruit::ActiveModel {
|
||||
/// id: Set(1),
|
||||
/// name: Set("Orange".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// orange.update(&db).await?,
|
||||
/// fruit::Model {
|
||||
/// id: 1,
|
||||
/// name: "Orange".to_owned(),
|
||||
/// cake_id: None,
|
||||
/// }
|
||||
/// .into_active_model()
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 RETURNING "id", "name", "cake_id""#,
|
||||
/// vec!["Orange".into(), 1i32.into()]
|
||||
/// )]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example (MySQL)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::MySql)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![fruit::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Orange".to_owned(),
|
||||
/// # cake_id: None,
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
/// let orange = fruit::ActiveModel {
|
||||
/// id: Set(1),
|
||||
/// name: Set("Orange".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// orange.update(&db).await?,
|
||||
/// fruit::Model {
|
||||
/// id: 1,
|
||||
/// name: "Orange".to_owned(),
|
||||
/// cake_id: None,
|
||||
/// }
|
||||
/// .into_active_model()
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![
|
||||
/// Transaction::from_sql_and_values(
|
||||
/// DbBackend::MySql,
|
||||
/// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ?"#,
|
||||
/// vec!["Orange".into(), 1i32.into()]
|
||||
/// ),
|
||||
/// Transaction::from_sql_and_values(
|
||||
/// DbBackend::MySql,
|
||||
/// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
|
||||
/// vec![1i32.into(), 1u64.into()]
|
||||
/// )]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
async fn update<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
|
||||
where
|
||||
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait<'a>,
|
||||
{
|
||||
@ -195,6 +408,48 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
}
|
||||
|
||||
/// Delete an active model by its primary key
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
/// let orange = fruit::ActiveModel {
|
||||
/// id: Set(3),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// let delete_result = orange.delete(&db).await?;
|
||||
///
|
||||
/// assert_eq!(delete_result.rows_affected, 1);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
|
||||
/// vec![3i32.into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
async fn delete<'a, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
|
||||
where
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
|
@ -95,8 +95,11 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
@ -121,8 +124,6 @@ pub trait EntityTrait: EntityName {
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// cake::Entity::find().one(&db).await?,
|
||||
/// Some(cake::Model {
|
||||
@ -144,9 +145,6 @@ pub trait EntityTrait: EntityName {
|
||||
/// },
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -163,6 +161,9 @@ pub trait EntityTrait: EntityName {
|
||||
/// ),
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn find() -> Select<Self> {
|
||||
Select::new()
|
||||
@ -173,8 +174,11 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
@ -189,8 +193,6 @@ pub trait EntityTrait: EntityName {
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// cake::Entity::find_by_id(11).all(&db).await?,
|
||||
/// vec![cake::Model {
|
||||
@ -198,9 +200,6 @@ pub trait EntityTrait: EntityName {
|
||||
/// name: "Sponge Cake".to_owned(),
|
||||
/// }]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -210,11 +209,17 @@ pub trait EntityTrait: EntityName {
|
||||
/// vec![11i32.into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
/// Find by composite key
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
@ -229,8 +234,6 @@ pub trait EntityTrait: EntityName {
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await?,
|
||||
/// vec![cake_filling::Model {
|
||||
@ -238,9 +241,6 @@ pub trait EntityTrait: EntityName {
|
||||
/// filling_id: 3,
|
||||
/// }]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -252,6 +252,9 @@ pub trait EntityTrait: EntityName {
|
||||
/// ].join(" ").as_str(),
|
||||
/// vec![2i32.into(), 3i32.into()]
|
||||
/// )]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn find_by_id(values: <Self::PrimaryKey as PrimaryKeyTrait>::ValueType) -> Select<Self> {
|
||||
let mut select = Self::find();
|
||||
@ -272,13 +275,55 @@ pub trait EntityTrait: EntityName {
|
||||
|
||||
/// Insert an model into database
|
||||
///
|
||||
/// # Example
|
||||
/// # Example (Postgres)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![maplit::btreemap! {
|
||||
/// # "id" => Into::<Value>::into(15),
|
||||
/// # }]])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// let apple = cake::ActiveModel {
|
||||
/// name: Set("Apple Pie".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
|
||||
///
|
||||
/// assert_eq!(dbg!(insert_result.last_insert_id), 15);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#,
|
||||
/// vec!["Apple Pie".into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example (MySQL)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::MySql)
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 15,
|
||||
@ -294,21 +339,21 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
|
||||
///
|
||||
/// assert_eq!(insert_result.last_insert_id, 15);
|
||||
/// // assert_eq!(insert_result.rows_affected, 1);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#, vec!["Apple Pie".into()]
|
||||
/// )]);
|
||||
/// DbBackend::MySql,
|
||||
/// r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
|
||||
/// vec!["Apple Pie".into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn insert<A>(model: A) -> Insert<A>
|
||||
where
|
||||
@ -319,13 +364,61 @@ pub trait EntityTrait: EntityName {
|
||||
|
||||
/// Insert many models into database
|
||||
///
|
||||
/// # Example
|
||||
/// # Example (Postgres)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![maplit::btreemap! {
|
||||
/// # "id" => Into::<Value>::into(28),
|
||||
/// # }]])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// let apple = cake::ActiveModel {
|
||||
/// name: Set("Apple Pie".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let orange = cake::ActiveModel {
|
||||
/// name: Set("Orange Scone".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// let insert_result = cake::Entity::insert_many(vec![apple, orange])
|
||||
/// .exec(&db)
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(insert_result.last_insert_id, 28);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
|
||||
/// vec!["Apple Pie".into(), "Orange Scone".into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example (MySQL)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::MySql)
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 28,
|
||||
@ -345,22 +438,23 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let insert_result = cake::Entity::insert_many(vec![apple, orange]).exec(&db).await?;
|
||||
/// let insert_result = cake::Entity::insert_many(vec![apple, orange])
|
||||
/// .exec(&db)
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(insert_result.last_insert_id, 28);
|
||||
/// // assert_eq!(insert_result.rows_affected, 2);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
|
||||
/// DbBackend::MySql,
|
||||
/// r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
|
||||
/// vec!["Apple Pie".into(), "Orange Scone".into()]
|
||||
/// )]);
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn insert_many<A, I>(models: I) -> Insert<A>
|
||||
where
|
||||
@ -374,18 +468,22 @@ pub trait EntityTrait: EntityName {
|
||||
///
|
||||
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||
///
|
||||
/// # Example
|
||||
/// # Example (Postgres)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![fruit::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Orange".to_owned(),
|
||||
/// # cake_id: None,
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
@ -397,25 +495,93 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// fruit::Entity::update(orange.clone())
|
||||
/// .filter(fruit::Column::Name.contains("orange"))
|
||||
/// .exec(&db)
|
||||
/// .await?,
|
||||
/// orange
|
||||
/// fruit::Model {
|
||||
/// id: 1,
|
||||
/// name: "Orange".to_owned(),
|
||||
/// cake_id: None,
|
||||
/// }
|
||||
/// .into_active_model(),
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3"#,
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
|
||||
/// vec!["Orange".into(), 1i32.into(), "%orange%".into()]
|
||||
/// )]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example (MySQL)
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::MySql)
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![fruit::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Orange".to_owned(),
|
||||
/// # cake_id: None,
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
/// let orange = fruit::ActiveModel {
|
||||
/// id: Set(1),
|
||||
/// name: Set("Orange".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// fruit::Entity::update(orange.clone())
|
||||
/// .filter(fruit::Column::Name.contains("orange"))
|
||||
/// .exec(&db)
|
||||
/// .await?,
|
||||
/// fruit::Model {
|
||||
/// id: 1,
|
||||
/// name: "Orange".to_owned(),
|
||||
/// cake_id: None,
|
||||
/// }
|
||||
/// .into_active_model(),
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![
|
||||
/// Transaction::from_sql_and_values(
|
||||
/// DbBackend::MySql,
|
||||
/// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
|
||||
/// vec!["Orange".into(), 1i32.into(), "%orange%".into()]
|
||||
/// ),
|
||||
/// Transaction::from_sql_and_values(
|
||||
/// DbBackend::MySql,
|
||||
/// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
|
||||
/// vec![1i32.into(), 1u64.into()]
|
||||
/// )]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn update<A>(model: A) -> UpdateOne<A>
|
||||
where
|
||||
@ -431,8 +597,11 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_exec_results(vec![
|
||||
@ -443,10 +612,13 @@ pub trait EntityTrait: EntityName {
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::{Expr, Value}};
|
||||
/// use sea_orm::{
|
||||
/// entity::*,
|
||||
/// query::*,
|
||||
/// sea_query::{Expr, Value},
|
||||
/// tests_cfg::fruit,
|
||||
/// };
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let update_result = fruit::Entity::update_many()
|
||||
/// .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
|
||||
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||
@ -454,15 +626,18 @@ pub trait EntityTrait: EntityName {
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(update_result.rows_affected, 5);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#, vec![Value::Int(None), "%Apple%".into()]
|
||||
/// )]);
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
|
||||
/// vec![Value::Int(None), "%Apple%".into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn update_many() -> UpdateMany<Self> {
|
||||
Update::many(Self::default())
|
||||
@ -475,8 +650,11 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_exec_results(vec![
|
||||
@ -494,20 +672,21 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
|
||||
///
|
||||
/// assert_eq!(delete_result.rows_affected, 1);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#, vec![3i32.into()]
|
||||
/// )]);
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
|
||||
/// vec![3i32.into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn delete<A>(model: A) -> DeleteOne<A>
|
||||
where
|
||||
@ -523,8 +702,11 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{entity::*, error::*, query::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_exec_results(vec![
|
||||
@ -533,27 +715,34 @@ pub trait EntityTrait: EntityName {
|
||||
/// # rows_affected: 5,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 15,
|
||||
/// # name: "Apple Pie".to_owned(),
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let delete_result = fruit::Entity::delete_many()
|
||||
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||
/// .exec(&db)
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(delete_result.rows_affected, 5);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
/// vec![Transaction::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#, vec!["%Apple%".into()]
|
||||
/// )]);
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
|
||||
/// vec!["%Apple%".into()]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn delete_many() -> DeleteMany<Self> {
|
||||
Delete::many(Self::default())
|
||||
|
@ -47,8 +47,11 @@ pub trait FromQueryResult: Sized {
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![
|
||||
@ -67,8 +70,6 @@ pub trait FromQueryResult: Sized {
|
||||
/// num_of_cakes: i32,
|
||||
/// }
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let res: Vec<SelectResult> = SelectResult::find_by_statement(Statement::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
/// r#"SELECT "name", COUNT(*) AS "num_of_cakes" FROM "cake" GROUP BY("name")"#,
|
||||
@ -85,8 +86,6 @@ pub trait FromQueryResult: Sized {
|
||||
/// },]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
/// # assert_eq!(
|
||||
/// # db.into_transaction_log(),
|
||||
/// # vec![Transaction::from_sql_and_values(
|
||||
@ -95,6 +94,9 @@ pub trait FromQueryResult: Sized {
|
||||
/// # vec![]
|
||||
/// # ),]
|
||||
/// # );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn find_by_statement(stmt: Statement) -> SelectorRaw<SelectModel<Self>> {
|
||||
SelectorRaw::<SelectModel<Self>>::from_statement(stmt)
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::{
|
||||
error::*, ActiveModelTrait, ConnectionTrait, DbBackend, EntityTrait, Insert, PrimaryKeyTrait,
|
||||
Statement, TryFromU64,
|
||||
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel,
|
||||
Iterable, PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64,
|
||||
};
|
||||
use sea_query::{
|
||||
Alias, Expr, FromValueTuple, Iden, InsertStatement, IntoColumnRef, Query, ValueTuple,
|
||||
};
|
||||
use sea_query::{FromValueTuple, InsertStatement, ValueTuple};
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
/// Defines a structure to perform INSERT operations in an ActiveModel
|
||||
@ -39,18 +41,28 @@ where
|
||||
{
|
||||
// so that self is dropped before entering await
|
||||
let mut query = self.query;
|
||||
if db.get_database_backend() == DbBackend::Postgres {
|
||||
use crate::{sea_query::Query, Iterable};
|
||||
if <A::Entity as EntityTrait>::PrimaryKey::iter().count() > 0 {
|
||||
query.returning(
|
||||
Query::select()
|
||||
.columns(<A::Entity as EntityTrait>::PrimaryKey::iter())
|
||||
.take(),
|
||||
);
|
||||
}
|
||||
if db.support_returning() && <A::Entity as EntityTrait>::PrimaryKey::iter().count() > 0 {
|
||||
let mut returning = Query::select();
|
||||
returning.columns(
|
||||
<A::Entity as EntityTrait>::PrimaryKey::iter().map(|c| c.into_column_ref()),
|
||||
);
|
||||
query.returning(returning);
|
||||
}
|
||||
Inserter::<A>::new(self.primary_key, query).exec(db)
|
||||
}
|
||||
|
||||
/// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported)
|
||||
pub fn exec_with_returning<'a, C>(
|
||||
self,
|
||||
db: &'a C,
|
||||
) -> impl Future<Output = Result<A, DbErr>> + '_
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
A: 'a,
|
||||
{
|
||||
Inserter::<A>::new(self.primary_key, self.query).exec_with_returning(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Inserter<A>
|
||||
@ -75,6 +87,19 @@ where
|
||||
let builder = db.get_database_backend();
|
||||
exec_insert(self.primary_key, builder.build(&self.query), db)
|
||||
}
|
||||
|
||||
/// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported)
|
||||
pub fn exec_with_returning<'a, C>(
|
||||
self,
|
||||
db: &'a C,
|
||||
) -> impl Future<Output = Result<A, DbErr>> + '_
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
A: 'a,
|
||||
{
|
||||
exec_insert_with_returning(self.primary_key, self.query, db)
|
||||
}
|
||||
}
|
||||
|
||||
async fn exec_insert<'a, A, C>(
|
||||
@ -88,16 +113,15 @@ where
|
||||
{
|
||||
type PrimaryKey<A> = <<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey;
|
||||
type ValueTypeOf<A> = <PrimaryKey<A> as PrimaryKeyTrait>::ValueType;
|
||||
let last_insert_id_opt = match db.get_database_backend() {
|
||||
DbBackend::Postgres => {
|
||||
use crate::{sea_query::Iden, Iterable};
|
||||
let last_insert_id_opt = match db.support_returning() {
|
||||
true => {
|
||||
let cols = PrimaryKey::<A>::iter()
|
||||
.map(|col| col.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let res = db.query_one(statement).await?.unwrap();
|
||||
res.try_get_many("", cols.as_ref()).ok()
|
||||
}
|
||||
_ => {
|
||||
false => {
|
||||
let last_insert_id = db.execute(statement).await?.last_insert_id();
|
||||
ValueTypeOf::<A>::try_from_u64(last_insert_id).ok()
|
||||
}
|
||||
@ -111,3 +135,47 @@ where
|
||||
};
|
||||
Ok(InsertResult { last_insert_id })
|
||||
}
|
||||
|
||||
async fn exec_insert_with_returning<'a, A, C>(
|
||||
primary_key: Option<ValueTuple>,
|
||||
mut insert_statement: InsertStatement,
|
||||
db: &'a C,
|
||||
) -> Result<A, DbErr>
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
A: ActiveModelTrait,
|
||||
{
|
||||
let db_backend = db.get_database_backend();
|
||||
let found = match db.support_returning() {
|
||||
true => {
|
||||
let mut returning = Query::select();
|
||||
returning.exprs(<A::Entity as EntityTrait>::Column::iter().map(|c| {
|
||||
let col = Expr::col(c);
|
||||
let col_def = ColumnTrait::def(&c);
|
||||
let col_type = col_def.get_column_type();
|
||||
match col_type.get_enum_name() {
|
||||
Some(_) => col.as_enum(Alias::new("text")),
|
||||
None => col.into(),
|
||||
}
|
||||
}));
|
||||
insert_statement.returning(returning);
|
||||
SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement(
|
||||
db_backend.build(&insert_statement),
|
||||
)
|
||||
.one(db)
|
||||
.await?
|
||||
}
|
||||
false => {
|
||||
let insert_res =
|
||||
exec_insert::<A, _>(primary_key, db_backend.build(&insert_statement), db).await?;
|
||||
<A::Entity as EntityTrait>::find_by_id(insert_res.last_insert_id)
|
||||
.one(db)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
match found {
|
||||
Some(model) => Ok(model.into_active_model()),
|
||||
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
|
||||
}
|
||||
}
|
||||
|
@ -96,12 +96,23 @@ where
|
||||
|
||||
/// Fetch one page and increment the page counter
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, MockDatabase, DbBackend};
|
||||
/// # let owned_db = MockDatabase::new(DbBackend::Postgres).into_connection();
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let owned_db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Cake".to_owned(),
|
||||
/// # }],
|
||||
/// # vec![],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// # let db = &owned_db;
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
/// let mut cake_pages = cake::Entity::find()
|
||||
@ -113,7 +124,7 @@ where
|
||||
/// }
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn fetch_and_next(&mut self) -> Result<Option<Vec<S::Item>>, DbErr> {
|
||||
let vec = self.fetch().await?;
|
||||
@ -124,12 +135,23 @@ where
|
||||
|
||||
/// Convert self into an async stream
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, MockDatabase, DbBackend};
|
||||
/// # let owned_db = MockDatabase::new(DbBackend::Postgres).into_connection();
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let owned_db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Cake".to_owned(),
|
||||
/// # }],
|
||||
/// # vec![],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// # let db = &owned_db;
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// use futures::TryStreamExt;
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
@ -143,7 +165,7 @@ where
|
||||
/// }
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn into_stream(mut self) -> PinBoxStream<'db, Result<Vec<S::Item>, DbErr>> {
|
||||
Box::pin(stream! {
|
||||
|
@ -320,8 +320,11 @@ pub trait TryGetableMany: Sized {
|
||||
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
|
||||
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(all(feature = "mock", feature = "macros"))]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![
|
||||
@ -344,8 +347,6 @@ pub trait TryGetableMany: Sized {
|
||||
/// NumOfCakes,
|
||||
/// }
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let res: Vec<(String, i32)> =
|
||||
/// <(String, i32)>::find_by_statement::<ResultCol>(Statement::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
@ -362,9 +363,6 @@ pub trait TryGetableMany: Sized {
|
||||
/// ("New York Cheese".to_owned(), 1),
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -374,6 +372,9 @@ pub trait TryGetableMany: Sized {
|
||||
/// vec![]
|
||||
/// ),]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
|
||||
where
|
||||
|
@ -143,8 +143,11 @@ where
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(all(feature = "mock", feature = "macros"))]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![
|
||||
@ -164,8 +167,6 @@ where
|
||||
/// CakeName,
|
||||
/// }
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let res: Vec<String> = cake::Entity::find()
|
||||
/// .select_only()
|
||||
/// .column_as(cake::Column::Name, QueryAs::CakeName)
|
||||
@ -177,9 +178,6 @@ where
|
||||
/// res,
|
||||
/// vec!["Chocolate Forest".to_owned(), "New York Cheese".to_owned()]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -189,11 +187,17 @@ where
|
||||
/// vec![]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(all(feature = "mock", feature = "macros"))]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![
|
||||
@ -212,8 +216,6 @@ where
|
||||
/// NumOfCakes,
|
||||
/// }
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let res: Vec<(String, i64)> = cake::Entity::find()
|
||||
/// .select_only()
|
||||
/// .column_as(cake::Column::Name, QueryAs::CakeName)
|
||||
@ -224,9 +226,6 @@ where
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(res, vec![("Chocolate Forest".to_owned(), 2i64)]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -241,6 +240,9 @@ where
|
||||
/// vec![]
|
||||
/// )]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn into_values<T, C>(self) -> Selector<SelectGetableValue<T, C>>
|
||||
where
|
||||
@ -490,8 +492,11 @@ where
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![
|
||||
@ -514,8 +519,6 @@ where
|
||||
/// num_of_cakes: i32,
|
||||
/// }
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let res: Vec<SelectResult> = cake::Entity::find()
|
||||
/// .from_raw_sql(Statement::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
@ -539,9 +542,6 @@ where
|
||||
/// },
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -551,6 +551,9 @@ where
|
||||
/// vec![]
|
||||
/// ),]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn into_model<M>(self) -> SelectorRaw<SelectModel<M>>
|
||||
where
|
||||
@ -563,8 +566,11 @@ where
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![vec![
|
||||
@ -581,8 +587,6 @@ where
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let res: Vec<serde_json::Value> = cake::Entity::find().from_raw_sql(
|
||||
/// Statement::from_sql_and_values(
|
||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
|
||||
@ -605,9 +609,6 @@ where
|
||||
/// }),
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -616,6 +617,9 @@ where
|
||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
|
||||
/// ),
|
||||
/// ]);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "with-json")]
|
||||
pub fn into_json(self) -> SelectorRaw<SelectModel<JsonValue>> {
|
||||
@ -627,15 +631,23 @@ where
|
||||
|
||||
/// Get an item from the Select query
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres).into_connection();
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Cake".to_owned(),
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let _: Option<cake::Model> = cake::Entity::find()
|
||||
/// .from_raw_sql(Statement::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
@ -644,9 +656,6 @@ where
|
||||
/// ))
|
||||
/// .one(&db)
|
||||
/// .await?;
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -656,6 +665,9 @@ where
|
||||
/// vec![1.into()]
|
||||
/// ),]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn one<'a, C>(self, db: &C) -> Result<Option<S::Item>, DbErr>
|
||||
where
|
||||
@ -670,15 +682,23 @@ where
|
||||
|
||||
/// Get all items from the Select query
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
|
||||
/// # use sea_orm::{error::*, tests_cfg::*, *};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres).into_connection();
|
||||
/// # #[smol_potat::main]
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # pub async fn main() -> Result<(), DbErr> {
|
||||
/// #
|
||||
/// # let db = MockDatabase::new(DbBackend::Postgres)
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![cake::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "Cake".to_owned(),
|
||||
/// # }],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||
/// #
|
||||
/// let _: Vec<cake::Model> = cake::Entity::find()
|
||||
/// .from_raw_sql(Statement::from_sql_and_values(
|
||||
/// DbBackend::Postgres,
|
||||
@ -687,9 +707,6 @@ where
|
||||
/// ))
|
||||
/// .all(&db)
|
||||
/// .await?;
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// db.into_transaction_log(),
|
||||
@ -699,6 +716,9 @@ where
|
||||
/// vec![]
|
||||
/// ),]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
|
||||
where
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, Statement, UpdateMany, UpdateOne,
|
||||
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, IntoActiveModel,
|
||||
Iterable, SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne,
|
||||
};
|
||||
use sea_query::UpdateStatement;
|
||||
use sea_query::{Alias, Expr, FromValueTuple, Query, UpdateStatement};
|
||||
use std::future::Future;
|
||||
|
||||
/// Defines an update operation
|
||||
@ -25,10 +26,11 @@ where
|
||||
/// Execute an update operation on an ActiveModel
|
||||
pub async fn exec<'b, C>(self, db: &'b C) -> Result<A, DbErr>
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'b>,
|
||||
{
|
||||
// so that self is dropped before entering await
|
||||
exec_update_and_return_original(self.query, self.model, db).await
|
||||
exec_update_and_return_updated(self.query, self.model, db).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,17 +80,61 @@ where
|
||||
Updater::new(query).exec(db).await
|
||||
}
|
||||
|
||||
async fn exec_update_and_return_original<'a, A, C>(
|
||||
query: UpdateStatement,
|
||||
async fn exec_update_and_return_updated<'a, A, C>(
|
||||
mut query: UpdateStatement,
|
||||
model: A,
|
||||
db: &'a C,
|
||||
) -> Result<A, DbErr>
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
A: ActiveModelTrait,
|
||||
C: ConnectionTrait<'a>,
|
||||
{
|
||||
Updater::new(query).check_record_exists().exec(db).await?;
|
||||
Ok(model)
|
||||
match db.support_returning() {
|
||||
true => {
|
||||
let mut returning = Query::select();
|
||||
returning.exprs(<A::Entity as EntityTrait>::Column::iter().map(|c| {
|
||||
let col = Expr::col(c);
|
||||
let col_def = c.def();
|
||||
let col_type = col_def.get_column_type();
|
||||
match col_type.get_enum_name() {
|
||||
Some(_) => col.as_enum(Alias::new("text")),
|
||||
None => col.into(),
|
||||
}
|
||||
}));
|
||||
query.returning(returning);
|
||||
let db_backend = db.get_database_backend();
|
||||
let found: Option<<A::Entity as EntityTrait>::Model> =
|
||||
SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement(
|
||||
db_backend.build(&query),
|
||||
)
|
||||
.one(db)
|
||||
.await?;
|
||||
// If we got `None` then we are updating a row that does not exist.
|
||||
match found {
|
||||
Some(model) => Ok(model.into_active_model()),
|
||||
None => Err(DbErr::RecordNotFound(
|
||||
"None of the database rows are affected".to_owned(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
false => {
|
||||
// If we updating a row that does not exist then an error will be thrown here.
|
||||
Updater::new(query).check_record_exists().exec(db).await?;
|
||||
let primary_key_value = match model.get_primary_key_value() {
|
||||
Some(val) => FromValueTuple::from_value_tuple(val),
|
||||
None => return Err(DbErr::Exec("Fail to get primary key from model".to_owned())),
|
||||
};
|
||||
let found = <A::Entity as EntityTrait>::find_by_id(primary_key_value)
|
||||
.one(db)
|
||||
.await?;
|
||||
// If we cannot select the updated row from db by the cached primary key
|
||||
match found {
|
||||
Some(model) => Ok(model.into_active_model()),
|
||||
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn exec_update<'a, C>(
|
||||
@ -119,23 +165,16 @@ mod tests {
|
||||
#[smol_potat::test]
|
||||
async fn update_record_not_found_1() -> Result<(), DbErr> {
|
||||
let db = MockDatabase::new(DbBackend::Postgres)
|
||||
.append_query_results(vec![
|
||||
vec![cake::Model {
|
||||
id: 1,
|
||||
name: "Cheese Cake".to_owned(),
|
||||
}],
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
])
|
||||
.append_exec_results(vec![
|
||||
MockExecResult {
|
||||
last_insert_id: 0,
|
||||
rows_affected: 1,
|
||||
},
|
||||
MockExecResult {
|
||||
last_insert_id: 0,
|
||||
rows_affected: 0,
|
||||
},
|
||||
MockExecResult {
|
||||
last_insert_id: 0,
|
||||
rows_affected: 0,
|
||||
},
|
||||
MockExecResult {
|
||||
last_insert_id: 0,
|
||||
rows_affected: 0,
|
||||
},
|
||||
MockExecResult {
|
||||
last_insert_id: 0,
|
||||
rows_affected: 0,
|
||||
@ -217,22 +256,22 @@ mod tests {
|
||||
vec![
|
||||
Transaction::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2 RETURNING "id", "name""#,
|
||||
vec!["Cheese Cake".into(), 1i32.into()]
|
||||
),
|
||||
Transaction::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2 RETURNING "id", "name""#,
|
||||
vec!["Cheese Cake".into(), 2i32.into()]
|
||||
),
|
||||
Transaction::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2 RETURNING "id", "name""#,
|
||||
vec!["Cheese Cake".into(), 2i32.into()]
|
||||
),
|
||||
Transaction::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#,
|
||||
r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2 RETURNING "id", "name""#,
|
||||
vec!["Cheese Cake".into(), 2i32.into()]
|
||||
),
|
||||
Transaction::from_sql_and_values(
|
||||
|
69
tests/returning_tests.rs
Normal file
69
tests/returning_tests.rs
Normal file
@ -0,0 +1,69 @@
|
||||
pub mod common;
|
||||
|
||||
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||
pub use sea_orm::{entity::prelude::*, *};
|
||||
pub use sea_query::Query;
|
||||
|
||||
#[sea_orm_macros::test]
|
||||
#[cfg(any(
|
||||
feature = "sqlx-mysql",
|
||||
feature = "sqlx-sqlite",
|
||||
feature = "sqlx-postgres"
|
||||
))]
|
||||
async fn main() -> Result<(), DbErr> {
|
||||
use bakery::*;
|
||||
|
||||
let ctx = TestContext::new("returning_tests").await;
|
||||
let db = &ctx.db;
|
||||
let builder = db.get_database_backend();
|
||||
|
||||
let mut insert = Query::insert();
|
||||
insert
|
||||
.into_table(Entity)
|
||||
.columns(vec![Column::Name, Column::ProfitMargin])
|
||||
.values_panic(vec!["Bakery Shop".into(), 0.5.into()]);
|
||||
|
||||
let mut update = Query::update();
|
||||
update
|
||||
.table(Entity)
|
||||
.values(vec![
|
||||
(Column::Name, "Bakery Shop".into()),
|
||||
(Column::ProfitMargin, 0.5.into()),
|
||||
])
|
||||
.and_where(Column::Id.eq(1));
|
||||
|
||||
let mut returning = Query::select();
|
||||
returning.columns(vec![Column::Id, Column::Name, Column::ProfitMargin]);
|
||||
|
||||
create_tables(db).await?;
|
||||
|
||||
if db.support_returning() {
|
||||
insert.returning(returning.clone());
|
||||
let insert_res = db
|
||||
.query_one(builder.build(&insert))
|
||||
.await?
|
||||
.expect("Insert failed with query_one");
|
||||
let _id: i32 = insert_res.try_get("", "id")?;
|
||||
let _name: String = insert_res.try_get("", "name")?;
|
||||
let _profit_margin: f64 = insert_res.try_get("", "profit_margin")?;
|
||||
|
||||
update.returning(returning.clone());
|
||||
let update_res = db
|
||||
.query_one(builder.build(&update))
|
||||
.await?
|
||||
.expect("Update filed with query_one");
|
||||
let _id: i32 = update_res.try_get("", "id")?;
|
||||
let _name: String = update_res.try_get("", "name")?;
|
||||
let _profit_margin: f64 = update_res.try_get("", "profit_margin")?;
|
||||
} else {
|
||||
let insert_res = db.execute(builder.build(&insert)).await?;
|
||||
assert!(insert_res.rows_affected() > 0);
|
||||
|
||||
let update_res = db.execute(builder.build(&update)).await?;
|
||||
assert!(update_res.rows_affected() > 0);
|
||||
}
|
||||
|
||||
ctx.delete().await;
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user