sqlx
Written
— Updated
Writing Functions Generic across Connection Types
- Sometimes you want a function that can take a connection pool, a connection, or a transaction. This is possible in sqlx, but it's not totally obvious how to do it.
Executor Trait
- For functions that just make a single call, you can use the Executor trait (here, PgExecutor which is an alias for the Postgres version)
async
- The main issue here is that Rust doesn't easily have a way to pass a reference to that
impl PgExecutor
object that it also thinks will implementPgExecutor
. Passing it once passes ownership and you can't use it again. Still this works fine for any function that only makes one database query, and provides the simples experience to the caller.
Dereferencing to Connection
- This method requires you to manually acquire a connection from a pool first, but it does allow you to call a function with either a plain connection or a transaction.
- Here, you need to use
&mut *conn
any time you use the connection. This looks a bit weird, but takes advantage of the fact that all the relevant objects implementDerefMut
to allow getting a reference to the Connection. async async
Deserializing JSON
Custom Types
- This is largely unintuitive if you don't want to go through a
serde_json::Value
. The trick is to rename the column in the query to include type information, which sqlx will use to sqlx_json_decode!; async
- I also have a crate sqlx-transparent-json-decode, that provides a macro to implement the
Decode
trait for JSON columns for any structure that implementsDeserialize
. - With this crate, you can import the
sqlx_json_decode
macro, then addsqlx_json_decode!(SomeJsonField)
to the above code, and then you won't have to wrap the type insqlx::types::Json
anymore. This can be convenient when using one of these structures for both database results and other purposes.
- This is largely unintuitive if you don't want to go through a
Box<RawValue>
- Sometimes you just want to get the raw JSON out of a field without needing to create a bunch of objects from it. For example, a web server that is passing the JSON straight down to the client without even looking at it.
serde_json
provides theRawValue
type for this, but it is a reference type, and so needs to be wrapped in aBox
or something to be usable once the database connection is released. sqlx
provides an implementation to decode a&RawValue
, but not aBox<RawValue>
, and so thesqlx-transparent-json-decode
crate also offers a wrapper type aroundBox<RawValue>
that implementsDecode
.
- Sometimes you just want to get the raw JSON out of a field without needing to create a bunch of objects from it. For example, a web server that is passing the JSON straight down to the client without even looking at it.
Migrations
- To update the checksum of a migration when the file changed, you can recalculate the hash
cat migrations/FILENAME.sql | openssl dgst -sha384 | cut -d ' ' -f 2
- Then change the checksum for that row in the database:
update _sqlx_migrations set checksum='\x<the checksum>' where version ='<the version>';
- To update the checksum of a migration when the file changed, you can recalculate the hash
Compiling Offline
- sqlx normally requires a database connection which allows it to check queries at compile time. For building without a valid database connection, it supports an offline mode.
cargo sqlx prepare
will create a.sqlx
directory with all the relevant information to check the queries without being able to talk to the database later.- Dependencies that use
sqlx
macros- The prepare command handles this, mostly. You need to run
cargo clean -p DEP_CRATE
before every time your runcargo sqlx prepare
for it to properly find all the queries. - With the default settings, the build process will not find the offline data when building the dependency. To fix that, set the
SQLX_OFFLINE_DIR
environment variable to the location of the.sqlx
directory.
- The prepare command handles this, mostly. You need to run