sqlx_cli/
opt.rs

1use std::ops::{Deref, Not};
2
3use clap::{Args, Parser};
4#[cfg(feature = "completions")]
5use clap_complete::Shell;
6
7#[derive(Parser, Debug)]
8#[clap(version, about, author)]
9pub struct Opt {
10    // https://github.com/launchbadge/sqlx/pull/3724 placed this here,
11    // but the intuitive place would be in the arguments for each subcommand.
12    #[clap(flatten)]
13    pub no_dotenv: NoDotenvOpt,
14
15    #[clap(subcommand)]
16    pub command: Command,
17}
18
19#[derive(Parser, Debug)]
20pub enum Command {
21    #[clap(alias = "db")]
22    Database(DatabaseOpt),
23
24    /// Generate query metadata to support offline compile-time verification.
25    ///
26    /// Saves metadata for all invocations of `query!` and related macros to a `.sqlx` directory
27    /// in the current directory (or workspace root with `--workspace`), overwriting if needed.
28    ///
29    /// During project compilation, the absence of the `DATABASE_URL` environment variable or
30    /// the presence of `SQLX_OFFLINE` (with a value of `true` or `1`) will constrain the
31    /// compile-time verification to only read from the cached query metadata.
32    #[clap(alias = "prep")]
33    Prepare {
34        /// Run in 'check' mode. Exits with 0 if the query metadata is up-to-date. Exits with
35        /// 1 if the query metadata needs updating.
36        #[clap(long)]
37        check: bool,
38
39        /// Prepare query macros in dependencies that exist outside the current crate or workspace.
40        #[clap(long)]
41        all: bool,
42
43        /// Generate a single workspace-level `.sqlx` folder.
44        ///
45        /// This option is intended for workspaces where multiple crates use SQLx. If there is only
46        /// one, it is better to run `cargo sqlx prepare` without this option inside that crate.
47        #[clap(long)]
48        workspace: bool,
49
50        /// Arguments to be passed to `cargo rustc ...`.
51        #[clap(last = true)]
52        args: Vec<String>,
53
54        #[clap(flatten)]
55        connect_opts: ConnectOpts,
56    },
57
58    #[clap(alias = "mig")]
59    Migrate(MigrateOpt),
60
61    #[cfg(feature = "completions")]
62    /// Generate shell completions for the specified shell
63    Completions { shell: Shell },
64}
65
66/// Group of commands for creating and dropping your database.
67#[derive(Parser, Debug)]
68pub struct DatabaseOpt {
69    #[clap(subcommand)]
70    pub command: DatabaseCommand,
71}
72
73#[derive(Parser, Debug)]
74pub enum DatabaseCommand {
75    /// Creates the database specified in your DATABASE_URL.
76    Create {
77        #[clap(flatten)]
78        connect_opts: ConnectOpts,
79    },
80
81    /// Drops the database specified in your DATABASE_URL.
82    Drop {
83        #[clap(flatten)]
84        confirmation: Confirmation,
85
86        #[clap(flatten)]
87        connect_opts: ConnectOpts,
88
89        /// PostgreSQL only: force drops the database.
90        #[clap(long, short, default_value = "false")]
91        force: bool,
92    },
93
94    /// Drops the database specified in your DATABASE_URL, re-creates it, and runs any pending migrations.
95    Reset {
96        #[clap(flatten)]
97        confirmation: Confirmation,
98
99        #[clap(flatten)]
100        source: Source,
101
102        #[clap(flatten)]
103        connect_opts: ConnectOpts,
104
105        /// PostgreSQL only: force drops the database.
106        #[clap(long, short, default_value = "false")]
107        force: bool,
108    },
109
110    /// Creates the database specified in your DATABASE_URL and runs any pending migrations.
111    Setup {
112        #[clap(flatten)]
113        source: Source,
114
115        #[clap(flatten)]
116        connect_opts: ConnectOpts,
117    },
118}
119
120/// Group of commands for creating and running migrations.
121#[derive(Parser, Debug)]
122pub struct MigrateOpt {
123    #[clap(subcommand)]
124    pub command: MigrateCommand,
125}
126
127#[derive(Parser, Debug)]
128pub enum MigrateCommand {
129    /// Create a new migration with the given description.
130    ///
131    /// A version number will be automatically assigned to the migration.
132    ///
133    /// For convenience, this command will attempt to detect if sequential versioning is in use,
134    /// and if so, continue the sequence.
135    ///
136    /// Sequential versioning is inferred if:
137    ///
138    /// * The version numbers of the last two migrations differ by exactly 1, or:
139    ///
140    /// * only one migration exists and its version number is either 0 or 1.
141    ///
142    /// Otherwise timestamp versioning is assumed.
143    ///
144    /// This behavior can overridden by `--sequential` or `--timestamp`, respectively.
145    Add {
146        description: String,
147
148        #[clap(flatten)]
149        source: Source,
150
151        /// If true, creates a pair of up and down migration files with same version
152        /// else creates a single sql file
153        #[clap(short)]
154        reversible: bool,
155
156        /// If set, use timestamp versioning for the new migration. Conflicts with `--sequential`.
157        #[clap(short, long)]
158        timestamp: bool,
159
160        /// If set, use sequential versioning for the new migration. Conflicts with `--timestamp`.
161        #[clap(short, long, conflicts_with = "timestamp")]
162        sequential: bool,
163    },
164
165    /// Run all pending migrations.
166    Run {
167        #[clap(flatten)]
168        source: Source,
169
170        /// List all the migrations to be run without applying
171        #[clap(long)]
172        dry_run: bool,
173
174        #[clap(flatten)]
175        ignore_missing: IgnoreMissing,
176
177        #[clap(flatten)]
178        connect_opts: ConnectOpts,
179
180        /// Apply migrations up to the specified version. If unspecified, apply all
181        /// pending migrations. If already at the target version, then no-op.
182        #[clap(long)]
183        target_version: Option<i64>,
184    },
185
186    /// Revert the latest migration with a down file.
187    Revert {
188        #[clap(flatten)]
189        source: Source,
190
191        /// List the migration to be reverted without applying
192        #[clap(long)]
193        dry_run: bool,
194
195        #[clap(flatten)]
196        ignore_missing: IgnoreMissing,
197
198        #[clap(flatten)]
199        connect_opts: ConnectOpts,
200
201        /// Revert migrations down to the specified version. If unspecified, revert
202        /// only the last migration. Set to 0 to revert all migrations. If already
203        /// at the target version, then no-op.
204        #[clap(long)]
205        target_version: Option<i64>,
206    },
207
208    /// List all available migrations.
209    Info {
210        #[clap(flatten)]
211        source: Source,
212
213        #[clap(flatten)]
214        connect_opts: ConnectOpts,
215    },
216
217    /// Generate a `build.rs` to trigger recompilation when a new migration is added.
218    ///
219    /// Must be run in a Cargo project root.
220    BuildScript {
221        #[clap(flatten)]
222        source: Source,
223
224        /// Overwrite the build script if it already exists.
225        #[clap(long)]
226        force: bool,
227    },
228}
229
230/// Argument for the migration scripts source.
231#[derive(Args, Debug)]
232pub struct Source {
233    /// Path to folder containing migrations.
234    #[clap(long, default_value = "migrations")]
235    source: String,
236}
237
238impl Deref for Source {
239    type Target = String;
240
241    fn deref(&self) -> &Self::Target {
242        &self.source
243    }
244}
245
246/// Argument for the database URL.
247#[derive(Args, Debug)]
248pub struct ConnectOpts {
249    #[clap(flatten)]
250    pub no_dotenv: NoDotenvOpt,
251
252    /// Location of the DB, by default will be read from the DATABASE_URL env var or `.env` files.
253    #[clap(long, short = 'D', env)]
254    pub database_url: Option<String>,
255
256    /// The maximum time, in seconds, to try connecting to the database server before
257    /// returning an error.
258    #[clap(long, default_value = "10")]
259    pub connect_timeout: u64,
260
261    /// Set whether or not to create SQLite databases in Write-Ahead Log (WAL) mode:
262    /// https://www.sqlite.org/wal.html
263    ///
264    /// WAL mode is enabled by default for SQLite databases created by `sqlx-cli`.
265    ///
266    /// However, if your application sets a `journal_mode` on `SqliteConnectOptions` to something
267    /// other than `Wal`, then it will have to take the database file out of WAL mode on connecting,
268    /// which requires an exclusive lock and may return a `database is locked` (`SQLITE_BUSY`) error.
269    #[cfg(feature = "_sqlite")]
270    #[clap(long, action = clap::ArgAction::Set, default_value = "true")]
271    pub sqlite_create_db_wal: bool,
272}
273
274#[derive(Args, Debug)]
275pub struct NoDotenvOpt {
276    /// Do not automatically load `.env` files.
277    #[clap(long)]
278    // Parsing of this flag is actually handled _before_ calling Clap,
279    // by `crate::maybe_apply_dotenv()`.
280    #[allow(unused)] // TODO: switch to `#[expect]`
281    pub no_dotenv: bool,
282}
283
284impl ConnectOpts {
285    /// Require a database URL to be provided, otherwise
286    /// return an error.
287    pub fn required_db_url(&self) -> anyhow::Result<&str> {
288        self.database_url.as_deref().ok_or_else(
289            || anyhow::anyhow!(
290                "the `--database-url` option or the `DATABASE_URL` environment variable must be provided"
291            )
292        )
293    }
294}
295
296/// Argument for automatic confirmation.
297#[derive(Args, Copy, Clone, Debug)]
298pub struct Confirmation {
299    /// Automatic confirmation. Without this option, you will be prompted before dropping
300    /// your database.
301    #[clap(short)]
302    pub yes: bool,
303}
304
305/// Argument for ignoring applied migrations that were not resolved.
306#[derive(Args, Copy, Clone, Debug)]
307pub struct IgnoreMissing {
308    /// Ignore applied migrations that are missing in the resolved migrations
309    #[clap(long)]
310    ignore_missing: bool,
311}
312
313impl Deref for IgnoreMissing {
314    type Target = bool;
315
316    fn deref(&self) -> &Self::Target {
317        &self.ignore_missing
318    }
319}
320
321impl Not for IgnoreMissing {
322    type Output = bool;
323
324    fn not(self) -> Self::Output {
325        !self.ignore_missing
326    }
327}