mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-02-14 20:07:31 +00:00
fix(sqlite): fallback to storage class when typing expressions and infer INTEGER as i64
This commit is contained in:
parent
513d666217
commit
cfa833fa0d
@ -10,6 +10,7 @@ use crate::type_info::TypeInfo;
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub(crate) enum DataType {
|
||||
Null,
|
||||
Int,
|
||||
Float,
|
||||
Text,
|
||||
@ -38,29 +39,30 @@ impl Display for SqliteTypeInfo {
|
||||
impl TypeInfo for SqliteTypeInfo {
|
||||
fn name(&self) -> &str {
|
||||
match self.0 {
|
||||
DataType::Null => "NULL",
|
||||
DataType::Text => "TEXT",
|
||||
DataType::Float => "FLOAT",
|
||||
DataType::Blob => "BLOB",
|
||||
DataType::Int => "INTEGER",
|
||||
DataType::Int | DataType::Int64 => "INTEGER",
|
||||
DataType::Numeric => "NUMERIC",
|
||||
|
||||
// non-standard extensions
|
||||
DataType::Bool => "BOOLEAN",
|
||||
DataType::Int64 => "BIGINT",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataType {
|
||||
pub(crate) fn from_code(code: c_int) -> Option<Self> {
|
||||
pub(crate) fn from_code(code: c_int) -> Self {
|
||||
match code {
|
||||
SQLITE_INTEGER => Some(DataType::Int),
|
||||
SQLITE_FLOAT => Some(DataType::Float),
|
||||
SQLITE_BLOB => Some(DataType::Blob),
|
||||
SQLITE_NULL => None,
|
||||
SQLITE_TEXT => Some(DataType::Text),
|
||||
SQLITE_INTEGER => DataType::Int,
|
||||
SQLITE_FLOAT => DataType::Float,
|
||||
SQLITE_BLOB => DataType::Blob,
|
||||
SQLITE_NULL => DataType::Null,
|
||||
SQLITE_TEXT => DataType::Text,
|
||||
|
||||
_ => None,
|
||||
// https://sqlite.org/c3ref/c_blob.html
|
||||
_ => panic!("unknown data type code {}", code),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,14 +76,11 @@ impl FromStr for DataType {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_ascii_lowercase();
|
||||
Ok(match &*s {
|
||||
"int4" => DataType::Int,
|
||||
"int8" => DataType::Int64,
|
||||
"boolean" | "bool" => DataType::Bool,
|
||||
|
||||
_ if s.contains("int") && s.contains("big") && s.find("int") > s.find("big") => {
|
||||
DataType::Int64
|
||||
}
|
||||
|
||||
_ if s.contains("int") => DataType::Int,
|
||||
_ if s.contains("int") => DataType::Int64,
|
||||
|
||||
_ if s.contains("char") || s.contains("clob") || s.contains("text") => DataType::Text,
|
||||
|
||||
@ -98,10 +97,12 @@ impl FromStr for DataType {
|
||||
|
||||
#[test]
|
||||
fn test_data_type_from_str() -> Result<(), BoxDynError> {
|
||||
assert_eq!(DataType::Int, "INT".parse()?);
|
||||
assert_eq!(DataType::Int, "INTEGER".parse()?);
|
||||
assert_eq!(DataType::Int, "INTBIG".parse()?);
|
||||
assert_eq!(DataType::Int, "MEDIUMINT".parse()?);
|
||||
assert_eq!(DataType::Int, "INT4".parse()?);
|
||||
|
||||
assert_eq!(DataType::Int64, "INT".parse()?);
|
||||
assert_eq!(DataType::Int64, "INTEGER".parse()?);
|
||||
assert_eq!(DataType::Int64, "INTBIG".parse()?);
|
||||
assert_eq!(DataType::Int64, "MEDIUMINT".parse()?);
|
||||
|
||||
assert_eq!(DataType::Int64, "BIGINT".parse()?);
|
||||
assert_eq!(DataType::Int64, "UNSIGNED BIG INT".parse()?);
|
||||
|
||||
@ -9,6 +9,10 @@ impl Type<Sqlite> for bool {
|
||||
fn type_info() -> SqliteTypeInfo {
|
||||
SqliteTypeInfo(DataType::Bool)
|
||||
}
|
||||
|
||||
fn compatible(ty: &SqliteTypeInfo) -> bool {
|
||||
matches!(ty.0, DataType::Bool | DataType::Int | DataType::Int64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Sqlite> for bool {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
@ -5,10 +7,62 @@ use crate::sqlite::type_info::DataType;
|
||||
use crate::sqlite::{Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef};
|
||||
use crate::types::Type;
|
||||
|
||||
impl Type<Sqlite> for i8 {
|
||||
fn type_info() -> SqliteTypeInfo {
|
||||
SqliteTypeInfo(DataType::Int)
|
||||
}
|
||||
|
||||
fn compatible(ty: &SqliteTypeInfo) -> bool {
|
||||
matches!(ty.0, DataType::Int | DataType::Int64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Sqlite> for i8 {
|
||||
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
|
||||
args.push(SqliteArgumentValue::Int(*self as i32));
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for i8 {
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.int().try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Sqlite> for i16 {
|
||||
fn type_info() -> SqliteTypeInfo {
|
||||
SqliteTypeInfo(DataType::Int)
|
||||
}
|
||||
|
||||
fn compatible(ty: &SqliteTypeInfo) -> bool {
|
||||
matches!(ty.0, DataType::Int | DataType::Int64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Sqlite> for i16 {
|
||||
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
|
||||
args.push(SqliteArgumentValue::Int(*self as i32));
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for i16 {
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.int().try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Sqlite> for i32 {
|
||||
fn type_info() -> SqliteTypeInfo {
|
||||
SqliteTypeInfo(DataType::Int)
|
||||
}
|
||||
|
||||
fn compatible(ty: &SqliteTypeInfo) -> bool {
|
||||
matches!(ty.0, DataType::Int | DataType::Int64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Sqlite> for i32 {
|
||||
@ -29,6 +83,10 @@ impl Type<Sqlite> for i64 {
|
||||
fn type_info() -> SqliteTypeInfo {
|
||||
SqliteTypeInfo(DataType::Int64)
|
||||
}
|
||||
|
||||
fn compatible(ty: &SqliteTypeInfo) -> bool {
|
||||
matches!(ty.0, DataType::Int | DataType::Int64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Sqlite> for i64 {
|
||||
|
||||
@ -83,9 +83,15 @@ impl<'r> ValueRef<'r> for SqliteValueRef<'r> {
|
||||
|
||||
fn type_info(&self) -> Option<Cow<'_, SqliteTypeInfo>> {
|
||||
match self.0 {
|
||||
SqliteValueData::Statement { statement, index } => {
|
||||
statement.column_decltype(index).map(Cow::Owned)
|
||||
}
|
||||
SqliteValueData::Statement { statement, index } => statement
|
||||
.column_decltype(index)
|
||||
.or_else(|| {
|
||||
// fall back to the storage class for expressions
|
||||
Some(SqliteTypeInfo(DataType::from_code(
|
||||
statement.column_type(index),
|
||||
)))
|
||||
})
|
||||
.map(Cow::Owned),
|
||||
|
||||
SqliteValueData::Value(v) => v.type_info(),
|
||||
}
|
||||
@ -115,7 +121,7 @@ impl SqliteValue {
|
||||
Self(Arc::new(NonNull::new_unchecked(sqlite3_value_dup(value))))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Option<DataType> {
|
||||
fn r#type(&self) -> DataType {
|
||||
DataType::from_code(unsafe { sqlite3_value_type(self.0.as_ptr()) })
|
||||
}
|
||||
|
||||
@ -158,7 +164,7 @@ impl Value for SqliteValue {
|
||||
}
|
||||
|
||||
fn type_info(&self) -> Option<Cow<'_, SqliteTypeInfo>> {
|
||||
self.r#type().map(SqliteTypeInfo).map(Cow::Owned)
|
||||
Some(Cow::Owned(SqliteTypeInfo(self.r#type())))
|
||||
}
|
||||
|
||||
fn is_null(&self) -> bool {
|
||||
|
||||
@ -72,6 +72,10 @@ fn expand_derive_has_sql_type_transparent(
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<#ty as sqlx::Type<DB>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||
<#ty as sqlx::Type<DB>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@ -29,10 +29,10 @@ async fn it_describes_simple() -> anyhow::Result<()> {
|
||||
|
||||
let column_type_names = type_names(&columns);
|
||||
|
||||
assert_eq!(column_type_names[0], "BIGINT");
|
||||
assert_eq!(column_type_names[0], "INTEGER");
|
||||
assert_eq!(column_type_names[1], "TEXT");
|
||||
assert_eq!(column_type_names[2], "BOOLEAN");
|
||||
assert_eq!(column_type_names[3], "BIGINT");
|
||||
assert_eq!(column_type_names[3], "INTEGER");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ async fn macro_select_bind() -> anyhow::Result<()> {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RawAccount {
|
||||
id: i32,
|
||||
id: i64,
|
||||
name: String,
|
||||
is_active: Option<bool>,
|
||||
}
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user