Skip to main content

soma_schema/
driver.rs

1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3
4use crate::error::Result;
5use crate::migration::Migration;
6
7/// A deployed migration row from the tracking table.
8#[non_exhaustive]
9#[derive(Debug)]
10pub struct AppliedMigration {
11    pub version: u32,
12    pub file: String,
13    pub name: String,
14    pub checksum: String,
15    pub description: Option<String>,
16    pub batch: i32,
17    pub applied_at: DateTime<Utc>,
18    pub applied_by: String,
19    pub execution_ms: Option<i32>,
20}
21
22impl AppliedMigration {
23    /// Construct a tracking-table row.
24    ///
25    /// Driver implementations use this inside `applied()` to build rows from
26    /// whatever backing store they query. The `#[non_exhaustive]` attribute on
27    /// the struct means struct-literal syntax is unavailable to external crates;
28    /// this constructor is the supported path for building `AppliedMigration` values.
29    #[allow(clippy::too_many_arguments)]
30    pub fn new(
31        version: u32,
32        file: String,
33        name: String,
34        checksum: String,
35        description: Option<String>,
36        batch: i32,
37        applied_at: DateTime<Utc>,
38        applied_by: String,
39        execution_ms: Option<i32>,
40    ) -> Self {
41        Self {
42            version,
43            file,
44            name,
45            checksum,
46            description,
47            batch,
48            applied_at,
49            applied_by,
50            execution_ms,
51        }
52    }
53}
54
55/// A lock guard. Releases the advisory lock on Drop.
56/// The concrete implementation keeps a dedicated connection alive.
57pub trait LockGuard: Send + Sync {}
58
59/// Object-safe driver trait. Implement for each database backend.
60#[async_trait]
61pub trait MigrationDriver: Send + Sync {
62    /// Acquire an advisory lock. The lock is held until the returned guard is dropped.
63    async fn acquire_lock(&self) -> Result<Box<dyn LockGuard>>;
64
65    /// Execute one 00_setup file's SQL in a single transaction (untracked).
66    async fn run_setup_sql(&self, name: &str, sql: &str) -> Result<()>;
67
68    /// Ensure the deployment tracking table (and schema, if configured) exist.
69    async fn ensure_tracking_table(&self) -> Result<()>;
70
71    /// Return all applied migrations, ordered by (version ASC, file ASC).
72    async fn applied(&self) -> Result<Vec<AppliedMigration>>;
73
74    /// Apply a migration: run UP SQL AND insert the tracking row in ONE transaction.
75    /// The `up_sql` is passed in rather than re-reading the file in the driver.
76    async fn apply(&self, migration: &Migration, up_sql: &str, batch: i32) -> Result<()>;
77
78    /// Revert an applied migration: run `down_sql` AND delete the tracking row in ONE transaction.
79    async fn revert(&self, applied: &AppliedMigration, down_sql: &str) -> Result<()>;
80}