mirror of
				https://github.com/Phundrak/georm.git
				synced 2025-11-04 01:11:10 +00:00 
			
		
		
		
	feat: deprecate create_or_update in favour of upsert
				
					
				
			The `create_or_update` method has been deprecated and replaced by `upsert` for clarity and consistency with common database terminology. This commit also removes the file `src/entity.rs` which has been forgotten in earlier commits and was no longer part of Georm.
This commit is contained in:
		
							parent
							
								
									49c7d86102
								
							
						
					
					
						commit
						5d8a1b1917
					
				@ -557,7 +557,7 @@ Post::find(executor, &post_id).await?;
 | 
				
			|||||||
// Mutation operations
 | 
					// Mutation operations
 | 
				
			||||||
post.create(executor).await?;
 | 
					post.create(executor).await?;
 | 
				
			||||||
post.update(executor).await?;
 | 
					post.update(executor).await?;
 | 
				
			||||||
post.create_or_update(executor).await?;
 | 
					post.upsert(executor).await?;
 | 
				
			||||||
post.delete(executor).await?;
 | 
					post.delete(executor).await?;
 | 
				
			||||||
Post::delete_by_id(executor, &post_id).await?;
 | 
					Post::delete_by_id(executor, &post_id).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,7 @@ impl Profile {
 | 
				
			|||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        display_name: Option<String>,
 | 
					        display_name: Option<String>,
 | 
				
			||||||
        bio: Option<String>,
 | 
					        bio: Option<String>,
 | 
				
			||||||
        executor: E
 | 
					        executor: E,
 | 
				
			||||||
    ) -> Result<Self>
 | 
					    ) -> Result<Self>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        E: sqlx::Executor<'e, Database = sqlx::Postgres>,
 | 
					        E: sqlx::Executor<'e, Database = sqlx::Postgres>,
 | 
				
			||||||
 | 
				
			|||||||
@ -69,7 +69,7 @@ impl User {
 | 
				
			|||||||
    pub async fn get_user_by_id_or_select<'e, E>(
 | 
					    pub async fn get_user_by_id_or_select<'e, E>(
 | 
				
			||||||
        id: Option<i32>,
 | 
					        id: Option<i32>,
 | 
				
			||||||
        prompt: &str,
 | 
					        prompt: &str,
 | 
				
			||||||
        executor: E
 | 
					        executor: E,
 | 
				
			||||||
    ) -> Result<Self>
 | 
					    ) -> Result<Self>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        E: sqlx::Executor<'e, Database = sqlx::Postgres>,
 | 
					        E: sqlx::Executor<'e, Database = sqlx::Postgres>,
 | 
				
			||||||
@ -128,8 +128,7 @@ impl User {
 | 
				
			|||||||
        Ok(user)
 | 
					        Ok(user)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn update_profile(id: Option<i32>, pool: &sqlx::PgPool) -> Result<(User, Profile)>
 | 
					    pub async fn update_profile(id: Option<i32>, pool: &sqlx::PgPool) -> Result<(User, Profile)> {
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let prompt = "Select the user whose profile you want to update";
 | 
					        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 user = Self::get_user_by_id_or_select(id, prompt, pool).await?;
 | 
				
			||||||
        let profile = match user.get_profile(pool).await? {
 | 
					        let profile = match user.get_profile(pool).await? {
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,7 @@ pub fn generate_upsert_query(
 | 
				
			|||||||
    let field_idents: Vec<syn::Ident> = fields.iter().map(|f| f.ident.clone()).collect();
 | 
					    let field_idents: Vec<syn::Ident> = fields.iter().map(|f| f.ident.clone()).collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quote! {
 | 
					    quote! {
 | 
				
			||||||
        async fn create_or_update<'e, E>(&self, mut executor: E) -> ::sqlx::Result<Self>
 | 
					        async fn upsert<'e, E>(&self, mut executor: E) -> ::sqlx::Result<Self>
 | 
				
			||||||
        where
 | 
					        where
 | 
				
			||||||
            E: ::sqlx::Executor<'e, Database = ::sqlx::Postgres>
 | 
					            E: ::sqlx::Executor<'e, Database = ::sqlx::Postgres>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,83 +0,0 @@
 | 
				
			|||||||
pub trait Georm<Id> {
 | 
					 | 
				
			||||||
    /// 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<Output = ::sqlx::Result<Vec<Self>>> + 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<Output = sqlx::Result<Option<Self>>> + 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<Output = sqlx::Result<Self>> + 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<Output = sqlx::Result<Self>> + 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<Output = sqlx::Result<Self>> + 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<Output = sqlx::Result<u64>> + 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<Output = sqlx::Result<u64>> + Send;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns the identifier of the entity.
 | 
					 | 
				
			||||||
    fn get_id(&self) -> &Id;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										17
									
								
								src/georm.rs
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/georm.rs
									
									
									
									
									
								
							@ -26,7 +26,7 @@ use sqlx::{Executor, Postgres};
 | 
				
			|||||||
/// ### Instance Methods (Mutation Operations)
 | 
					/// ### Instance Methods (Mutation Operations)
 | 
				
			||||||
/// - [`create`] - Insert a new entity into the database
 | 
					/// - [`create`] - Insert a new entity into the database
 | 
				
			||||||
/// - [`update`] - Update an existing entity in 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
 | 
					/// - [`delete`] - Delete this entity from the database
 | 
				
			||||||
/// - [`get_id`] - Get the primary key of this entity
 | 
					/// - [`get_id`] - Get the primary key of this entity
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@ -92,7 +92,7 @@ use sqlx::{Executor, Postgres};
 | 
				
			|||||||
/// [`find`]: Georm::find
 | 
					/// [`find`]: Georm::find
 | 
				
			||||||
/// [`create`]: Georm::create
 | 
					/// [`create`]: Georm::create
 | 
				
			||||||
/// [`update`]: Georm::update
 | 
					/// [`update`]: Georm::update
 | 
				
			||||||
/// [`create_or_update`]: Georm::create_or_update
 | 
					/// [`upsert`]: Georm::upsert
 | 
				
			||||||
/// [`delete`]: Georm::delete
 | 
					/// [`delete`]: Georm::delete
 | 
				
			||||||
/// [`delete_by_id`]: Georm::delete_by_id
 | 
					/// [`delete_by_id`]: Georm::delete_by_id
 | 
				
			||||||
/// [`get_id`]: Georm::get_id
 | 
					/// [`get_id`]: Georm::get_id
 | 
				
			||||||
@ -264,7 +264,7 @@ pub trait Georm<Id> {
 | 
				
			|||||||
    /// # Examples
 | 
					    /// # Examples
 | 
				
			||||||
    /// ```ignore
 | 
					    /// ```ignore
 | 
				
			||||||
    /// let user = User { id: 1, username: "alice".into(), email: "alice@example.com".into() };
 | 
					    /// 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
 | 
					    /// // Will insert if ID 1 doesn't exist, update if it does
 | 
				
			||||||
    /// ```
 | 
					    /// ```
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
@ -273,13 +273,22 @@ pub trait Georm<Id> {
 | 
				
			|||||||
    /// - Non-primary-key constraint violations
 | 
					    /// - Non-primary-key constraint violations
 | 
				
			||||||
    /// - Database connection issues
 | 
					    /// - Database connection issues
 | 
				
			||||||
    /// - Permission problems
 | 
					    /// - Permission problems
 | 
				
			||||||
 | 
					    fn upsert<'e, E>(&self, executor: E) -> impl ::std::future::Future<Output = sqlx::Result<Self>>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        Self: Sized,
 | 
				
			||||||
 | 
					        E: Executor<'e, Database = Postgres>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[deprecated(since = "0.3.0", note = "Please use `upsert` instead")]
 | 
				
			||||||
    fn create_or_update<'e, E>(
 | 
					    fn create_or_update<'e, E>(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        executor: E,
 | 
					        executor: E,
 | 
				
			||||||
    ) -> impl ::std::future::Future<Output = sqlx::Result<Self>>
 | 
					    ) -> impl ::std::future::Future<Output = sqlx::Result<Self>>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        Self: Sized,
 | 
					        Self: Sized,
 | 
				
			||||||
        E: Executor<'e, Database = Postgres>;
 | 
					        E: Executor<'e, Database = Postgres>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        self.upsert(executor)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Delete this entity from the database.
 | 
					    /// Delete this entity from the database.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,7 @@
 | 
				
			|||||||
//! ### Instance Methods (called on entity objects)
 | 
					//! ### Instance Methods (called on entity objects)
 | 
				
			||||||
//! - `entity.create(pool)` - Insert new record, returns created entity with database-generated values
 | 
					//! - `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.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.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)
 | 
					//! - `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:
 | 
					//! 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)
 | 
					//! - **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
 | 
					//! - **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
 | 
					//! - **Compile-time verification**: SQLx macros verify all generated SQL against your database schema at compile time
 | 
				
			||||||
//!
 | 
					//!
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ fn composite_key_get_id() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[sqlx::test(fixtures("composite_key"))]
 | 
					#[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 {
 | 
					    let new_user_role = UserRole {
 | 
				
			||||||
        user_id: 5,
 | 
					        user_id: 5,
 | 
				
			||||||
        role_id: 2,
 | 
					        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
 | 
					    // 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!(5, result.user_id);
 | 
				
			||||||
    assert_eq!(2, result.role_id);
 | 
					    assert_eq!(2, result.role_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -52,7 +52,7 @@ async fn upsert_handles_generated_fields(pool: sqlx::PgPool) -> sqlx::Result<()>
 | 
				
			|||||||
    let mut modified_product = product.clone();
 | 
					    let mut modified_product = product.clone();
 | 
				
			||||||
    modified_product.price = BigDecimal::from(1200);
 | 
					    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
 | 
					    // price is updated
 | 
				
			||||||
    assert_eq!(upserted.price, BigDecimal::from(1200));
 | 
					    assert_eq!(upserted.price, BigDecimal::from(1200));
 | 
				
			||||||
 | 
				
			|||||||
@ -115,7 +115,7 @@ async fn should_create_if_does_not_exist(pool: sqlx::PgPool) -> sqlx::Result<()>
 | 
				
			|||||||
        name: "Miura Kentaro".into(),
 | 
					        name: "Miura Kentaro".into(),
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    author.create_or_update(&pool).await?;
 | 
					    author.upsert(&pool).await?;
 | 
				
			||||||
    let all_authors = Author::find_all(&pool).await?;
 | 
					    let all_authors = Author::find_all(&pool).await?;
 | 
				
			||||||
    assert_eq!(1, all_authors.len());
 | 
					    assert_eq!(1, all_authors.len());
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
@ -130,7 +130,7 @@ async fn should_update_if_exist(pool: sqlx::PgPool) -> sqlx::Result<()> {
 | 
				
			|||||||
        name: "Miura Kentaro".into(),
 | 
					        name: "Miura Kentaro".into(),
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    author.create_or_update(&pool).await?;
 | 
					    author.upsert(&pool).await?;
 | 
				
			||||||
    let mut all_authors = Author::find_all(&pool).await?;
 | 
					    let mut all_authors = Author::find_all(&pool).await?;
 | 
				
			||||||
    all_authors.sort();
 | 
					    all_authors.sort();
 | 
				
			||||||
    assert_eq!(3, all_authors.len());
 | 
					    assert_eq!(3, all_authors.len());
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user