Skip to main content

evento_sql_migrator/
lib.rs

1//! SQL database migrations for the Evento event sourcing library.
2//!
3//! This crate provides database schema migrations required for storing events, snapshots,
4//! and subscriber state in SQL databases. It supports SQLite, MySQL, and PostgreSQL through
5//! feature flags.
6//!
7//! # Features
8//!
9//! - **`sqlite`** - Enables SQLite database support
10//! - **`mysql`** - Enables MySQL database support
11//! - **`postgres`** - Enables PostgreSQL database support
12//!
13//! All features are enabled by default. You can selectively enable only the databases you need:
14//!
15//! ```toml
16//! [dependencies]
17//! evento-sql-migrator = { version = "1.8", default-features = false, features = ["postgres"] }
18//! ```
19//!
20//! # Usage
21//!
22//! The main entry point is the [`new`] function, which creates a [`Migrator`]
23//! instance configured with all Evento migrations.
24//!
25//! ```rust,ignore
26//! use sqlx_migrator::{Migrate, Plan};
27//!
28//! // Acquire a database connection
29//! let mut conn = pool.acquire().await?;
30//!
31//! // Create the migrator for your database type
32//! let migrator = evento_sql_migrator::new::<sqlx::Sqlite>()?;
33//!
34//! // Run all pending migrations
35//! migrator.run(&mut *conn, &Plan::apply_all()).await?;
36//! ```
37//!
38//! When using the main `evento` crate, the migrator is re-exported:
39//!
40//! ```rust,ignore
41//! let migrator = evento::sql_migrator::new::<sqlx::Sqlite>()?;
42//! ```
43//!
44//! # Migrations
45//!
46//! The crate includes the following migrations:
47//!
48//! - [`InitMigration`] - Creates the initial database schema (event, snapshot, subscriber tables)
49//! - [`M0002`] - Adds `timestamp_subsec` column for sub-second precision timestamps
50//! - [`M0003`] - Drops the snapshot table and extends the event name column length
51//! - [`M0004`] - Replaces `idx_event_type` with a composite cursor-scan index
52//! - [`M0005`] - Adds a leading-cursor index for no-routing-key subscription scans
53//!
54//! # Database Schema
55//!
56//! After running all migrations, the database will contain:
57//!
58//! ## Event Table
59//!
60//! Stores all domain events:
61//!
62//! | Column | Type | Description |
63//! |--------|------|-------------|
64//! | `id` | VARCHAR(26) | Event ID (ULID format) |
65//! | `name` | VARCHAR(50) | Event type name |
66//! | `aggregator_type` | VARCHAR(50) | Aggregate root type |
67//! | `aggregator_id` | VARCHAR(26) | Aggregate root instance ID |
68//! | `version` | INTEGER | Event sequence number |
69//! | `data` | BLOB | Serialized event data |
70//! | `metadata` | BLOB | Serialized event metadata |
71//! | `routing_key` | VARCHAR(50) | Optional routing key |
72//! | `timestamp` | BIGINT | Event timestamp (seconds) |
73//! | `timestamp_subsec` | BIGINT | Sub-second precision |
74//!
75//! ## Subscriber Table
76//!
77//! Tracks event subscription progress:
78//!
79//! | Column | Type | Description |
80//! |--------|------|-------------|
81//! | `key` | VARCHAR(50) | Subscriber identifier (primary key) |
82//! | `worker_id` | VARCHAR(26) | Associated worker ID |
83//! | `cursor` | TEXT | Current event stream position |
84//! | `lag` | INTEGER | Subscription lag counter |
85//! | `enabled` | BOOLEAN | Whether subscription is active |
86//! | `created_at` | TIMESTAMP | Creation timestamp |
87//! | `updated_at` | TIMESTAMP | Last update timestamp |
88
89use sqlx_migrator::{Info, Migrator};
90
91#[cfg(feature = "accord")]
92mod accord;
93mod m0001;
94mod m0002;
95mod m0003;
96mod m0004;
97mod m0005;
98
99#[cfg(feature = "accord")]
100pub use accord::AccordMigration;
101pub use m0001::InitMigration;
102pub use m0002::M0002;
103pub use m0003::M0003;
104pub use m0004::M0004;
105pub use m0005::M0005;
106
107/// Creates a new [`Migrator`] instance with all Evento migrations registered.
108///
109/// The migrator is generic over the database type and works with SQLite, MySQL, and PostgreSQL
110/// when the corresponding feature is enabled.
111///
112/// # Example
113///
114/// ```rust,ignore
115/// use sqlx_migrator::{Migrate, Plan};
116///
117/// // For SQLite
118/// let migrator = evento_sql_migrator::new::<sqlx::Sqlite>()?;
119///
120/// // For PostgreSQL
121/// let migrator = evento_sql_migrator::new::<sqlx::Postgres>()?;
122///
123/// // For MySQL
124/// let migrator = evento_sql_migrator::new::<sqlx::MySql>()?;
125///
126/// // Run migrations
127/// migrator.run(&mut *conn, &Plan::apply_all()).await?;
128/// ```
129///
130/// # Errors
131///
132/// Returns an error if migration registration fails.
133// Two definitions: the `accord` build adds the consensus-journal migration (and its
134// extra trait bound); the default build is unchanged — so the `AccordMigration` bound
135// never leaks onto callers (e.g. evento-sql's generic test harness) that don't opt in.
136#[cfg(not(feature = "accord"))]
137pub fn new<DB: sqlx::Database>() -> Result<Migrator<DB>, sqlx_migrator::Error>
138where
139    InitMigration: sqlx_migrator::Migration<DB>,
140    M0002: sqlx_migrator::Migration<DB>,
141    M0003: sqlx_migrator::Migration<DB>,
142    M0004: sqlx_migrator::Migration<DB>,
143    M0005: sqlx_migrator::Migration<DB>,
144{
145    let mut migrator = Migrator::default();
146    migrator.add_migration(Box::new(InitMigration))?;
147    migrator.add_migration(Box::new(M0002))?;
148    migrator.add_migration(Box::new(M0003))?;
149    migrator.add_migration(Box::new(M0004))?;
150    migrator.add_migration(Box::new(M0005))?;
151    Ok(migrator)
152}
153
154#[cfg(feature = "accord")]
155pub fn new<DB: sqlx::Database>() -> Result<Migrator<DB>, sqlx_migrator::Error>
156where
157    InitMigration: sqlx_migrator::Migration<DB>,
158    M0002: sqlx_migrator::Migration<DB>,
159    M0003: sqlx_migrator::Migration<DB>,
160    M0004: sqlx_migrator::Migration<DB>,
161    M0005: sqlx_migrator::Migration<DB>,
162    AccordMigration: sqlx_migrator::Migration<DB>,
163{
164    let mut migrator = Migrator::default();
165    migrator.add_migration(Box::new(InitMigration))?;
166    migrator.add_migration(Box::new(M0002))?;
167    migrator.add_migration(Box::new(M0003))?;
168    migrator.add_migration(Box::new(M0004))?;
169    migrator.add_migration(Box::new(M0005))?;
170    // The optional evento-accord consensus-journal tables.
171    migrator.add_migration(Box::new(AccordMigration))?;
172    Ok(migrator)
173}