use std::collections::HashMap;
use crate::control::security::catalog::trigger_types::TriggerExecutionMode;
use crate::control::security::identity::AuthenticatedIdentity;
use crate::control::state::SharedState;
use crate::types::TenantId;
use super::dml_hook::DmlWriteInfo;
use super::fire_after;
use super::fire_before;
use super::fire_instead::InsteadOfResult;
use super::fire_statement;
use super::registry::DmlEvent;
pub enum PreDispatchResult {
Handled,
Proceed {
mutated_fields: Option<HashMap<String, nodedb_types::Value>>,
},
}
pub struct DispatchTriggerParams<'a> {
pub state: &'a SharedState,
pub identity: &'a AuthenticatedIdentity,
pub tenant_id: TenantId,
pub info: &'a DmlWriteInfo,
pub old_row: &'a Option<HashMap<String, nodedb_types::Value>>,
pub cascade_depth: u32,
}
pub async fn fire_pre_dispatch_triggers(
params: DispatchTriggerParams<'_>,
) -> crate::Result<PreDispatchResult> {
let DispatchTriggerParams {
state,
identity,
tenant_id,
info,
old_row,
cascade_depth,
} = params;
match info.event {
DmlEvent::Insert => {
if let Some(ref new_fields) = info.new_fields {
match super::fire_instead::fire_instead_of_insert(
state,
identity,
tenant_id,
&info.collection,
new_fields,
cascade_depth,
)
.await?
{
InsteadOfResult::Handled => return Ok(PreDispatchResult::Handled),
InsteadOfResult::NoTrigger => {}
}
}
}
DmlEvent::Update => {
let empty = HashMap::new();
let old_fields = old_row.as_ref().unwrap_or(&empty);
let new_fields = info.new_fields.as_ref().unwrap_or(&empty);
match super::fire_instead::fire_instead_of_update(
super::fire_instead::InsteadOfUpdateParams {
state,
identity,
tenant_id,
collection: &info.collection,
old_fields,
new_fields,
cascade_depth,
},
)
.await?
{
InsteadOfResult::Handled => return Ok(PreDispatchResult::Handled),
InsteadOfResult::NoTrigger => {}
}
}
DmlEvent::Delete => {
let empty = HashMap::new();
let old_fields = old_row.as_ref().unwrap_or(&empty);
match super::fire_instead::fire_instead_of_delete(
state,
identity,
tenant_id,
&info.collection,
old_fields,
cascade_depth,
)
.await?
{
InsteadOfResult::Handled => return Ok(PreDispatchResult::Handled),
InsteadOfResult::NoTrigger => {}
}
}
}
let mutated_fields = match info.event {
DmlEvent::Insert => {
if let Some(ref new_fields) = info.new_fields {
let mutated = fire_before::fire_before_insert(
state,
identity,
tenant_id,
&info.collection,
new_fields,
cascade_depth,
)
.await?;
if mutated != *new_fields {
Some(mutated)
} else {
None
}
} else {
None
}
}
DmlEvent::Update => {
let empty = HashMap::new();
let old_fields = old_row.as_ref().unwrap_or(&empty);
let new_fields = info.new_fields.as_ref().unwrap_or(&empty);
let mutated = fire_before::fire_before_update(
state,
identity,
tenant_id,
&info.collection,
old_fields,
new_fields,
cascade_depth,
)
.await?;
if mutated != *new_fields {
Some(mutated)
} else {
None
}
}
DmlEvent::Delete => {
let empty = HashMap::new();
let old_fields = old_row.as_ref().unwrap_or(&empty);
fire_before::fire_before_delete(
state,
identity,
tenant_id,
&info.collection,
old_fields,
cascade_depth,
)
.await?;
None
}
};
Ok(PreDispatchResult::Proceed { mutated_fields })
}
pub async fn fire_post_dispatch_triggers(params: DispatchTriggerParams<'_>) -> crate::Result<()> {
let DispatchTriggerParams {
state,
identity,
tenant_id,
info,
old_row,
cascade_depth,
} = params;
let empty = HashMap::new();
match info.event {
DmlEvent::Insert => {
if let Some(ref new_fields) = info.new_fields {
fire_after::fire_after_insert(fire_after::FireAfterInsertParams {
state,
identity,
tenant_id,
collection: &info.collection,
new_fields,
cascade_depth,
mode_filter: Some(TriggerExecutionMode::Sync),
cross_shard_origin: None,
})
.await?;
}
}
DmlEvent::Update => {
let old_fields = old_row.as_ref().unwrap_or(&empty);
let new_fields = info.new_fields.as_ref().unwrap_or(&empty);
fire_after::fire_after_update(fire_after::FireAfterUpdateParams {
state,
identity,
tenant_id,
collection: &info.collection,
old_fields,
new_fields,
cascade_depth,
mode_filter: Some(TriggerExecutionMode::Sync),
cross_shard_origin: None,
})
.await?;
}
DmlEvent::Delete => {
let old_fields = old_row.as_ref().unwrap_or(&empty);
fire_after::fire_after_delete(fire_after::FireAfterDeleteParams {
state,
identity,
tenant_id,
collection: &info.collection,
old_fields,
cascade_depth,
mode_filter: Some(TriggerExecutionMode::Sync),
cross_shard_origin: None,
})
.await?;
}
}
fire_statement::fire_after_statement(
state,
identity,
tenant_id,
&info.collection,
info.event,
cascade_depth,
Some(TriggerExecutionMode::Sync),
)
.await?;
Ok(())
}