Skip to content
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

sqlx prepare fails with error "unsupported type NULL of column #2" #1979

Open
yzernik opened this issue Jul 16, 2022 · 10 comments · Fixed by #1960
Open

sqlx prepare fails with error "unsupported type NULL of column #2" #1979

yzernik opened this issue Jul 16, 2022 · 10 comments · Fixed by #1960

Comments

@yzernik
Copy link

yzernik commented Jul 16, 2022

I have a query that in my master branch works fine, but when I try to modify the query to add some additional clauses, the sqlx prepare command fails with:

$ cargo sqlx prepare --database-url sqlite://db.sqlite --check
   Compiling squeakroad v0.0.2 (/home/yzernik/work/squeakroad)
error: unsupported type NULL of column #2 ("amount_change_sat")
    --> src/models.rs:2597:39
     |
2597 |           let account_balance_changes = sqlx::query!("
     |  _______________________________________^
2598 | | SELECT * FROM
2599 | | (select orders.seller_user_id as user_id, orders.seller_credit_sat as amount_change_sat, 'received_order' as event_type, orders.public_id...
2600 | | from
...    |
2628 | | OFFSET ?
2629 | | ;", limit, offset)
     | |__________________^
     |
     = note: this error originates in the macro `$crate::sqlx_macros::expand_query` (in Nightly builds, run with -Z macro-backtrace for more info)

error: unsupported type NULL of column #3 ("event_type")
    --> src/models.rs:2597:39
     |
2597 |           let account_balance_changes = sqlx::query!("
     |  _______________________________________^
2598 | | SELECT * FROM
2599 | | (select orders.seller_user_id as user_id, orders.seller_credit_sat as amount_change_sat, 'received_order' as event_type, orders.public_id...
2600 | | from
...    |
2628 | | OFFSET ?
2629 | | ;", limit, offset)
     | |__________________^
     |
     = note: this error originates in the macro `$crate::sqlx_macros::expand_query` (in Nightly builds, run with -Z macro-backtrace for more info)

This is the query, and this is the pull request that is failing.

Related to #1350

@tyrelr
Copy link
Contributor

tyrelr commented Jul 17, 2022

Thanks for the schema & query, I've added a [partial] fix into PR #1960 fixing a smaller reproducer. That fix doesn't seem to work for your query though, so there are still issues remaining after that.

@rongcuid
Copy link
Contributor

rongcuid commented Jul 17, 2022

I also have a query that fails for this exact error. I can't isolate a minimum example, but this is my project commit:

rongcuid/gqlforum-rs@33b238a

Hopefully it is not too large. The same query sql/topic_by_id.sql works in SQLite console.

@rongcuid
Copy link
Contributor

I got the same error also after factoring out the WITH clause into a view. The view's column type seems to be NULL for the post_number column. Maybe this is the issue? SQLite doesn't seem to give types for view columns.

@yzernik
Copy link
Author

yzernik commented Jul 18, 2022

@rongcuid my app is also using SQLite. I wonder if sqlx has more difficulty checking types when the db is SQLite?

@tyrelr
Copy link
Contributor

tyrelr commented Jul 18, 2022

I know next to nothing about the other sqlx database backends. But sqlite does have some tricky calculations/trade-offs to do when it attempts to do type checking.

Long story short... sqlite does not appear to have any high-level API to find query result types/nullability without running the query. So sqlx inspects a low-level query plan from sqlite instead, sqlx then infers/traces through that query plan to determine the types. Unrecognized operations can cause type information to be lost. When that type information is lost, it can cause sqlx to not know which type a column is (ie. column type NULL).

I'm out of time this week. But I am guessing that the issue with @rongcuid's query is due to the row_number() function being unrecognized. But tat's just a hunch, if you dig in you may find I'm mistaken, or even find that there are multiple issues at play.

@yzernik
Copy link
Author

yzernik commented Jul 21, 2022

@tyrelr I noticed that the prepare command fails for certain queries when I have an empty database.

In my app buyers put ratings on orders. When I run prepare when there are no ratings, it fails:

$ cargo sqlx prepare --database-url sqlite://db.sqlite --check
   Compiling squeakroad v0.0.2 (/home/yzernik/work/squeakroad)
error: unsupported type NULL of column #1 ("weighted_average")
    --> src/models.rs:1959:28
     |
1959 |           let seller_infos = sqlx::query!(
     |  ____________________________^
1960 | |                     "
1961 | |         SELECT weighted_average, total_amount_sold_sat, users.email
1962 | |         FROM
...    |
1991 | |          total_amount_sold_sat DESC
1992 | |             ;")
     | |_______________^
     |
     = note: this error originates in the macro `$crate::sqlx_macros::expand_query` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `squeakroad` due to previous error
error: `cargo check` failed with status: exit status: 101

But after I put at least one rating in the database, it works again:

$ cargo sqlx prepare --database-url sqlite://db.sqlite --check
   Compiling squeakroad v0.0.2 (/home/yzernik/work/squeakroad)
    Finished dev [unoptimized + debuginfo] target(s) in 41.98s

@tyrelr
Copy link
Contributor

tyrelr commented Jul 22, 2022

That's interesting, @yzernik.

Nothing on the macro side inspects actual data rows within the tables, as far as I'm aware. Sqlx retrieves the declared table column types, and the query plan, and all calculations are based on those inputs. Based on that, I wouldn't expect the presence of data within a table to impact the outcome of the check. But I believe if the non-empty database has statistics, i suppose the query optimizer COULD select a different query plan, and that alternate query plan may be lucky enough to avoid this bug?

@yzernik
Copy link
Author

yzernik commented Jul 31, 2022

I realized that I can use "query" instead of "query!" for the queries where type inference fails as a workaround for now. Is it expected that these queries will work in v0.7?

@davidcornu
Copy link

Just ran into the same sort of error using query! against sqlite with a column that was the result of a join against a common table expression.

I realized that I can use "query" instead of "query!" for the queries where type inference fails as a workaround for now. Is it expected that these queries will work in v0.7?

I was still able to use query! by using a override (https://docs.rs/sqlx/latest/sqlx/macro.query.html#overrides-cheatsheet) on the affected column.

@sasacocic
Copy link

sasacocic commented Oct 24, 2022

I'm running into the same issue I believe. I have a small reproducible example using SQLite

Here's some relavent info

cargo.toml

sqlx = { version = "0.6.2", features = [ "sqlite", "runtime-actix-native-tls" ]}
actix-web = "4.2.1"

main.rs

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // .env file with DATABASE_URL=sqlite:sqlite.db
    let pool = SqlitePool::connect("sqlite.db").await.unwrap();

    let mut conn = pool.acquire().await.unwrap();

    sqlx::query!(
        r#"
        CREATE TABLE IF NOT EXISTS Ads (
            company,
            position,
            remote,
            apply_link,
            description,
            paid
        )
    "#
    )
    .execute(&mut conn)
    .await
    .unwrap();

   // insert data here if you wish

    let res = sqlx::query!(r#"select company, position from Ads"#)
        .fetch_all(&pool)
        .await
        .unwrap();
   Ok(())
}

You'll get these errors

error: unsupported type NULL of column #1 ("company")
   --> src/main.rs:123:15
    |
123 |     let res = sqlx::query!(r#"select company, position from Ads"#)
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this error originates in the macro `$crate::sqlx_macros::expand_query` (in Nightly builds, run with -Z macro-backtrace for more info)

error: unsupported type NULL of column #2 ("position")
   --> src/main.rs:123:15
    |
123 |     let res = sqlx::query!(r#"select company, position from Ads"#)
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this error originates in the macro `$crate::sqlx_macros::expand_query` (in Nightly builds, run with -Z macro-backtrace for more info)

glad to provide any more information if needed.

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

Successfully merging a pull request may close this issue.

5 participants