mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-03-06 05:42:38 +00:00
remove is_null_type and use Option<TypeInfo>
This commit is contained in:
parent
927e2b1586
commit
d380f4b4a8
@ -11,7 +11,7 @@ where
|
||||
DB: Database + ?Sized,
|
||||
{
|
||||
/// The expected types for the parameters of the query.
|
||||
pub param_types: Box<[DB::TypeInfo]>,
|
||||
pub param_types: Box<[Option<DB::TypeInfo>]>,
|
||||
|
||||
/// The type and table information, if any for the results of the query.
|
||||
pub result_columns: Box<[Column<DB>]>,
|
||||
@ -39,7 +39,7 @@ where
|
||||
{
|
||||
pub name: Option<Box<str>>,
|
||||
pub table_id: Option<DB::TableId>,
|
||||
pub type_info: DB::TypeInfo,
|
||||
pub type_info: Option<DB::TypeInfo>,
|
||||
/// Whether or not the column cannot be `NULL` (or if that is even knowable).
|
||||
pub non_null: Option<bool>,
|
||||
}
|
||||
|
||||
@ -43,13 +43,17 @@ impl MySqlTypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_column_def(def: &ColumnDefinition) -> Self {
|
||||
Self {
|
||||
pub(crate) fn from_column_def(def: &ColumnDefinition) -> Option<Self> {
|
||||
if def.type_id == TypeId::NULL {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
id: def.type_id,
|
||||
is_unsigned: def.flags.contains(FieldFlags::UNSIGNED),
|
||||
is_binary: def.flags.contains(FieldFlags::BINARY),
|
||||
char_set: def.char_set,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -106,10 +110,6 @@ 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>
|
||||
|
||||
@ -199,7 +199,7 @@ impl PgConnection {
|
||||
param_types: params
|
||||
.ids
|
||||
.iter()
|
||||
.map(|id| PgTypeInfo::new(*id, &type_names[&id.0]))
|
||||
.map(|id| Some(PgTypeInfo::new(*id, &type_names[&id.0])))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
result_columns: self
|
||||
@ -315,7 +315,10 @@ impl PgConnection {
|
||||
Ok(Column {
|
||||
name: field.name,
|
||||
table_id: field.table_id,
|
||||
type_info: PgTypeInfo::new(field.type_id, &type_names[&field.type_id.0]),
|
||||
type_info: Some(PgTypeInfo::new(
|
||||
field.type_id,
|
||||
&type_names[&field.type_id.0],
|
||||
)),
|
||||
non_null,
|
||||
})
|
||||
})
|
||||
|
||||
@ -81,11 +81,6 @@ 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>
|
||||
|
||||
@ -138,14 +138,7 @@ impl Executor for SqliteConnection {
|
||||
// First let's attempt to describe what we can about parameter types
|
||||
// Which happens to just be the count, heh
|
||||
let num_params = statement.params();
|
||||
let params = vec![
|
||||
SqliteTypeInfo {
|
||||
r#type: SqliteType::Null,
|
||||
affinity: None,
|
||||
};
|
||||
num_params
|
||||
]
|
||||
.into_boxed_slice();
|
||||
let params = vec![None; num_params].into_boxed_slice();
|
||||
|
||||
// Next, collect (return) column types and names
|
||||
let num_columns = statement.column_count();
|
||||
@ -155,15 +148,15 @@ impl Executor for SqliteConnection {
|
||||
let decl = statement.column_decltype(i);
|
||||
|
||||
let r#type = match decl {
|
||||
None => SqliteType::Null,
|
||||
None => None,
|
||||
Some(decl) => match &*decl.to_ascii_lowercase() {
|
||||
"bool" | "boolean" => SqliteType::Boolean,
|
||||
"clob" | "text" => SqliteType::Text,
|
||||
"blob" => SqliteType::Blob,
|
||||
"real" | "double" | "double precision" | "float" => SqliteType::Float,
|
||||
decl @ _ if decl.contains("int") => SqliteType::Integer,
|
||||
decl @ _ if decl.contains("char") => SqliteType::Text,
|
||||
_ => SqliteType::Null,
|
||||
"bool" | "boolean" => Some(SqliteType::Boolean),
|
||||
"clob" | "text" => Some(SqliteType::Text),
|
||||
"blob" => Some(SqliteType::Blob),
|
||||
"real" | "double" | "double precision" | "float" => Some(SqliteType::Float),
|
||||
decl @ _ if decl.contains("int") => Some(SqliteType::Integer),
|
||||
decl @ _ if decl.contains("char") => Some(SqliteType::Text),
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
|
||||
@ -171,10 +164,10 @@ impl Executor for SqliteConnection {
|
||||
name: Some(name.into()),
|
||||
non_null: None,
|
||||
table_id: None,
|
||||
type_info: SqliteTypeInfo {
|
||||
type_info: r#type.map(|r#type| SqliteTypeInfo {
|
||||
r#type,
|
||||
affinity: None,
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -66,10 +66,6 @@ impl TypeInfo for SqliteTypeInfo {
|
||||
fn compatible(&self, other: &Self) -> bool {
|
||||
self.r#type == other.r#type || 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,14 +18,6 @@ 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.
|
||||
|
||||
@ -30,11 +30,14 @@ pub fn quote_args<DB: DatabaseExt>(
|
||||
.iter()
|
||||
.zip(input.arg_names.iter().zip(&input.arg_exprs))
|
||||
.enumerate()
|
||||
.map(|(i, (param_ty, (name, expr)))| -> crate::Result<_>{
|
||||
.map(|(i, (param_ty, (name, expr)))| -> crate::Result<_> {
|
||||
// TODO: We could remove the ParamChecking flag and just filter to only test params that are non-null
|
||||
let param_ty = param_ty.as_ref().unwrap();
|
||||
|
||||
let param_ty = get_type_override(expr)
|
||||
.or_else(|| {
|
||||
Some(
|
||||
DB::param_type_for_id(param_ty)?
|
||||
DB::param_type_for_id(¶m_ty)?
|
||||
.parse::<proc_macro2::TokenStream>()
|
||||
.unwrap(),
|
||||
)
|
||||
|
||||
@ -45,44 +45,51 @@ pub fn columns_to_rust<DB: DatabaseExt>(describe: &Describe<DB>) -> crate::Resul
|
||||
|
||||
let ident = parse_ident(name)?;
|
||||
|
||||
let type_ = <DB as DatabaseExt>::return_type_for_id(&column.type_info)
|
||||
.ok_or_else(|| {
|
||||
if let Some(feature_gate) =
|
||||
<DB as DatabaseExt>::get_feature_gate(&column.type_info)
|
||||
{
|
||||
format!(
|
||||
"optional feature `{feat}` required for type {ty} of {col}",
|
||||
ty = &column.type_info,
|
||||
feat = feature_gate,
|
||||
col = DisplayColumn {
|
||||
idx: i,
|
||||
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}",
|
||||
ty = column.type_info,
|
||||
col = DisplayColumn {
|
||||
idx: i,
|
||||
name: column.name.as_deref()
|
||||
}
|
||||
)
|
||||
let type_ = if let Some(type_info) = &column.type_info {
|
||||
<DB as DatabaseExt>::return_type_for_id(&type_info)
|
||||
.ok_or_else(|| {
|
||||
if let Some(feature_gate) =
|
||||
<DB as DatabaseExt>::get_feature_gate(&type_info)
|
||||
{
|
||||
format!(
|
||||
"optional feature `{feat}` required for type {ty} of {col}",
|
||||
ty = &type_info,
|
||||
feat = feature_gate,
|
||||
col = DisplayColumn {
|
||||
idx: i,
|
||||
name: column.name.as_deref()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"unsupported type {ty} of {col}",
|
||||
ty = type_info,
|
||||
col = DisplayColumn {
|
||||
idx: i,
|
||||
name: column.name.as_deref()
|
||||
}
|
||||
)
|
||||
}
|
||||
})?
|
||||
.parse::<TokenStream>()
|
||||
.unwrap()
|
||||
} else {
|
||||
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()
|
||||
}
|
||||
})?
|
||||
)
|
||||
.parse::<TokenStream>()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
Ok(RustColumn { ident, type_ })
|
||||
Ok(RustColumn {
|
||||
ident,
|
||||
type_: type_,
|
||||
})
|
||||
})
|
||||
.collect::<crate::Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
@ -230,13 +230,41 @@ async fn test_describe() -> anyhow::Result<()> {
|
||||
.await?;
|
||||
|
||||
assert_eq!(describe.result_columns[0].non_null, Some(true));
|
||||
assert_eq!(describe.result_columns[0].type_info.type_name(), "INT4");
|
||||
assert_eq!(
|
||||
describe.result_columns[0]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.type_name(),
|
||||
"INT4"
|
||||
);
|
||||
assert_eq!(describe.result_columns[1].non_null, Some(true));
|
||||
assert_eq!(describe.result_columns[1].type_info.type_name(), "TEXT");
|
||||
assert_eq!(
|
||||
describe.result_columns[1]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.type_name(),
|
||||
"TEXT"
|
||||
);
|
||||
assert_eq!(describe.result_columns[2].non_null, Some(false));
|
||||
assert_eq!(describe.result_columns[2].type_info.type_name(), "BYTEA");
|
||||
assert_eq!(
|
||||
describe.result_columns[2]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.type_name(),
|
||||
"BYTEA"
|
||||
);
|
||||
assert_eq!(describe.result_columns[3].non_null, None);
|
||||
assert_eq!(describe.result_columns[3].type_info.type_name(), "BOOL");
|
||||
assert_eq!(
|
||||
describe.result_columns[3]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.type_name(),
|
||||
"BOOL"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -21,9 +21,12 @@ async fn macro_select() -> anyhow::Result<()> {
|
||||
async fn macro_select_bind() -> anyhow::Result<()> {
|
||||
let mut conn = new::<Sqlite>().await?;
|
||||
|
||||
let account = sqlx::query!("select id, name, is_active from accounts where id = ?", 1i32)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
let account = sqlx::query!(
|
||||
"select id, name, is_active from accounts where id = ?",
|
||||
1i32
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert_eq!(1, account.id);
|
||||
assert_eq!("Herp Derpinson", account.name);
|
||||
|
||||
@ -129,17 +129,73 @@ CREATE TEMPORARY TABLE describe_test (
|
||||
.describe("select nt.*, false from describe_test nt")
|
||||
.await?;
|
||||
|
||||
assert_eq!(describe.result_columns[0].type_info.to_string(), "INTEGER");
|
||||
assert_eq!(describe.result_columns[1].type_info.to_string(), "TEXT");
|
||||
assert_eq!(describe.result_columns[2].type_info.to_string(), "BLOB");
|
||||
assert_eq!(describe.result_columns[3].type_info.to_string(), "BOOLEAN");
|
||||
assert_eq!(describe.result_columns[4].type_info.to_string(), "DOUBLE");
|
||||
assert_eq!(describe.result_columns[5].type_info.to_string(), "TEXT");
|
||||
assert_eq!(describe.result_columns[6].type_info.to_string(), "DOUBLE");
|
||||
assert_eq!(describe.result_columns[7].type_info.to_string(), "INTEGER");
|
||||
assert_eq!(
|
||||
describe.result_columns[0]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"INTEGER"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[1]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"TEXT"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[2]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"BLOB"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[3]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"BOOLEAN"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[4]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"DOUBLE"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[5]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"TEXT"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[6]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"DOUBLE"
|
||||
);
|
||||
assert_eq!(
|
||||
describe.result_columns[7]
|
||||
.type_info
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"INTEGER"
|
||||
);
|
||||
|
||||
// Expressions can not be described
|
||||
assert_eq!(describe.result_columns[8].type_info.to_string(), "NULL");
|
||||
assert!(describe.result_columns[8].type_info.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user