safe-migrate 0.4.4

Analyze PostgreSQL migrations for schema and locking risks
Documentation
// FILE: src/analysis/transaction.rs

use crate::analysis::graph::DependencyEdge;
use crate::ast::identifiers::ObjectId;
use crate::model::relation::RelationOverlay;
use crate::model::sequence::SequenceOverlay;
use crate::model::types::TypeOverlay;
use std::collections::HashSet;

#[derive(Debug, Clone)]
pub enum StateChange {
    RelationSnapshot {
        id: ObjectId,
        previous: Box<Option<RelationOverlay>>,
    },
    TypeSnapshot {
        id: ObjectId,
        previous: Option<TypeOverlay>,
    },
    SequenceSnapshot {
        id: ObjectId,
        previous: Option<SequenceOverlay>,
    },
    SearchPathSnapshot {
        previous: Vec<String>,
        previous_template: Vec<String>,
    },
    GenerationCounterSnapshot {
        previous: u64,
    },
    PendingValidationSnapshot {
        previous: HashSet<(ObjectId, String)>,
    },
    GraphLengthMarker {
        len: usize,
    },
    GraphSnapshot {
        previous: Vec<DependencyEdge>,
    },
    FunctionSnapshot {
        id: ObjectId,
        previous: Option<crate::model::function::FunctionOverlay>,
    },
    PublicationSnapshot {
        id: ObjectId,
        previous: Option<crate::model::replication::PublicationOverlay>,
    },
    SubscriptionSnapshot {
        id: ObjectId,
        previous: Option<crate::model::replication::SubscriptionOverlay>,
    },
    RoleSnapshot {
        id: ObjectId,
        previous: Option<crate::model::role::RoleOverlay>,
    },
    TriggerSnapshot {
        id: ObjectId,
        previous: Option<crate::model::trigger::TriggerOverlay>,
    },
    ConstraintSnapshot {
        table_id: ObjectId,
        name: String,
        previous: Option<crate::model::constraint::ConstraintState>,
    },
    RoleContextSnapshot {
        current_role: String,
        current_role_known: bool,
        persistent_current_role: String,
        persistent_current_role_known: bool,
        session_role: String,
        session_role_known: bool,
        persistent_session_role: String,
        persistent_session_role_known: bool,
    },
    ConfidenceSnapshot {
        previous: crate::analysis::state::Confidence,
    },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransactionFrameKind {
    Root,
    Savepoint(String),
}

#[derive(Debug, Clone)]
pub struct TransactionFrame {
    pub kind: TransactionFrameKind,
    pub undo_log: Vec<StateChange>,
}

impl TransactionFrame {
    pub fn root() -> Self {
        Self {
            kind: TransactionFrameKind::Root,
            undo_log: Vec::new(),
        }
    }

    pub fn savepoint(name: impl Into<String>) -> Self {
        Self {
            kind: TransactionFrameKind::Savepoint(name.into()),
            undo_log: Vec::new(),
        }
    }

    pub fn is_named_savepoint(&self, name: &str) -> bool {
        matches!(&self.kind, TransactionFrameKind::Savepoint(candidate) if candidate == name)
    }
}