Skip to content

[offline] Change prepare to one-file-per-query #2110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .github/workflows/sqlx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ jobs:

- run: echo "using ${DATABASE_URL}"

# Create data dir for offline mode
- run: mkdir .sqlx

- uses: actions-rs/cargo@v1
with:
command: test
Expand All @@ -168,6 +171,39 @@ jobs:
--test-threads=1
env:
DATABASE_URL: sqlite:tests/sqlite/sqlite.db
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg sqlite_ipaddr
LD_LIBRARY_PATH: /tmp/sqlite3-lib

# Remove test artifacts
- run: cargo clean -p sqlx

# Build the macros-test in offline mode (omit DATABASE_URL)
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--no-default-features
--test sqlite-macros
--features any,macros,sqlite,_unstable-all-types,runtime-${{ matrix.runtime }}
env:
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg sqlite_ipaddr
LD_LIBRARY_PATH: /tmp/sqlite3-lib

# Test macros in offline mode (still needs DATABASE_URL to run)
- uses: actions-rs/cargo@v1
with:
command: test
args: >
--no-default-features
--test sqlite-macros
--features any,macros,sqlite,_unstable-all-types,runtime-${{ matrix.runtime }}
env:
DATABASE_URL: sqlite://tests/sqlite/sqlite.db
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg sqlite_ipaddr
LD_LIBRARY_PATH: /tmp/sqlite3-lib

Expand Down Expand Up @@ -207,6 +243,9 @@ jobs:
docker-compose -f tests/docker-compose.yml run -d -p 5432:5432 --name postgres_${{ matrix.postgres }} postgres_${{ matrix.postgres }}
docker exec postgres_${{ matrix.postgres }} bash -c "until pg_isready; do sleep 1; done"

# Create data dir for offline mode
- run: mkdir .sqlx

- uses: actions-rs/cargo@v1
with:
command: test
Expand All @@ -215,6 +254,7 @@ jobs:
--features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx
SQLX_OFFLINE_DIR: .sqlx
# FIXME: needed to disable `ltree` tests in Postgres 9.6
# but `PgLTree` should just fall back to text format
RUSTFLAGS: --cfg postgres_${{ matrix.postgres }}
Expand All @@ -228,6 +268,41 @@ jobs:
--features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx?sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt
SQLX_OFFLINE_DIR: .sqlx
# FIXME: needed to disable `ltree` tests in Postgres 9.6
# but `PgLTree` should just fall back to text format
RUSTFLAGS: --cfg postgres_${{ matrix.postgres }}

# Remove test artifacts
- run: cargo clean -p sqlx

# Build the macros-test in offline mode (omit DATABASE_URL)
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--no-default-features
--test postgres-macros
--features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
# FIXME: needed to disable `ltree` tests in Postgres 9.6
# but `PgLTree` should just fall back to text format
RUSTFLAGS: --cfg postgres_${{ matrix.postgres }}

# Test macros in offline mode (still needs DATABASE_URL to run)
- uses: actions-rs/cargo@v1
with:
command: test
args: >
--no-default-features
--test postgres-macros
--features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
# FIXME: needed to disable `ltree` tests in Postgres 9.6
# but `PgLTree` should just fall back to text format
RUSTFLAGS: --cfg postgres_${{ matrix.postgres }}
Expand Down Expand Up @@ -283,6 +358,9 @@ jobs:
- run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mysql_${{ matrix.mysql }} mysql_${{ matrix.mysql }}
- run: sleep 60

# Create data dir for offline mode
- run: mkdir .sqlx

- uses: actions-rs/cargo@v1
with:
command: test
Expand All @@ -291,6 +369,7 @@ jobs:
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx?ssl-mode=disabled
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mysql_${{ matrix.mysql }}

# MySQL 5.7 supports TLS but not TLSv1.3 as required by RusTLS.
Expand All @@ -303,6 +382,39 @@ jobs:
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mysql_${{ matrix.mysql }}

# Remove test artifacts
- run: cargo clean -p sqlx

# Build the macros-test in offline mode (omit DATABASE_URL)
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--no-default-features
--test mysql-macros
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mysql_${{ matrix.mysql }}

# Test macros in offline mode (still needs DATABASE_URL to run)
# MySQL 5.7 supports TLS but not TLSv1.3 as required by RusTLS.
- uses: actions-rs/cargo@v1
if: ${{ !(matrix.mysql == '5_7' && matrix.tls == 'rustls') }}
with:
command: test
args: >
--no-default-features
--test mysql-macros
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mysql_${{ matrix.mysql }}

# client SSL authentication
Expand Down Expand Up @@ -355,14 +467,48 @@ jobs:
- run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mariadb_${{ matrix.mariadb }} mariadb_${{ matrix.mariadb }}
- run: sleep 30

# Create data dir for offline mode
- run: mkdir .sqlx

- uses: actions-rs/cargo@v1
with:
command: test
args: >
--no-default-features
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }}

# Remove test artifacts
- run: cargo clean -p sqlx

# Build the macros-test in offline mode (omit DATABASE_URL)
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--no-default-features
--test mysql-macros
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }}

# Test macros in offline mode (still needs DATABASE_URL to run)
- uses: actions-rs/cargo@v1
with:
command: test
args: >
--no-default-features
--test mysql-macros
--features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx
SQLX_OFFLINE: true
SQLX_OFFLINE_DIR: .sqlx
RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }}

# client SSL authentication
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 17 additions & 8 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,26 +180,35 @@ See Also:
----
### How do I compile with the macros without needing a database, e.g. in CI?

The macros support an offline mode which saves data for existing queries to a JSON file,
so the macros can just read that file instead of talking to a database.
The macros support an offline mode which saves data for existing queries to a `.sqlx` directory,
so the macros can just read those instead of talking to a database.

See the following:

* [the docs for `query!()`](https://docs.rs/sqlx/0.5.5/sqlx/macro.query.html#offline-mode-requires-the-offline-feature)
* [the README for `sqlx-cli`](sqlx-cli/README.md#enable-building-in-offline-mode-with-query)

To keep `sqlx-data.json` up-to-date you need to run `cargo sqlx prepare` before every commit that
To keep `.sqlx` up-to-date you need to run `cargo sqlx prepare` before every commit that
adds or changes a query; you can do this with a Git pre-commit hook:

```shell
$ echo "cargo sqlx prepare > /dev/null 2>&1; git add sqlx-data.json > /dev/null" > .git/hooks/pre-commit
$ echo "cargo sqlx prepare > /dev/null 2>&1; git add .sqlx > /dev/null" > .git/hooks/pre-commit
```

Note that this may make committing take some time as it'll cause your project to be recompiled, and
as an ergonomic choice it does _not_ block committing if `cargo sqlx prepare` fails.

We're working on a way for the macros to save their data to the filesystem automatically which should be part of SQLx 0.6,
so your pre-commit hook would then just need to stage the changed files.
We're working on a way for the macros to save their data to the filesystem automatically which should be part of SQLx 0.7,
so your pre-commit hook would then just need to stage the changed files. This can be enabled by creating a directory
and setting the `SQLX_OFFLINE_DIR` environment variable to it before compiling, e.g.

```shell
$ mkdir .sqlx
$ export SQLX_OFFLINE_DIR="./.sqlx"`
$ cargo check
```

However, this behaviour is not considered stable and it is still recommended to use `cargo sqlx prepare`.

----

Expand Down Expand Up @@ -253,9 +262,9 @@ Even Sisyphus would pity us.

### Why does my project using sqlx query macros not build on docs.rs?

Docs.rs doesn't have access to your database, so it needs to be provided a `sqlx-data.json` file and be instructed to set the `SQLX_OFFLINE` environment variable to true while compiling your project. Luckily for us, docs.rs creates a `DOCS_RS` environment variable that we can access in a custom build script to achieve this functionality.
Docs.rs doesn't have access to your database, so it needs to be provided prepared queries in a `.sqlx` directory and be instructed to set the `SQLX_OFFLINE` environment variable to true while compiling your project. Luckily for us, docs.rs creates a `DOCS_RS` environment variable that we can access in a custom build script to achieve this functionality.

To do so, first, make sure that you have run `cargo sqlx prepare` to generate a `sqlx-data.json` file in your project.
To do so, first, make sure that you have run `cargo sqlx prepare` to generate a `.sqlx` directory in your project.

Next, create a file called `build.rs` in the root of your project directory (at the same level as `Cargo.toml`). Add the following code to it:
```rs
Expand Down
37 changes: 18 additions & 19 deletions sqlx-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,44 +106,43 @@ error: cannot mix reversible migrations with simple migrations. All migrations s

### Enable building in "offline mode" with `query!()`

There are 3 steps to building with "offline mode":
There are 2 steps to building with "offline mode":

1. Enable the SQLx's Cargo feature `offline`
- E.g. in your `Cargo.toml`, `sqlx = { features = [ "offline", ... ] }`
2. Save query metadata for offline usage
1. Save query metadata for offline usage
- `cargo sqlx prepare`
3. Build
2. Build

Note: Saving query metadata must be run as `cargo sqlx`.

```bash
cargo sqlx prepare
```

Invoking `prepare` saves query metadata to `sqlx-data.json` in the current directory; check this file into version
control and an active database connection will no longer be needed to build your project.
Invoking `prepare` saves query metadata to `.sqlx` in the current directory.
For workspaces where several crates are using query macros, pass the `--workspace` flag
to generate a single `.sqlx` directory at the root of the workspace.

Has no effect unless the `offline` Cargo feature of `sqlx` is enabled in your project. Omitting that
feature is the most likely cause if you get a `sqlx-data.json` file that looks like this:

```json
{
"database": "PostgreSQL"
}
```bash
cargo sqlx prepare --workspace
```

Check this directory into version control and an active database connection will
no longer be needed to build your project.

---

```bash
cargo sqlx prepare --check
# OR
cargo sqlx prepare --check --workspace
```

Exits with a nonzero exit status if the data in `sqlx-data.json` is out of date with the current
database schema and queries in the project. Intended for use in Continuous Integration.
Exits with a nonzero exit status if the data in `.sqlx` is out of date with the current
database schema or queries in the project. Intended for use in Continuous Integration.

### Force building in offline mode

The presence of a `DATABASE_URL` environment variable will take precedence over the presence of `sqlx-data.json`, meaning SQLx will default to building against a database if it can. To make sure an accidentally-present `DATABASE_URL` environment variable or `.env` file does not
The presence of a `DATABASE_URL` environment variable will take precedence over the presence of `.sqlx`, meaning SQLx will default to building against a database if it can. To make sure an accidentally-present `DATABASE_URL` environment variable or `.env` file does not
result in `cargo build` (trying to) access the database, you can set the `SQLX_OFFLINE` environment
variable to `true`.

Expand All @@ -152,8 +151,8 @@ still do the right thing and connect to the database.

### Include queries behind feature flags (such as queries inside of tests)

In order for sqlx to be able to find queries behind certain feature flags you need to turn them
on by passing arguments to rustc.
In order for sqlx to be able to find queries behind certain feature flags or in tests, you need to turn them
on by passing arguments to `cargo`.

This is how you would turn all targets and features on.

Expand Down
19 changes: 7 additions & 12 deletions sqlx-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::io;
use std::time::Duration;

use anyhow::Result;
use futures::{Future, TryFutureExt};

use sqlx::{AnyConnection, Connection};
use std::io;
use std::time::Duration;

use crate::opt::{Command, ConnectOpts, DatabaseCommand, MigrateCommand};

Expand Down Expand Up @@ -61,18 +63,11 @@ pub async fn run(opt: Opt) -> Result<()> {
},

Command::Prepare {
check: false,
merged,
args,
check,
workspace,
connect_opts,
} => prepare::run(&connect_opts, merged, args).await?,

Command::Prepare {
check: true,
merged,
args,
connect_opts,
} => prepare::check(&connect_opts, merged, args).await?,
} => prepare::run(check, workspace, connect_opts, args).await?,
};

Ok(())
Expand Down
Loading