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