use std::collections::HashMap;
use crate::control::planner::procedural::executor::bindings::RowBindings;
use crate::control::security::catalog::trigger_types::TriggerTiming;
use crate::control::security::identity::AuthenticatedIdentity;
use crate::control::state::SharedState;
use crate::types::TenantId;
use super::fire_common::{FireTriggersParams, check_cascade_depth, fire_triggers};
use super::registry::DmlEvent;
pub enum InsteadOfResult {
NoTrigger,
Handled,
}
pub async fn fire_instead_of_insert(
state: &SharedState,
identity: &AuthenticatedIdentity,
tenant_id: TenantId,
collection: &str,
new_fields: &HashMap<String, nodedb_types::Value>,
cascade_depth: u32,
) -> crate::Result<InsteadOfResult> {
let triggers =
state
.trigger_registry
.get_matching(tenant_id.as_u64(), collection, DmlEvent::Insert);
let instead_triggers: Vec<_> = triggers
.into_iter()
.filter(|t| t.timing == TriggerTiming::InsteadOf)
.collect();
if instead_triggers.is_empty() {
return Ok(InsteadOfResult::NoTrigger);
}
check_cascade_depth(cascade_depth, collection)?;
let bindings = RowBindings::before_insert(collection, new_fields.clone());
fire_triggers(FireTriggersParams {
state,
identity,
tenant_id,
collection,
triggers: &instead_triggers,
bindings: &bindings,
cascade_depth,
cross_shard_origin: None,
})
.await?;
Ok(InsteadOfResult::Handled)
}
pub struct InsteadOfUpdateParams<'a> {
pub state: &'a SharedState,
pub identity: &'a AuthenticatedIdentity,
pub tenant_id: TenantId,
pub collection: &'a str,
pub old_fields: &'a HashMap<String, nodedb_types::Value>,
pub new_fields: &'a HashMap<String, nodedb_types::Value>,
pub cascade_depth: u32,
}
pub async fn fire_instead_of_update(
params: InsteadOfUpdateParams<'_>,
) -> crate::Result<InsteadOfResult> {
let InsteadOfUpdateParams {
state,
identity,
tenant_id,
collection,
old_fields,
new_fields,
cascade_depth,
} = params;
let triggers =
state
.trigger_registry
.get_matching(tenant_id.as_u64(), collection, DmlEvent::Update);
let instead_triggers: Vec<_> = triggers
.into_iter()
.filter(|t| t.timing == TriggerTiming::InsteadOf)
.collect();
if instead_triggers.is_empty() {
return Ok(InsteadOfResult::NoTrigger);
}
check_cascade_depth(cascade_depth, collection)?;
let bindings = RowBindings::before_update(collection, old_fields.clone(), new_fields.clone());
fire_triggers(FireTriggersParams {
state,
identity,
tenant_id,
collection,
triggers: &instead_triggers,
bindings: &bindings,
cascade_depth,
cross_shard_origin: None,
})
.await?;
Ok(InsteadOfResult::Handled)
}
pub async fn fire_instead_of_delete(
state: &SharedState,
identity: &AuthenticatedIdentity,
tenant_id: TenantId,
collection: &str,
old_fields: &HashMap<String, nodedb_types::Value>,
cascade_depth: u32,
) -> crate::Result<InsteadOfResult> {
let triggers =
state
.trigger_registry
.get_matching(tenant_id.as_u64(), collection, DmlEvent::Delete);
let instead_triggers: Vec<_> = triggers
.into_iter()
.filter(|t| t.timing == TriggerTiming::InsteadOf)
.collect();
if instead_triggers.is_empty() {
return Ok(InsteadOfResult::NoTrigger);
}
check_cascade_depth(cascade_depth, collection)?;
let bindings = RowBindings::before_delete(collection, old_fields.clone());
fire_triggers(FireTriggersParams {
state,
identity,
tenant_id,
collection,
triggers: &instead_triggers,
bindings: &bindings,
cascade_depth,
cross_shard_origin: None,
})
.await?;
Ok(InsteadOfResult::Handled)
}