diff --git a/eth-bytecode-db/verifier-alliance-database/src/helpers.rs b/eth-bytecode-db/verifier-alliance-database/src/helpers.rs index 3df290d42..ee1801e73 100644 --- a/eth-bytecode-db/verifier-alliance-database/src/helpers.rs +++ b/eth-bytecode-db/verifier-alliance-database/src/helpers.rs @@ -1,63 +1,91 @@ -macro_rules! insert_then_select { - ( $txn:expr, $entity_module:ident, $active_model:expr, $update_on_conflict:expr, [ $( ($column:ident, $value:expr) ),+ $(,)? ] ) => { - { - use anyhow::Context; - use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use anyhow::Context; +use sea_orm::{ + sea_query::OnConflict, ActiveModelBehavior, ActiveModelTrait, ColumnTrait, ConnectionTrait, + DbErr, EntityTrait, IntoActiveModel, ModelTrait, PrimaryKeyToColumn, QueryFilter, +}; - let result: Result<_, sea_orm::DbErr> = $entity_module::Entity::insert($active_model.clone()) - .on_conflict(sea_orm::sea_query::OnConflict::new().do_nothing().to_owned()) - .exec($txn) - .await; +pub async fn insert_then_select( + txn: &C, + entity: Entity, + active_model: ActiveModel, + unique_columns: impl IntoIterator, +) -> Result<(Entity::Model, bool), anyhow::Error> +where + C: ConnectionTrait, + Entity: EntityTrait, + ActiveModel: ActiveModelTrait + ActiveModelBehavior + Send, + ::Model: IntoActiveModel, +{ + insert_then_select_internal(txn, entity, active_model, unique_columns, false).await +} - // Returns the model and the bool flag showing whether the model was actually inserted. - match result { - Ok(res) => { - let last_insert_id = res.last_insert_id; - let model = $entity_module::Entity::find_by_id(last_insert_id.clone()) - .one($txn) - .await - .context(format!("select from \"{}\" by \"id\"", stringify!($entity_module)))? - .ok_or(anyhow::anyhow!( - "select from \"{}\" by \"id\"={:?} returned no data", - stringify!($entity_module), - last_insert_id - ))?; +async fn insert_then_select_internal( + txn: &C, + entity: Entity, + active_model: ActiveModel, + unique_columns: impl IntoIterator, + update_on_conflict: bool, +) -> Result<(Entity::Model, bool), anyhow::Error> +where + C: ConnectionTrait, + Entity: EntityTrait, + ActiveModel: ActiveModelTrait + ActiveModelBehavior + Send, + ::Model: IntoActiveModel, +{ + let entity_table_name = entity.table_name(); - Ok((model, true)) - } - Err(sea_orm::DbErr::RecordNotInserted) => { - let mut model = - $entity_module::Entity::find() - $( - .filter($entity_module::Column::$column.eq($value)) - )* - .one($txn) - .await - .context(format!("select from \"{}\" by unique columns", stringify!($entity_module)))? - .ok_or(anyhow::anyhow!("select from \"{}\" by unique columns returned no data", stringify!($entity_module)))?; - // The active model have not been inserted. - // Thus, there were a value already that we need to update. - if $update_on_conflict { - let mut active_model_to_update = $active_model; - for primary_key in <$entity_module::PrimaryKey as sea_orm::Iterable>::iter() { - let column = sea_orm::PrimaryKeyToColumn::into_column(primary_key); - let value = sea_orm::ModelTrait::get(&model, column); - sea_orm::ActiveModelTrait::set(&mut active_model_to_update, column, value); - } - let updated_model = sea_orm::ActiveModelTrait::update( - active_model_to_update, $txn - ).await.context(format!("update on conflict in \"{}\"", stringify!($entity_module)))?; + let result: Result<_, DbErr> = Entity::insert(active_model.clone()) + .on_conflict(OnConflict::new().do_nothing().to_owned()) + .exec(txn) + .await; + + // Returns the model and the bool flag showing whether the model was actually inserted. + match result { + Ok(res) => { + let last_insert_id = res.last_insert_id; + let id_debug_str = format!("{last_insert_id:?}"); + let model = Entity::find_by_id(last_insert_id) + .one(txn) + .await + .context(format!("select from \"{entity_table_name}\" by \"id\""))? + .ok_or(anyhow::anyhow!( + "select from \"{entity_table_name}\" by \"id\"={id_debug_str} returned no data" + ))?; - if updated_model != model { - model = updated_model; - } - } + Ok((model, true)) + } + Err(DbErr::RecordNotInserted) => { + let mut query = Entity::find(); + for (column, value) in unique_columns { + query = query.filter(column.eq(value)); + } + let mut model = query + .one(txn) + .await + .context(format!( + "select from \"{entity_table_name}\" by unique columns" + ))? + .ok_or(anyhow::anyhow!( + "select from \"{entity_table_name}\" by unique columns returned no data" + ))?; - Ok((model, false)) + // The active model have not been inserted. + // Thus, there were a value already that we need to update. + if update_on_conflict { + let mut active_model_to_update = active_model; + for primary_key in ::iter() { + let column = PrimaryKeyToColumn::into_column(primary_key); + let value = ModelTrait::get(&model, column); + ActiveModelTrait::set(&mut active_model_to_update, column, value); } - Err(err) => Err(err).context(format!("insert into \"{}\"", stringify!($entity_module))), + model = active_model_to_update + .update(txn) + .await + .context(format!("update on conflict in \"{entity_table_name}\""))?; } + + Ok((model, false)) } - }; + Err(err) => Err(err).context(format!("insert into \"{entity_table_name}\"")), + } } -pub(crate) use insert_then_select; diff --git a/eth-bytecode-db/verifier-alliance-database/src/internal.rs b/eth-bytecode-db/verifier-alliance-database/src/internal.rs index 19e192c5f..abc18d5ec 100644 --- a/eth-bytecode-db/verifier-alliance-database/src/internal.rs +++ b/eth-bytecode-db/verifier-alliance-database/src/internal.rs @@ -1,4 +1,4 @@ -use crate::helpers::insert_then_select; +use crate::helpers; use anyhow::{anyhow, Context, Error}; use blockscout_display_bytes::ToHex; use sea_orm::{ @@ -92,16 +92,17 @@ pub async fn insert_verified_contract( runtime_metadata_match: Set(runtime_match_data.metadata_match), }; - let (model, _inserted) = insert_then_select!( + use verified_contracts::Column; + let (model, _inserted) = helpers::insert_then_select( database_connection, - verified_contracts, + verified_contracts::Entity, active_model, - false, [ - (CompilationId, compiled_contract_id), - (DeploymentId, contract_deployment_id), - ] - )?; + (Column::CompilationId, compiled_contract_id.into()), + (Column::DeploymentId, contract_deployment_id.into()), + ], + ) + .await?; Ok(model) } @@ -138,18 +139,25 @@ pub async fn insert_compiled_contract( runtime_code_artifacts: Set(compiled_contract.runtime_code_artifacts.into()), }; - let (model, _inserted) = insert_then_select!( + use compiled_contracts::Column; + let (model, _inserted) = helpers::insert_then_select( database_connection, - compiled_contracts, + compiled_contracts::Entity, active_model, - false, [ - (Compiler, compiled_contract.compiler.to_string()), - (Language, compiled_contract.language.to_string()), - (CreationCodeHash, creation_code_hash), - (RuntimeCodeHash, runtime_code_hash) - ] - )?; + ( + Column::Compiler, + compiled_contract.compiler.to_string().into(), + ), + ( + Column::Language, + compiled_contract.language.to_string().into(), + ), + (Column::CreationCodeHash, creation_code_hash.into()), + (Column::RuntimeCodeHash, runtime_code_hash.into()), + ], + ) + .await?; Ok(model) } @@ -172,13 +180,13 @@ pub async fn insert_sources( created_by: Default::default(), updated_by: Default::default(), }; - let (model, _inserted) = insert_then_select!( + let (model, _inserted) = helpers::insert_then_select( database_connection, - sources, + sources::Entity, active_model, - false, - [(SourceHash, source_hash)] - )?; + [(sources::Column::SourceHash, source_hash.into())], + ) + .await?; models.push(model) } @@ -199,13 +207,17 @@ pub async fn insert_compiled_contract_sources( path: Set(path.clone()), source_hash: Set(source_hash), }; - let (model, _inserted) = insert_then_select!( + use compiled_contracts_sources::Column; + let (model, _inserted) = helpers::insert_then_select( database_connection, - compiled_contracts_sources, + compiled_contracts_sources::Entity, active_model, - false, - [(CompilationId, compiled_contract_id), (Path, path)] - )?; + [ + (Column::CompilationId, compiled_contract_id.into()), + (Column::Path, path.into()), + ], + ) + .await?; models.push(model); } @@ -232,17 +244,21 @@ pub async fn insert_contract_deployment( contract_id: Set(contract_id), }; - let (model, _inserted) = insert_then_select!( + use contract_deployments::Column; + let (model, _inserted) = helpers::insert_then_select( database_connection, - contract_deployments, + contract_deployments::Entity, active_model, - false, [ - (ChainId, internal_data.chain_id), - (Address, internal_data.address), - (TransactionHash, internal_data.transaction_hash) - ] - )?; + (Column::ChainId, internal_data.chain_id.into()), + (Column::Address, internal_data.address.into()), + ( + Column::TransactionHash, + internal_data.transaction_hash.into(), + ), + ], + ) + .await?; Ok(model) } @@ -286,16 +302,17 @@ pub async fn insert_contract( runtime_code_hash: Set(runtime_code_hash.clone()), }; - let (model, _inserted) = insert_then_select!( + use contracts::Column; + let (model, _inserted) = helpers::insert_then_select( database_connection, - contracts, + contracts::Entity, active_model, - false, [ - (CreationCodeHash, creation_code_hash), - (RuntimeCodeHash, runtime_code_hash) - ] - )?; + (Column::CreationCodeHash, creation_code_hash.into()), + (Column::RuntimeCodeHash, runtime_code_hash.into()), + ], + ) + .await?; Ok(model) } @@ -317,13 +334,13 @@ pub async fn insert_code( code: Set(Some(code)), }; - let (model, _inserted) = insert_then_select!( + let (model, _inserted) = helpers::insert_then_select( database_connection, - code, + code::Entity, active_model, - false, - [(CodeHash, code_hash)] - )?; + [(code::Column::CodeHash, code_hash.into())], + ) + .await?; Ok(model) }