sqlx_sqlite/
type_info.rs

1use std::fmt::{self, Display, Formatter};
2use std::os::raw::c_int;
3use std::str::FromStr;
4
5use libsqlite3_sys::{SQLITE_BLOB, SQLITE_FLOAT, SQLITE_INTEGER, SQLITE_NULL, SQLITE_TEXT};
6
7use crate::error::BoxDynError;
8
9pub(crate) use sqlx_core::type_info::*;
10
11#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
12#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
13pub(crate) enum DataType {
14    // These variants should correspond to `SQLITE_*` type constants.
15    Null,
16    /// Note: SQLite's type system has no notion of integer widths.
17    /// The `INTEGER` type affinity can store up to 8 byte integers,
18    /// making `i64` the only safe choice when mapping integer types to Rust.
19    Integer,
20    Float,
21    Text,
22    Blob,
23
24    // Explicitly not supported: see documentation in `types/mod.rs`
25    #[allow(dead_code)]
26    Numeric,
27
28    // non-standard extensions (chosen based on the column's declared type)
29    /// Chosen if the column's declared type is `BOOLEAN`.
30    Bool,
31    /// Chosen if the column's declared type is `INT4`;
32    /// instructs the macros to use `i32` instead of `i64`.
33    /// Legacy feature; no idea if this is actually used anywhere.
34    Int4,
35    Date,
36    Time,
37    Datetime,
38}
39
40/// Type information for a SQLite type.
41#[derive(Debug, Clone, Eq, PartialEq, Hash)]
42#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
43pub struct SqliteTypeInfo(pub(crate) DataType);
44
45impl Display for SqliteTypeInfo {
46    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47        f.pad(self.name())
48    }
49}
50
51impl TypeInfo for SqliteTypeInfo {
52    fn is_null(&self) -> bool {
53        matches!(self.0, DataType::Null)
54    }
55
56    fn name(&self) -> &str {
57        match self.0 {
58            DataType::Null => "NULL",
59            DataType::Text => "TEXT",
60            DataType::Float => "REAL",
61            DataType::Blob => "BLOB",
62            DataType::Int4 | DataType::Integer => "INTEGER",
63            DataType::Numeric => "NUMERIC",
64
65            // non-standard extensions
66            DataType::Bool => "BOOLEAN",
67            DataType::Date => "DATE",
68            DataType::Time => "TIME",
69            DataType::Datetime => "DATETIME",
70        }
71    }
72}
73
74impl DataType {
75    pub(crate) fn from_code(code: c_int) -> Self {
76        match code {
77            SQLITE_INTEGER => DataType::Integer,
78            SQLITE_FLOAT => DataType::Float,
79            SQLITE_BLOB => DataType::Blob,
80            SQLITE_NULL => DataType::Null,
81            SQLITE_TEXT => DataType::Text,
82
83            // https://sqlite.org/c3ref/c_blob.html
84            _ => panic!("unknown data type code {code}"),
85        }
86    }
87}
88
89// note: this implementation is particularly important as this is how the macros determine
90//       what Rust type maps to what *declared* SQL type
91// <https://www.sqlite.org/datatype3.html#affname>
92impl FromStr for DataType {
93    type Err = BoxDynError;
94
95    fn from_str(s: &str) -> Result<Self, Self::Err> {
96        let s = s.to_ascii_lowercase();
97        Ok(match &*s {
98            "int4" => DataType::Int4,
99            "int8" => DataType::Integer,
100            "boolean" | "bool" => DataType::Bool,
101
102            "date" => DataType::Date,
103            "time" => DataType::Time,
104            "datetime" | "timestamp" => DataType::Datetime,
105
106            _ if s.contains("int") => DataType::Integer,
107
108            _ if s.contains("char") || s.contains("clob") || s.contains("text") => DataType::Text,
109
110            _ if s.contains("blob") => DataType::Blob,
111
112            _ if s.contains("real") || s.contains("floa") || s.contains("doub") => DataType::Float,
113
114            _ => {
115                return Err(format!("unknown type: `{s}`").into());
116            }
117        })
118    }
119}
120
121// #[cfg(feature = "any")]
122// impl From<SqliteTypeInfo> for crate::any::AnyTypeInfo {
123//     #[inline]
124//     fn from(ty: SqliteTypeInfo) -> Self {
125//         crate::any::AnyTypeInfo(crate::any::type_info::AnyTypeInfoKind::Sqlite(ty))
126//     }
127// }
128
129#[test]
130fn test_data_type_from_str() -> Result<(), BoxDynError> {
131    assert_eq!(DataType::Int4, "INT4".parse()?);
132
133    assert_eq!(DataType::Integer, "INT".parse()?);
134    assert_eq!(DataType::Integer, "INTEGER".parse()?);
135    assert_eq!(DataType::Integer, "INTBIG".parse()?);
136    assert_eq!(DataType::Integer, "MEDIUMINT".parse()?);
137
138    assert_eq!(DataType::Integer, "BIGINT".parse()?);
139    assert_eq!(DataType::Integer, "UNSIGNED BIG INT".parse()?);
140    assert_eq!(DataType::Integer, "INT8".parse()?);
141
142    assert_eq!(DataType::Text, "CHARACTER(20)".parse()?);
143    assert_eq!(DataType::Text, "NCHAR(55)".parse()?);
144    assert_eq!(DataType::Text, "TEXT".parse()?);
145    assert_eq!(DataType::Text, "CLOB".parse()?);
146
147    assert_eq!(DataType::Blob, "BLOB".parse()?);
148
149    assert_eq!(DataType::Float, "REAL".parse()?);
150    assert_eq!(DataType::Float, "FLOAT".parse()?);
151    assert_eq!(DataType::Float, "DOUBLE PRECISION".parse()?);
152
153    assert_eq!(DataType::Bool, "BOOLEAN".parse()?);
154    assert_eq!(DataType::Bool, "BOOL".parse()?);
155
156    assert_eq!(DataType::Datetime, "DATETIME".parse()?);
157    assert_eq!(DataType::Time, "TIME".parse()?);
158    assert_eq!(DataType::Date, "DATE".parse()?);
159
160    Ok(())
161}