Closed
Description
This was a popular request in the original Reddit announcement: https://www.reddit.com/r/rust/comments/egpw7g/announcing_sqlx_a_fully_asynchronous_pure_rust/fc8ldpw/?context=1000
The idea is to allow expansion of query!()
, et al, without having to connect directly to the database by running the PREPARE
s for all queries in a separate step and caching the result.
The solution in my head looks something like this:
- We create a Cargo subcommand,
cargo-sqlx
, which linkssqlx-core
internally. - The user runs
cargo sqlx prepare
in their project which invokescargo check
withQUERY_PREPARE_OUT=$OUT_DIR/sqlx
set in the environment- By being a subcommand we can know which
cargo
they're using so we build with the right toolchain, e.g. if the user runscargo +nightly sqlx prepare
when their default toolchain isstable
- By being a subcommand we can know which
- When
query!()
and friends seeQUERY_PREPARE_OUT
in the environment they don't perform normal expansion:- Instead they run
PREPARE
on their query strings and output the hash of the query and the prepare result as randomly named JSON files toQUERY_PREPARE_OUT
- Then emit panics instead of their normal expansion so compilation can still finish.
- Instead they run
- The subcommand then gathers the files inside
QUERY_PREPARE_OUT
and writes them to a single JSON file in a predetermined location in the project (TBD)- These separate scatter/gather steps is because the macro expansions may run in parallel, so we don't want them all to clobber the same file.
- This new file is intended to be checked-in to source control where it can be used even without a database connection, e.g. in CI.
- If two queries have the same hash, the subcommand deduplicates the entries by asserting that the
PREPARE
data is the same.
- If the file exists when
query!()
is later expanded normally, the macro checks in the file for the hash of its query string and uses that data instead of connecting to the database.- If the hash isn't found it emits a compile error saying to run
cargo sqlx prepare
again. - We can also support
cargo sqlx prepare --check
which does the opposite; it rerunsPREPARE
for the cached query strings and asserts that the result is the same.
- If the hash isn't found it emits a compile error saying to run
We can then later extend the cargo-sqlx
command to add migrations. With migrations in place we have a stronger guarantee that the schema we compiled and cached the PREPARE
data against is the same as the one we are running against.
Questions:
- Is
prepare
the right name for the sub-subcommand? - What should the cached file be named and where should it go?
- Should we support a
sqlx.toml
to change the location and possibly other behaviors? - Should the macro use an environment variable like
QUERY_PREPARE_OUT
or look at acfg
flag? I'm thinking the former might fail to trigger rebuilds when set if Cargo doesn't know to watch for it but the latter might trigger a rebuild of the whole dep graph.- We could also just
touch src/
to trigger a rebuild, no?
- We could also just
- Do we want to support checking the cached file at runtime to ensure the
PREPARE
data we saved still matches what we get back from the database server?