mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-01-06 08:42:06 +00:00
macros + sqlite: fix error for null-typed columns
This commit is contained in:
parent
2a76123bc3
commit
8328e07c97
@ -106,6 +106,10 @@ impl TypeInfo for MySqlTypeInfo {
|
||||
_ => self.id.0 == other.id.0 && self.is_unsigned == other.is_unsigned,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_null_type(&self) -> bool {
|
||||
self.id == TypeId::NULL
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Decode<'de, MySql> for Option<T>
|
||||
|
||||
@ -81,6 +81,11 @@ impl TypeInfo for PgTypeInfo {
|
||||
// TODO: 99% of postgres types are direct equality for [compatible]; when we add something that isn't (e.g, JSON/JSONB), fix this here
|
||||
self.id.0 == other.id.0
|
||||
}
|
||||
|
||||
fn is_null_type(&self) -> bool {
|
||||
// Postgres doesn't have a "null" type
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Decode<'de, Postgres> for Option<T>
|
||||
|
||||
@ -66,6 +66,10 @@ impl TypeInfo for SqliteTypeInfo {
|
||||
fn compatible(&self, other: &Self) -> bool {
|
||||
self.affinity == other.affinity
|
||||
}
|
||||
|
||||
fn is_null_type(&self) -> bool {
|
||||
self.r#type == SqliteType::Null
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Decode<'de, Sqlite> for Option<T>
|
||||
|
||||
@ -18,6 +18,14 @@ pub trait TypeInfo: Debug + Display + Clone {
|
||||
/// Compares type information to determine if `other` is compatible at the Rust level
|
||||
/// with `self`.
|
||||
fn compatible(&self, other: &Self) -> bool;
|
||||
|
||||
/// Return `true` if this is the database flavor's "null" sentinel type.
|
||||
///
|
||||
/// For type info coming from the description of a prepared statement, this means
|
||||
/// that the server could not infer the expected type of a bind parameter or result column;
|
||||
/// the latter is most often the case with columns that are the result of an expression
|
||||
/// in a weakly-typed database like MySQL or SQLite.
|
||||
fn is_null_type(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Indicates that a SQL type is supported for a database.
|
||||
|
||||
@ -3,6 +3,7 @@ use quote::quote;
|
||||
use syn::Path;
|
||||
|
||||
use sqlx::describe::Describe;
|
||||
use sqlx::types::TypeInfo;
|
||||
|
||||
use crate::database::DatabaseExt;
|
||||
|
||||
@ -58,6 +59,15 @@ pub fn columns_to_rust<DB: DatabaseExt>(describe: &Describe<DB>) -> crate::Resul
|
||||
name: column.name.as_deref()
|
||||
}
|
||||
)
|
||||
} else if column.type_info.is_null_type() {
|
||||
format!(
|
||||
"database couldn't tell us the type of {col}; \
|
||||
this can happen for columns that are the result of an expression",
|
||||
col = DisplayColumn {
|
||||
idx: i,
|
||||
name: column.name.as_deref()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"unsupported type {ty} of {col}",
|
||||
|
||||
61
tests/sqlite-macros.rs
Normal file
61
tests/sqlite-macros.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use sqlx::Sqlite;
|
||||
use sqlx_test::new;
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn macro_select_from_cte() -> anyhow::Result<()> {
|
||||
let mut conn = new::<Sqlite>().await?;
|
||||
let account = sqlx::query!(
|
||||
"with accounts(id, name) as (values (1, 'Herp Derpinson')) select * from accounts"
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
println!("{:?}", account);
|
||||
println!("{}: {}", account.id, account.name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn macro_select_from_cte_bind() -> anyhow::Result<()> {
|
||||
let mut conn = new::<Sqlite>().await?;
|
||||
let account = sqlx::query!(
|
||||
"with accounts(id, name) as (select 1, 'Herp Derpinson') select * from accounts where id = ?",
|
||||
1i32
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
println!("{:?}", account);
|
||||
println!("{}: {}", account.id, account.name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RawAccount {
|
||||
r#type: i32,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn test_query_as_raw() -> anyhow::Result<()> {
|
||||
let mut conn = new::<Sqlite>().await?;
|
||||
|
||||
let account = sqlx::query_as!(
|
||||
RawAccount,
|
||||
"SELECT * from (select 1 as type, cast(null as char) as name) accounts"
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert_eq!(None, account.name);
|
||||
assert_eq!(1, account.r#type);
|
||||
|
||||
println!("{:?}", account);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -24,5 +24,9 @@ fn ui_tests() {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg!(feature = "sqlite") {
|
||||
t.compile_fail("tests/ui/sqlite/*.rs");
|
||||
}
|
||||
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
||||
|
||||
3
tests/ui/sqlite/expression-column-type.rs
Normal file
3
tests/ui/sqlite/expression-column-type.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
let _ = sqlx::query!("select 1 as id");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user