optional sqlx feature `json` required for type JSONB of column/param X
Bug Description
Compiling my project on using sqlx from latest GitHub commit gives me an error akin to:
$ cargo check --all-features
error: optional sqlx feature `json` required for type JSONB of column #6 ("embeds")
--> src/db/channel.rs:340:27
|
340 | let mut message = sqlx::query!(
| ___________________________^
341 | | r#"SELECT
342 | | messages.*,
343 | | embeds AS "embeds_ser: sqlx::types::Json<Vec<Embed>>"
... |
351 | | channel_id as i64,
352 | | )
| |_________^
|
= note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info)
My Cargo.toml:
[dependencies.sqlx]
git = "https://github.com/launchbadge/sqlx.git"
version = "0.8.0-alpha.0"
features = ["postgres", "macros", "runtime-tokio-rustls", "chrono", "json", "uuid"]
optional = true
(as seen, i have the json feature enabled)
This occurs is all places where JSONB is used
Minimal Reproduction
Use the latest commit of sqlx, enabling the json feature, then write a query using the sqlx::query! macro that uses JSONB
Info
- SQLx version: 0.8.0-alpha0 (commit 0eb2ee93)
- SQLx features enabled:
["postgres", "macros", "runtime-tokio-rustls", "chrono", "json", "uuid"] - Database server and version: Postgres 14.12
- Operating system: macOS Sonoma 14.0
-
rustc --version: rustc 1.81.0-nightly (b5b13568f 2024-06-10)
Notes
Using the following patch fixes this error:
git = "https://github.com/benluelo/sqlx.git"
branch = "fix-encode-decode-derives"
Why not open a PR?
Why not open a PR?
The patch was taken from an old PR by another author that was merged that solved an unrelated issue. This PR was created before a commit (not to my knowledge) that led to the referenced error (thus this breaking commit was not in the PR branch) but merged after this commit was pushed.
Okay.. so that seems somewhat git bisect-able
As said by you, https://github.com/launchbadge/sqlx/pull/2940 is unrelated. I have included it in the bisect table below anyways for completeness.
| Date | Commit | issue(s)^1 | note |
|---|---|---|---|
| 31.Nov | 31d402b469dde449eecca8af02447b9f8dd8dca8 | lifetime |
|
| 03.Mar | 791a7f5417ca46859ababd97ee0d52c0356f4024 | lifetime |
|
| 14.Mar | 8f926e590cce1fb3a0d4120f215c771ddd88a084 | lifetime |
|
| 14.Mar | e0a1f1633c115bb5653fc7bd54b233c677546b95 | lifetime |
|
| 14.Mar | 1f6642cafa98a2ae8cc2c407d85fcad17564bf01 | lifetime |
|
| 30.Mar | 1c7b3d0751cdca5a08fbfa7f24c985fc3774cf11 | lifetime |
1 before #2970 |
| 30.Mar | 02c68a46c7c64b03b2a4eb7375041e0ca86d2fe5 | JSON + lifetime |
#2970 |
| 31.May | 240b4fffd319b20c872fa4c5f05fa17c455db6f5 | JSON + lifetime |
1 before #2940 |
| 31.May | 1ce0e760deb8ad70ed17931a9981377ae42855fe | JSON |
#2940 |
- JSON => optional sqlx feature json required for type JSONB
- lifetime => lifetime may not live long enough
(very much partially) debugging this:
-
The issue surfaces as this function gets triggered
https://github.com/launchbadge/sqlx/blob/3bec3f0f0c0ae979679517a95279d71c8dea5717/sqlx-macros-core/src/query/args.rs#L58-L67
-
So
param_type_for_idreturnsNone. -
param_type_for_idis implemented here: https://github.com/launchbadge/sqlx/blob/3bec3f0f0c0ae979679517a95279d71c8dea5717/sqlx-core/src/type_checking.rs#L141-L153 That comes from macro code which I am currently too unqualified to compremehend 🪄 => lets expand the macro
Here it goes into my code as above does not have a reproducible example attached ^^
My code is this. I have included just the relevant parts, and it is not fully minimal. If this turns out to be a problem, I will contribute a minimal example
CREATE TABLE en
(
key TEXT UNIQUE PRIMARY KEY NOT NULL,
data TEXT NOT NULL,
);
alter table en alter column data type jsonb using data::jsonb;
let key = "1";
let result = sqlx::query_scalar!("SELECT data FROM en WHERE key = $1", key)
.fetch_optional(&pool)
.await;
The macro expands to:
-
Level 1
::sqlx::sqlx_macros::expand_query!( scalar = _ , source = "SELECT data FROM en WHERE key = $1" , args = [ key ] ) -
Level 2
{ #[allow(clippy::all)] { use ::sqlx::Arguments as _; let arg0 = &(key); if false { use ::sqlx::ty_match::{WrapSameExt as _, MatchBorrowExt as _}; let expr = ::sqlx::ty_match::dupe_value(arg0); let ty_check = ::sqlx::ty_match::WrapSame::<&str, _>::new(&expr).wrap_same(); let (mut _ty_check, match_borrow) = ::sqlx::ty_match::MatchBorrow::new(ty_check, &expr); _ty_check = match_borrow.match_borrow(); ::std::panic!(); } let mut query_args = <sqlx::postgres::Postgres as ::sqlx::database::HasArguments>::Arguments::default(); query_args.reserve(1usize, 0 + ::sqlx::encode::Encode::<sqlx::postgres::Postgres>::size_hint(arg0)); query_args.add(arg0); ::sqlx::query_scalar_with::<sqlx::postgres::Postgres, sqlx::types::JsonValue, _>("SELECT data FROM en WHERE key = $1", query_args) } } -
Recursive expansion
// Recursive expansion of query_scalar! macro // =========================================== { #[allow(clippy::all)] { use ::sqlx::Arguments as _; let arg0 = &(key); if false { use ::sqlx::ty_match::{MatchBorrowExt as _, WrapSameExt as _}; let expr = ::sqlx::ty_match::dupe_value(arg0); let ty_check = ::sqlx::ty_match::WrapSame::<&str, _>::new(&expr).wrap_same(); let (mut _ty_check, match_borrow) = ::sqlx::ty_match::MatchBorrow::new(ty_check, &expr); _ty_check = match_borrow.match_borrow(); { #[cold] #[track_caller] #[inline(never)] const fn panic_cold_explicit() -> ! { $crate::panicking::panic_explicit() } panic_cold_explicit(); }; } let mut query_args = <sqlx::postgres::Postgres as ::sqlx::database::HasArguments>::Arguments::default(); query_args.reserve( 1usize, 0 + ::sqlx::encode::Encode::<sqlx::postgres::Postgres>::size_hint(arg0), ); query_args.add(arg0); ::sqlx::query_scalar_with::< sqlx::postgres::Postgres, ::core::compile_error! { "optional sqlx feature `json` required for type JSONB of column #1 (\"data\")" }, _, >("SELECT data FROM en WHERE key = $1", query_args) } }
I am somewhat lost what happens in that macros' type_info or compatible calls don't end up matching..
https://github.com/launchbadge/sqlx/blob/3bec3f0f0c0ae979679517a95279d71c8dea5717/sqlx-postgres/src/types/json.rs#L17-L25
So to me it seems like param_ty is not correct.
The probelm is that param_ty is JSONB (the Display implementation of PgTypeInfo::JSONB) as by error message.
type_info() returns PgTypeInfo::JSONB
expanding the impl_type_checking macro just once makes also does not yield anything wrong I can see
#[cfg(feature = "json")]
_ if <sqlx::types::JsonValue as sqlx_core::types::Type<Postgres>>::type_info() == *info => Some(::sqlx_core::select_input_type!( sqlx::types::JsonValue )),
#[cfg(feature = "json")]
_ if <sqlx::types::JsonValue as sqlx_core::types::Type<Postgres>>::compatible(info) => Some(select_input_type!( sqlx::types::JsonValue )),
@CommanderStorm is it fixed if you add sqlx-postgres?/json here: https://github.com/launchbadge/sqlx/blob/main/sqlx-macros-core/Cargo.toml#L31