sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

optional sqlx feature `json` required for type JSONB of column/param X

Open jay3332 opened this issue 1 year ago • 2 comments

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"

jay3332 avatar Jun 28 '24 21:06 jay3332

Why not open a PR?

abonander avatar Jun 28 '24 22:06 abonander

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.

jay3332 avatar Jun 29 '24 00:06 jay3332

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

CommanderStorm avatar Jul 03 '24 21:07 CommanderStorm

(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_id returns None.

  • param_type_for_id is 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

CommanderStorm avatar Jul 03 '24 22:07 CommanderStorm

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 avatar Jul 04 '24 00:07 CommanderStorm

@CommanderStorm is it fixed if you add sqlx-postgres?/json here: https://github.com/launchbadge/sqlx/blob/main/sqlx-macros-core/Cargo.toml#L31

abonander avatar Jul 16 '24 20:07 abonander