sqlx_core/config/
migrate.rs

1use std::collections::BTreeSet;
2
3/// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
4///
5/// ### Note
6/// A manually constructed [`Migrator`][crate::migrate::Migrator] will not be aware of these
7/// configuration options. We recommend using `sqlx::migrate!()` instead.
8///
9/// ### Warning: Potential Data Loss or Corruption!
10/// Many of these options, if changed after migrations are set up,
11/// can result in data loss or corruption of a production database
12/// if the proper precautions are not taken.
13///
14/// Be sure you know what you are doing and that you read all relevant documentation _thoroughly_.
15#[derive(Debug, Default)]
16#[cfg_attr(
17    feature = "sqlx-toml",
18    derive(serde::Deserialize),
19    serde(default, rename_all = "kebab-case", deny_unknown_fields)
20)]
21pub struct Config {
22    /// Specify the names of schemas to create if they don't already exist.
23    ///
24    /// This is done before checking the existence of the migrations table
25    /// (`_sqlx_migrations` or overridden `table_name` below) so that it may be placed in
26    /// one of these schemas.
27    ///
28    /// ### Example
29    /// `sqlx.toml`:
30    /// ```toml
31    /// [migrate]
32    /// create-schemas = ["foo"]
33    /// ```
34    pub create_schemas: BTreeSet<Box<str>>,
35
36    /// Override the name of the table used to track executed migrations.
37    ///
38    /// May be schema-qualified and/or contain quotes. Defaults to `_sqlx_migrations`.
39    ///
40    /// Potentially useful for multi-tenant databases.
41    ///
42    /// ### Warning: Potential Data Loss or Corruption!
43    /// Changing this option for a production database will likely result in data loss or corruption
44    /// as the migration machinery will no longer be aware of what migrations have been applied
45    /// and will attempt to re-run them.
46    ///
47    /// You should create the new table as a copy of the existing migrations table (with contents!),
48    /// and be sure all instances of your application have been migrated to the new
49    /// table before deleting the old one.
50    ///
51    /// ### Example
52    /// `sqlx.toml`:
53    /// ```toml
54    /// [migrate]
55    /// # Put `_sqlx_migrations` in schema `foo`
56    /// table-name = "foo._sqlx_migrations"
57    /// ```
58    pub table_name: Option<Box<str>>,
59
60    /// Override the directory used for migrations files.
61    ///
62    /// Relative to the crate root for `sqlx::migrate!()`, or the current directory for `sqlx-cli`.
63    pub migrations_dir: Option<Box<str>>,
64
65    /// Specify characters that should be ignored when hashing migrations.
66    ///
67    /// Any characters contained in the given array will be dropped when a migration is hashed.
68    ///
69    /// ### Warning: May Change Hashes for Existing Migrations
70    /// Changing the characters considered in hashing migrations will likely
71    /// change the output of the hash.
72    ///
73    /// This may require manual rectification for deployed databases.
74    ///
75    /// ### Example: Ignore Carriage Return (`<CR>` | `\r`)
76    /// Line ending differences between platforms can result in migrations having non-repeatable
77    /// hashes. The most common culprit is the carriage return (`<CR>` | `\r`), which Windows
78    /// uses in its line endings alongside line feed (`<LF>` | `\n`), often written `CRLF` or `\r\n`,
79    /// whereas Linux and macOS use only line feeds.
80    ///
81    /// `sqlx.toml`:
82    /// ```toml
83    /// [migrate]
84    /// ignored-chars = ["\r"]
85    /// ```
86    ///
87    /// For projects using Git, this can also be addressed using [`.gitattributes`]:
88    ///
89    /// ```text
90    /// # Force newlines in migrations to be line feeds on all platforms
91    /// migrations/*.sql text eol=lf
92    /// ```
93    ///
94    /// This may require resetting or re-checking out the migrations files to take effect.
95    ///
96    /// [`.gitattributes`]: https://git-scm.com/docs/gitattributes
97    ///
98    /// ### Example: Ignore all Whitespace Characters
99    /// To make your migrations amenable to reformatting, you may wish to tell SQLx to ignore
100    /// _all_ whitespace characters in migrations.
101    ///
102    /// ##### Warning: Beware Syntactically Significant Whitespace!
103    /// If your migrations use string literals or quoted identifiers which contain whitespace,
104    /// this configuration will cause the migration machinery to ignore some changes to these.
105    /// This may result in a mismatch between the development and production versions of
106    /// your database.
107    ///
108    /// `sqlx.toml`:
109    /// ```toml
110    /// [migrate]
111    /// # Ignore common whitespace characters when hashing
112    /// ignored-chars = [" ", "\t", "\r", "\n"]  # Space, tab, CR, LF
113    /// ```
114    // Likely lower overhead for small sets than `HashSet`.
115    pub ignored_chars: BTreeSet<char>,
116
117    /// Specify default options for new migrations created with `sqlx migrate add`.
118    pub defaults: MigrationDefaults,
119}
120
121#[derive(Debug, Default)]
122#[cfg_attr(
123    feature = "sqlx-toml",
124    derive(serde::Deserialize),
125    serde(default, rename_all = "kebab-case")
126)]
127pub struct MigrationDefaults {
128    /// Specify the default type of migration that `sqlx migrate add` should create by default.
129    ///
130    /// ### Example: Use Reversible Migrations by Default
131    /// `sqlx.toml`:
132    /// ```toml
133    /// [migrate.defaults]
134    /// migration-type = "reversible"
135    /// ```
136    pub migration_type: DefaultMigrationType,
137
138    /// Specify the default scheme that `sqlx migrate add` should use for version integers.
139    ///
140    /// ### Example: Use Sequential Versioning by Default
141    /// `sqlx.toml`:
142    /// ```toml
143    /// [migrate.defaults]
144    /// migration-versioning = "sequential"
145    /// ```
146    pub migration_versioning: DefaultVersioning,
147}
148
149/// The default type of migration that `sqlx migrate add` should create by default.
150#[derive(Debug, Default, PartialEq, Eq)]
151#[cfg_attr(
152    feature = "sqlx-toml",
153    derive(serde::Deserialize),
154    serde(rename_all = "snake_case")
155)]
156pub enum DefaultMigrationType {
157    /// Create the same migration type as that of the latest existing migration,
158    /// or `Simple` otherwise.
159    #[default]
160    Inferred,
161
162    /// Create non-reversible migrations (`<VERSION>_<DESCRIPTION>.sql`) by default.
163    Simple,
164
165    /// Create reversible migrations (`<VERSION>_<DESCRIPTION>.up.sql` and `[...].down.sql`) by default.
166    Reversible,
167}
168
169/// The default scheme that `sqlx migrate add` should use for version integers.
170#[derive(Debug, Default, PartialEq, Eq)]
171#[cfg_attr(
172    feature = "sqlx-toml",
173    derive(serde::Deserialize),
174    serde(rename_all = "snake_case")
175)]
176pub enum DefaultVersioning {
177    /// Infer the versioning scheme from existing migrations:
178    ///
179    /// * If the versions of the last two migrations differ by `1`, infer `Sequential`.
180    /// * If only one migration exists and has version `1`, infer `Sequential`.
181    /// * Otherwise, infer `Timestamp`.
182    #[default]
183    Inferred,
184
185    /// Use UTC timestamps for migration versions.
186    ///
187    /// This is the recommended versioning format as it's less likely to collide when multiple
188    /// developers are creating migrations on different branches.
189    ///
190    /// The exact timestamp format is unspecified.
191    Timestamp,
192
193    /// Use sequential integers for migration versions.
194    Sequential,
195}
196
197#[cfg(feature = "migrate")]
198impl Config {
199    pub fn migrations_dir(&self) -> &str {
200        self.migrations_dir.as_deref().unwrap_or("migrations")
201    }
202
203    pub fn table_name(&self) -> &str {
204        self.table_name.as_deref().unwrap_or("_sqlx_migrations")
205    }
206
207    pub fn to_resolve_config(&self) -> crate::migrate::ResolveConfig {
208        let mut config = crate::migrate::ResolveConfig::new();
209        config.ignore_chars(self.ignored_chars.iter().copied());
210        config
211    }
212}