use crate::analysis::mutations::Mutation;
use crate::analysis::state::{AnalysisState, CascadeResult, MutationResult};
use crate::engine::config::Config;
use crate::report::violations::{ObjectKind, OperationKind, Violation, ViolationTier};
use crate::rules::Rule;
pub struct OpaqueDynamicSqlRule;
impl Rule for OpaqueDynamicSqlRule {
fn id(&self) -> &'static str {
"opaque-dynamic-sql"
}
fn default_tier(&self) -> ViolationTier {
ViolationTier::Tier2
}
fn recipe(&self) -> &'static str {
"Procedural or dynamic SQL (DO blocks, EXECUTE) obscures schema mutations. Lock analysis confidence is heavily degraded."
}
fn evaluate(
&self,
mutation: &Mutation,
_result: &MutationResult,
_pre_state: &crate::analysis::state::PreState,
_state: &AnalysisState,
_config: &Config,
_cascade: Option<&CascadeResult>,
) -> Vec<Violation> {
let mut violations = Vec::new();
if let Mutation::Opaque(op) = mutation {
if matches!(
op,
crate::analysis::mutations::OpaqueMutation::UnresolvedReference { .. }
) {
return vec![];
}
let block_type = match op {
crate::analysis::mutations::OpaqueMutation::DoBlock => "DO block",
crate::analysis::mutations::OpaqueMutation::Execute => "EXECUTE statement",
crate::analysis::mutations::OpaqueMutation::DynamicSql => "Dynamic SQL",
crate::analysis::mutations::OpaqueMutation::PrepareTransaction => {
"PREPARE TRANSACTION"
}
crate::analysis::mutations::OpaqueMutation::SetTransaction => "SET TRANSACTION",
crate::analysis::mutations::OpaqueMutation::SetConstraints => "SET CONSTRAINTS",
crate::analysis::mutations::OpaqueMutation::UnresolvedReference { .. } => {
unreachable!()
}
};
violations.push(Violation {
source_range: None,
rule_id: self.id(),
operation_kind: OperationKind::OpaqueSql,
object_kind: ObjectKind::Unknown,
object_name: "<dynamic>".to_string(),
tier: self.default_tier(),
reason: format!("Encountered opaque {}", block_type),
recipe: self.recipe(),
dedup_key: None,
sql: None,
});
}
violations
}
}