2

I have two Rust methods selecting data with sqlx from the same SQLite table depending on two different parameters.

I cannot manage to have both working due to an expected `i64`, found enum `std::option::Option` error.

Code

// src/main.rs
use tokio;                                                                                     

use anyhow::Result;
use sqlx::sqlite::SqlitePool;                                             

// The model `StorageName` that I'm retrieving is something like
pub struct StorageName {                                                                       
    pub _id: i64,
    pub name: String,                                                                          
    pub url: String,                                                                           
}                                                                                              

// This compiles only if `_id` is `Option<i64>`
async fn queryByName(pool: &SqlitePool, name: String) -> Result<Vec<StorageName>> {            
    let results = sqlx::query_as!(                                                             
        StorageName,                                                                           
        "SELECT * FROM names                                                                   
        WHERE name = ?;",                                                                      
        name,                                                                                  
    )                                                                                          
    .fetch_all(pool)                                                                           
    .await?;                                                                                   
    Ok(results)                                                                                
}                                                                                              

// This compiles if `_id` is `i64`
// Also querying by `_id` is ok
async fn queryByURL(pool: &SqlitePool, url: String) -> Result<Vec<StorageName>> {              
    let results = sqlx::query_as!(                                                             
        StorageName,                                                                           
        "SELECT * FROM names                                                                   
        WHERE url = ?;",                                                                       
        url,                                                                                   
    )                                                                                          
    .fetch_all(pool)                                                                           
    .await?;                                                                                   
    Ok(results)                                                                                
}                                                                                              
                                                                                               
#[tokio::main]                                                                                 
async fn main() -> Result<()> {                                                                                                                           
    Ok(())                                                                                     
}

sqllite .schema names returns

CREATE TABLE names (
  _id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  url TEXT NOT NULL,
  UNIQUE(name, url)
);

Cargo.toml dependencies:

[dependencies]    
anyhow = "1.0.44"         
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "sqlite" ] }                    
tokio = { version = "1.12.0", features = ["full"] }

How can I solve this? _id cannot be both Option and i64.

[updated] I've the suspect that the plural in the table's name collides with the field someway.

Rust version is 1.56.0

smitop
  • 4,770
  • 2
  • 20
  • 53
AleG
  • 108
  • 9
  • Please post your database schema so we can help you out. – justinas Feb 17 '22 at 13:44
  • ok, I've updated the question – AleG Feb 17 '22 at 14:07
  • For sqlite `i64/u64` is for `BIGINT, INT8`. But that doesn't seem to be the issue. Somehow nullability inference of sqlx seems to have a problem for that very query. You can solve it with [force non-null](https://docs.rs/sqlx/latest/sqlx/macro.query.html#force-not-null). But I've no idea why the inference fails. Sqlx does a lot of stuff to figure out null/non-null. Might be a better issue on their issue tracker if you provide the whole schema etc – peterulb Feb 17 '22 at 14:53
  • forcing non-null on `_id` works - thank you!!!! Maybe there's something cached somewhere - I've migrated the project from Diesel and maybe this leaved something behind. – AleG Feb 17 '22 at 16:58
  • I've updated the question with a full example. This happens even on a new project, so I wasn't in luck with my previous comment. I think that the problematic method uses a field called as the table itself, the only difference being the field is singular and the table is plural (this situation wasn't in the previous code samples) and this causes compilation problems.. – AleG Feb 18 '22 at 11:35
  • does the compiler expect an option because _id can be null? – Paul Oskar Mayer Feb 24 '22 at 14:15
  • [According to the SQL standard, PRIMARY KEY should always imply NOT NULL. ... due to a bug in some early versions, this is not the case in SQLite.](https://sqlite.org/lang_createtable.html) whoa, I didn't know this. Thank you! Modifying `_id` to also be `NOT NULL` works. It bugs me how to solve the `Option<>` deadlock in the case one can't modify the schema...I opened [an issue](https://github.com/launchbadge/sqlx/issues/1717) on the project' repo. – AleG Feb 28 '22 at 09:23

2 Answers2

3

Add AS "<field name><symbol>" to a row that causes this error.
<symbol> is: ? - if the field is optional and ! if not.

In your case this should solve the issue:

SELECT 
  _id AS "_id!",
  name AS "name!",
  url AS "url!"
FROM names                                                                   
WHERE name = ?;

Docs on type overrides:

https://docs.rs/sqlx/0.5.13/sqlx/macro.query.html#type-overrides-output-columns

Cheatsheet:

enter image description here

(yeah I'm late but I'll leave this here if anyone else has the same issue)

griffi-gh
  • 76
  • 5
1

IMHO, SELECT * should be avoided in production code. It might cause a problem when a new column is added and introduce unnecessary coupling between DB schema and application code.

As for struct fields, we should make it Option because once we specify which column we want (instead of *), there will be times when some columns are not selected and put to struct (hence, None)