Skip to content

Commit

Permalink
refactor(contracts-info): make insert_with_select to be a function in…
Browse files Browse the repository at this point in the history
…stead of macro
  • Loading branch information
rimrakhimov committed Dec 12, 2024
1 parent 51c437e commit da2750c
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 101 deletions.
136 changes: 82 additions & 54 deletions eth-bytecode-db/verifier-alliance-database/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -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<C, Entity, ActiveModel>(
txn: &C,
entity: Entity,
active_model: ActiveModel,
unique_columns: impl IntoIterator<Item = (Entity::Column, sea_orm::Value)>,
) -> Result<(Entity::Model, bool), anyhow::Error>
where
C: ConnectionTrait,
Entity: EntityTrait,
ActiveModel: ActiveModelTrait<Entity = Entity> + ActiveModelBehavior + Send,
<Entity as EntityTrait>::Model: IntoActiveModel<ActiveModel>,
{
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<C, Entity, ActiveModel>(
txn: &C,
entity: Entity,
active_model: ActiveModel,
unique_columns: impl IntoIterator<Item = (Entity::Column, sea_orm::Value)>,
update_on_conflict: bool,
) -> Result<(Entity::Model, bool), anyhow::Error>
where
C: ConnectionTrait,
Entity: EntityTrait,
ActiveModel: ActiveModelTrait<Entity = Entity> + ActiveModelBehavior + Send,
<Entity as EntityTrait>::Model: IntoActiveModel<ActiveModel>,
{
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 <Entity::PrimaryKey as sea_orm::Iterable>::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;
111 changes: 64 additions & 47 deletions eth-bytecode-db/verifier-alliance-database/src/internal.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -92,16 +92,17 @@ pub async fn insert_verified_contract<C: ConnectionTrait>(
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)
}
Expand Down Expand Up @@ -138,18 +139,25 @@ pub async fn insert_compiled_contract<C: ConnectionTrait>(
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)
}
Expand All @@ -172,13 +180,13 @@ pub async fn insert_sources<C: ConnectionTrait>(
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)
}

Expand All @@ -199,13 +207,17 @@ pub async fn insert_compiled_contract_sources<C: ConnectionTrait>(
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);
}

Expand All @@ -232,17 +244,21 @@ pub async fn insert_contract_deployment<C: ConnectionTrait>(
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)
}
Expand Down Expand Up @@ -286,16 +302,17 @@ pub async fn insert_contract<C: ConnectionTrait>(
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)
}
Expand All @@ -317,13 +334,13 @@ pub async fn insert_code<C: ConnectionTrait>(
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)
}
Expand Down

0 comments on commit da2750c

Please sign in to comment.