Skip to content

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

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
jay3332 opened this issue Jun 28, 2024 · 6 comments · Fixed by #3350
Closed

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

jay3332 opened this issue Jun 28, 2024 · 6 comments · Fixed by #3350
Labels

Comments

@jay3332
Copy link

jay3332 commented Jun 28, 2024

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 0eb2ee9)
  • 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"
@abonander
Copy link
Collaborator

Why not open a PR?

@jay3332
Copy link
Author

jay3332 commented Jun 29, 2024

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.

@CommanderStorm
Copy link
Contributor

Okay.. so that seems somewhat git bisect-able
As said by you, #2940 is unrelated. I have included it in the bisect table below anyways for completeness.

Date Commit issue(s)1 note
31.Nov 31d402b lifetime
03.Mar 791a7f5 lifetime
14.Mar 8f926e5 lifetime
14.Mar e0a1f16 lifetime
14.Mar 1f6642c lifetime
30.Mar 1c7b3d0 lifetime 1 before #2970
30.Mar 02c68a4 JSON + lifetime #2970
31.May 240b4ff JSON + lifetime 1 before #2940
31.May 1ce0e76 JSON #2940

Footnotes

  1. Issues-key:

    • JSON => optional sqlx feature json required for type JSONB
    • lifetime => lifetime may not live long enough

@CommanderStorm
Copy link
Contributor

CommanderStorm commented Jul 3, 2024

(very much partially) debugging this:

  • The issue surfaces as this function gets triggered

    let param_ty =
    DB::param_type_for_id(&param_ty)
    .ok_or_else(|| {
    if let Some(feature_gate) = DB::get_feature_gate(&param_ty) {
    format!(
    "optional sqlx feature `{}` required for type {} of param #{}",
    feature_gate,
    param_ty,
    i + 1,
    )

  • So param_type_for_id returns None.

  • param_type_for_id is implemented here:

    fn param_type_for_id(info: &Self::TypeInfo) -> Option<&'static str> {
    match () {
    $(
    $(#[$meta])?
    _ if <$ty as sqlx_core::types::Type<$database>>::type_info() == *info => Some($crate::select_input_type!($ty $(, $input)?)),
    )*
    $(
    $(#[$meta])?
    _ if <$ty as sqlx_core::types::Type<$database>>::compatible(info) => Some(select_input_type!($ty $(, $input)?)),
    )*
    _ => None
    }
    }

    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..

impl<T> Type<Postgres> for Json<T> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::JSONB
}
fn compatible(ty: &PgTypeInfo) -> bool {
*ty == PgTypeInfo::JSON || *ty == PgTypeInfo::JSONB
}
}

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
Copy link
Contributor

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 )),

@abonander
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants