diff --git a/README.md b/README.md index e3cd469..e52c1b2 100644 --- a/README.md +++ b/README.md @@ -557,7 +557,7 @@ Post::find(executor, &post_id).await?; // Mutation operations post.create(executor).await?; post.update(executor).await?; -post.create_or_update(executor).await?; +post.upsert(executor).await?; post.delete(executor).await?; Post::delete_by_id(executor, &post_id).await?; diff --git a/examples/postgres/users-comments-and-followers/src/models/profiles.rs b/examples/postgres/users-comments-and-followers/src/models/profiles.rs index 0ff3be7..643759e 100644 --- a/examples/postgres/users-comments-and-followers/src/models/profiles.rs +++ b/examples/postgres/users-comments-and-followers/src/models/profiles.rs @@ -58,7 +58,7 @@ impl Profile { &mut self, display_name: Option, bio: Option, - executor: E + executor: E, ) -> Result where E: sqlx::Executor<'e, Database = sqlx::Postgres>, diff --git a/examples/postgres/users-comments-and-followers/src/models/users.rs b/examples/postgres/users-comments-and-followers/src/models/users.rs index 650aaef..418dec1 100644 --- a/examples/postgres/users-comments-and-followers/src/models/users.rs +++ b/examples/postgres/users-comments-and-followers/src/models/users.rs @@ -69,7 +69,7 @@ impl User { pub async fn get_user_by_id_or_select<'e, E>( id: Option, prompt: &str, - executor: E + executor: E, ) -> Result where E: sqlx::Executor<'e, Database = sqlx::Postgres>, @@ -128,8 +128,7 @@ impl User { Ok(user) } - pub async fn update_profile(id: Option, pool: &sqlx::PgPool) -> Result<(User, Profile)> - { + pub async fn update_profile(id: Option, pool: &sqlx::PgPool) -> Result<(User, Profile)> { let prompt = "Select the user whose profile you want to update"; let user = Self::get_user_by_id_or_select(id, prompt, pool).await?; let profile = match user.get_profile(pool).await? { diff --git a/georm-macros/src/georm/traits/upsert.rs b/georm-macros/src/georm/traits/upsert.rs index aaf2c5c..271f0bd 100644 --- a/georm-macros/src/georm/traits/upsert.rs +++ b/georm-macros/src/georm/traits/upsert.rs @@ -44,7 +44,7 @@ pub fn generate_upsert_query( let field_idents: Vec = fields.iter().map(|f| f.ident.clone()).collect(); quote! { - async fn create_or_update<'e, E>(&self, mut executor: E) -> ::sqlx::Result + async fn upsert<'e, E>(&self, mut executor: E) -> ::sqlx::Result where E: ::sqlx::Executor<'e, Database = ::sqlx::Postgres> { diff --git a/src/entity.rs b/src/entity.rs deleted file mode 100644 index 4b17100..0000000 --- a/src/entity.rs +++ /dev/null @@ -1,83 +0,0 @@ -pub trait Georm { - /// Find all the entities in the database. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn find_all( - pool: &sqlx::PgPool, - ) -> impl ::std::future::Future>> + Send - where - Self: Sized; - - /// Find the entiy in the database based on its identifier. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn find( - pool: &sqlx::PgPool, - id: &Id, - ) -> impl std::future::Future>> + Send - where - Self: Sized; - - /// Create the entity in the database. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn create( - &self, - pool: &sqlx::PgPool, - ) -> impl std::future::Future> + Send - where - Self: Sized; - - /// Update an entity with a matching identifier in the database. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn update( - &self, - pool: &sqlx::PgPool, - ) -> impl std::future::Future> + Send - where - Self: Sized; - - /// Update an entity with a matching identifier in the database if - /// it exists, create it otherwise. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn create_or_update( - &self, - pool: &sqlx::PgPool, - ) -> impl std::future::Future> + Send - where - Self: Sized; - - /// Delete the entity from the database if it exists. - /// - /// # Returns - /// Returns the amount of rows affected by the deletion. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn delete( - &self, - pool: &sqlx::PgPool, - ) -> impl std::future::Future> + Send; - - /// Delete any entity with the identifier `id`. - /// - /// # Returns - /// Returns the amount of rows affected by the deletion. - /// - /// # Errors - /// Returns any error Postgres may have encountered - fn delete_by_id( - pool: &sqlx::PgPool, - id: &Id, - ) -> impl std::future::Future> + Send; - - /// Returns the identifier of the entity. - fn get_id(&self) -> &Id; -} diff --git a/src/georm.rs b/src/georm.rs index 923e4c0..623213f 100644 --- a/src/georm.rs +++ b/src/georm.rs @@ -26,7 +26,7 @@ use sqlx::{Executor, Postgres}; /// ### Instance Methods (Mutation Operations) /// - [`create`] - Insert a new entity into the database /// - [`update`] - Update an existing entity in the database -/// - [`create_or_update`] - Upsert (insert or update) an entity +/// - [`upsert`] - Upsert (insert or update) an entity /// - [`delete`] - Delete this entity from the database /// - [`get_id`] - Get the primary key of this entity /// @@ -92,7 +92,7 @@ use sqlx::{Executor, Postgres}; /// [`find`]: Georm::find /// [`create`]: Georm::create /// [`update`]: Georm::update -/// [`create_or_update`]: Georm::create_or_update +/// [`upsert`]: Georm::upsert /// [`delete`]: Georm::delete /// [`delete_by_id`]: Georm::delete_by_id /// [`get_id`]: Georm::get_id @@ -264,7 +264,7 @@ pub trait Georm { /// # Examples /// ```ignore /// let user = User { id: 1, username: "alice".into(), email: "alice@example.com".into() }; - /// let final_user = user.create_or_update(&pool).await?; + /// let final_user = user.upsert(&pool).await?; /// // Will insert if ID 1 doesn't exist, update if it does /// ``` /// @@ -273,13 +273,22 @@ pub trait Georm { /// - Non-primary-key constraint violations /// - Database connection issues /// - Permission problems + fn upsert<'e, E>(&self, executor: E) -> impl ::std::future::Future> + where + Self: Sized, + E: Executor<'e, Database = Postgres>; + + #[deprecated(since = "0.3.0", note = "Please use `upsert` instead")] fn create_or_update<'e, E>( &self, executor: E, ) -> impl ::std::future::Future> where Self: Sized, - E: Executor<'e, Database = Postgres>; + E: Executor<'e, Database = Postgres>, + { + self.upsert(executor) + } /// Delete this entity from the database. /// diff --git a/src/lib.rs b/src/lib.rs index a018548..186a437 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ //! ### Instance Methods (called on entity objects) //! - `entity.create(pool)` - Insert new record, returns created entity with database-generated values //! - `entity.update(pool)` - Update existing record, returns updated entity with fresh database state -//! - `entity.create_or_update(pool)` - True PostgreSQL upsert using `ON CONFLICT`, returns final entity +//! - `entity.upsert(pool)` - True PostgreSQL upsert using `ON CONFLICT`, returns final entity //! - `entity.delete(pool)` - Delete this record, returns affected row count //! - `entity.get_id()` - Get reference to the entity's ID (`&Id` for simple keys, owned for composite) //! @@ -55,7 +55,7 @@ //! Georm leverages PostgreSQL-specific features for performance and reliability: //! //! - **RETURNING clause**: All `INSERT` and `UPDATE` operations use `RETURNING *` to capture database-generated values (sequences, defaults, triggers) -//! - **True upserts**: `create_or_update()` uses `INSERT ... ON CONFLICT ... DO UPDATE` for atomic upsert operations +//! - **True upserts**: `upsert()` uses `INSERT ... ON CONFLICT ... DO UPDATE` for atomic upsert operations //! - **Prepared statements**: All queries use parameter binding for security and performance //! - **Compile-time verification**: SQLx macros verify all generated SQL against your database schema at compile time //! diff --git a/tests/composite_key.rs b/tests/composite_key.rs index 8c16079..7ef1ba1 100644 --- a/tests/composite_key.rs +++ b/tests/composite_key.rs @@ -36,7 +36,7 @@ fn composite_key_get_id() { } #[sqlx::test(fixtures("composite_key"))] -async fn composite_key_create_or_update(pool: sqlx::PgPool) -> sqlx::Result<()> { +async fn composite_key_upsert(pool: sqlx::PgPool) -> sqlx::Result<()> { let new_user_role = UserRole { user_id: 5, role_id: 2, @@ -44,7 +44,7 @@ async fn composite_key_create_or_update(pool: sqlx::PgPool) -> sqlx::Result<()> }; // This will test the upsert query generation bug - let result = new_user_role.create_or_update(&pool).await?; + let result = new_user_role.upsert(&pool).await?; assert_eq!(5, result.user_id); assert_eq!(2, result.role_id); diff --git a/tests/generated.rs b/tests/generated.rs index 5ca39a4..2dfe73c 100644 --- a/tests/generated.rs +++ b/tests/generated.rs @@ -52,7 +52,7 @@ async fn upsert_handles_generated_fields(pool: sqlx::PgPool) -> sqlx::Result<()> let mut modified_product = product.clone(); modified_product.price = BigDecimal::from(1200); - let upserted = modified_product.create_or_update(&pool).await?; + let upserted = modified_product.upsert(&pool).await?; // price is updated assert_eq!(upserted.price, BigDecimal::from(1200)); diff --git a/tests/simple_struct.rs b/tests/simple_struct.rs index ea17dad..7ea6e1b 100644 --- a/tests/simple_struct.rs +++ b/tests/simple_struct.rs @@ -115,7 +115,7 @@ async fn should_create_if_does_not_exist(pool: sqlx::PgPool) -> sqlx::Result<()> name: "Miura Kentaro".into(), ..Default::default() }; - author.create_or_update(&pool).await?; + author.upsert(&pool).await?; let all_authors = Author::find_all(&pool).await?; assert_eq!(1, all_authors.len()); Ok(()) @@ -130,7 +130,7 @@ async fn should_update_if_exist(pool: sqlx::PgPool) -> sqlx::Result<()> { name: "Miura Kentaro".into(), ..Default::default() }; - author.create_or_update(&pool).await?; + author.upsert(&pool).await?; let mut all_authors = Author::find_all(&pool).await?; all_authors.sort(); assert_eq!(3, all_authors.len());