Skip to main content

safe_migrate/rules/
opaque.rs

1// FILE: src/rules/opaque.rs
2use crate::analysis::mutations::Mutation;
3use crate::analysis::state::{AnalysisState, CascadeResult, MutationResult};
4use crate::ast::identifiers::ObjectId;
5use crate::engine::config::Config;
6use crate::model::relation::RelationState;
7use crate::report::violations::{Violation, ViolationTier};
8use crate::rules::Rule;
9use std::collections::HashMap;
10
11pub struct OpaqueDynamicSqlRule;
12
13impl Rule for OpaqueDynamicSqlRule {
14    fn id(&self) -> &'static str {
15        "opaque-dynamic-sql"
16    }
17    fn default_tier(&self) -> ViolationTier {
18        ViolationTier::Tier2
19    }
20    fn recipe(&self) -> &'static str {
21        "Procedural or dynamic SQL (DO blocks, EXECUTE) obscures schema mutations. Lock analysis confidence is heavily degraded."
22    }
23
24    fn evaluate(
25        &self,
26        mutation: &Mutation,
27        _result: &MutationResult,
28        _pre_relations: &HashMap<ObjectId, RelationState>,
29        _state: &AnalysisState,
30        _config: &Config,
31        _cascade: Option<&CascadeResult>,
32    ) -> Vec<Violation> {
33        let mut violations = Vec::new();
34
35        if let Mutation::Opaque(op) = mutation {
36            let block_type = match op {
37                crate::analysis::mutations::OpaqueMutation::DoBlock => "DO block",
38                crate::analysis::mutations::OpaqueMutation::Execute => "EXECUTE statement",
39                crate::analysis::mutations::OpaqueMutation::DynamicSql => "Dynamic SQL",
40                crate::analysis::mutations::OpaqueMutation::PrepareTransaction => {
41                    "PREPARE TRANSACTION"
42                }
43                crate::analysis::mutations::OpaqueMutation::SetTransaction => "SET TRANSACTION",
44                crate::analysis::mutations::OpaqueMutation::SetConstraints => "SET CONSTRAINTS",
45            };
46
47            violations.push(Violation {
48                rule_id: self.id(),
49                title: format!("Encountered opaque {}", block_type),
50                tier: self.default_tier(),
51                recipe: self.recipe(),
52                dedup_key: None,
53            });
54        }
55
56        violations
57    }
58}