Skip to main content

rbdc_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};
6use rbdc::Error;
7use rbs::Value;
8
9pub trait Type {
10    fn type_info(&self) -> SqliteTypeInfo;
11}
12
13// for optionals, the underlying SQL type is identical
14impl<T: Type> Type for Option<T> {
15    fn type_info(&self) -> SqliteTypeInfo {
16        match self {
17            None => SqliteTypeInfo(DataType::Null),
18            Some(v) => v.type_info(),
19        }
20    }
21}
22
23#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
24pub(crate) enum DataType {
25    Null,
26    Int,
27    Float,
28    Text,
29    Blob,
30
31    Numeric,
32
33    // non-standard extensions
34    Bool,
35    Int64,
36    Date,
37    Time,
38    Datetime,
39}
40
41/// Type information for a SQLite type.
42#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
43pub struct SqliteTypeInfo(pub(crate) DataType);
44
45impl SqliteTypeInfo {
46    pub fn null() -> Self {
47        SqliteTypeInfo(DataType::Null)
48    }
49}
50
51impl Display for SqliteTypeInfo {
52    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53        f.pad(self.name())
54    }
55}
56
57impl SqliteTypeInfo {
58    pub fn is_null(&self) -> bool {
59        matches!(self.0, DataType::Null)
60    }
61
62    pub fn name(&self) -> &str {
63        match self.0 {
64            DataType::Null => "NULL",
65            DataType::Text => "TEXT",
66            DataType::Float => "REAL",
67            DataType::Blob => "BLOB",
68            DataType::Int | DataType::Int64 => "INTEGER",
69            DataType::Numeric => "NUMERIC",
70
71            // non-standard extensions
72            DataType::Bool => "BOOLEAN",
73            DataType::Date => "DATE",
74            DataType::Time => "TIME",
75            DataType::Datetime => "DATETIME",
76        }
77    }
78}
79
80impl DataType {
81    pub(crate) fn from_code(code: c_int) -> Self {
82        match code {
83            SQLITE_INTEGER => DataType::Int,
84            SQLITE_FLOAT => DataType::Float,
85            SQLITE_BLOB => DataType::Blob,
86            SQLITE_NULL => DataType::Null,
87            SQLITE_TEXT => DataType::Text,
88
89            // https://sqlite.org/c3ref/c_blob.html
90            // SQLite type codes are exhaustive; reaching here indicates a library bug
91            _ => unreachable!("unknown SQLite data type code {} - this indicates a version mismatch between libsqlite3_sys and the SQLite library", code),
92        }
93    }
94}
95
96// note: this implementation is particularly important as this is how the macros determine
97//       what Rust type maps to what *declared* SQL type
98// <https://www.sqlite.org/datatype3.html#affname>
99impl FromStr for DataType {
100    type Err = Error;
101
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        let s = s.to_ascii_lowercase();
104        Ok(match &*s {
105            "int4" => DataType::Int,
106            "int8" => DataType::Int64,
107            "boolean" | "bool" => DataType::Bool,
108
109            "date" => DataType::Date,
110            "time" => DataType::Time,
111            "datetime" | "timestamp" => DataType::Datetime,
112
113            _ if s.contains("int") => DataType::Int64,
114
115            _ if s.contains("char") || s.contains("clob") || s.contains("text") => DataType::Text,
116
117            _ if s.contains("blob") => DataType::Blob,
118
119            _ if s.contains("real") || s.contains("floa") || s.contains("doub") => DataType::Float,
120
121            _ if s.contains("num") || s.contains("dec") => DataType::Numeric,
122
123            _ => {
124                return Err(format!("unknown type: `{}`", s).into());
125            }
126        })
127    }
128}
129
130impl Type for Value {
131    fn type_info(&self) -> SqliteTypeInfo {
132        match self {
133            Value::Null => SqliteTypeInfo::null(),
134            Value::Bool(_) => SqliteTypeInfo(DataType::Bool),
135            Value::I32(_) => SqliteTypeInfo(DataType::Int),
136            Value::I64(_) => SqliteTypeInfo(DataType::Int64),
137            Value::U32(_) => SqliteTypeInfo(DataType::Int),
138            Value::U64(_) => SqliteTypeInfo(DataType::Int64),
139            Value::F32(_) => SqliteTypeInfo(DataType::Float),
140            Value::F64(_) => SqliteTypeInfo(DataType::Float),
141            Value::String(_) => SqliteTypeInfo(DataType::Text),
142            Value::Binary(_) => SqliteTypeInfo(DataType::Blob),
143            Value::Array(_) => SqliteTypeInfo(DataType::Null),
144            Value::Map(_) => SqliteTypeInfo(DataType::Null),
145            Value::Ext(t, _) => match *t {
146                "Date" => SqliteTypeInfo(DataType::Text),
147                "DateTime" => SqliteTypeInfo(DataType::Text),
148                "Time" => SqliteTypeInfo(DataType::Text),
149                "Timestamp" => SqliteTypeInfo(DataType::Int64),
150                "Decimal" => SqliteTypeInfo(DataType::Numeric),
151                "Json" => SqliteTypeInfo(DataType::Blob),
152                "Uuid" => SqliteTypeInfo(DataType::Text),
153                _ => SqliteTypeInfo(DataType::Null),
154            },
155        }
156    }
157}
158
159#[test]
160fn test_data_type_from_str() -> Result<(), Error> {
161    assert_eq!(DataType::Int, "INT4".parse()?);
162
163    assert_eq!(DataType::Int64, "INT".parse()?);
164    assert_eq!(DataType::Int64, "INTEGER".parse()?);
165    assert_eq!(DataType::Int64, "INTBIG".parse()?);
166    assert_eq!(DataType::Int64, "MEDIUMINT".parse()?);
167
168    assert_eq!(DataType::Int64, "BIGINT".parse()?);
169    assert_eq!(DataType::Int64, "UNSIGNED BIG INT".parse()?);
170    assert_eq!(DataType::Int64, "INT8".parse()?);
171
172    assert_eq!(DataType::Text, "CHARACTER(20)".parse()?);
173    assert_eq!(DataType::Text, "NCHAR(55)".parse()?);
174    assert_eq!(DataType::Text, "TEXT".parse()?);
175    assert_eq!(DataType::Text, "CLOB".parse()?);
176
177    assert_eq!(DataType::Blob, "BLOB".parse()?);
178
179    assert_eq!(DataType::Float, "REAL".parse()?);
180    assert_eq!(DataType::Float, "FLOAT".parse()?);
181    assert_eq!(DataType::Float, "DOUBLE PRECISION".parse()?);
182
183    assert_eq!(DataType::Numeric, "NUMERIC".parse()?);
184    assert_eq!(DataType::Numeric, "DECIMAL".parse()?);
185    assert_eq!(DataType::Numeric, "DECIMAL(10,2)".parse()?);
186
187    assert_eq!(DataType::Bool, "BOOLEAN".parse()?);
188    assert_eq!(DataType::Bool, "BOOL".parse()?);
189
190    assert_eq!(DataType::Datetime, "DATETIME".parse()?);
191    assert_eq!(DataType::Time, "TIME".parse()?);
192    assert_eq!(DataType::Date, "DATE".parse()?);
193
194    Ok(())
195}