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 Null,
16 Integer,
20 Float,
21 Text,
22 Blob,
23
24 #[allow(dead_code)]
26 Numeric,
27
28 Bool,
31 Int4,
35 Date,
36 Time,
37 Datetime,
38}
39
40#[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 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 _ => panic!("unknown data type code {code}"),
85 }
86 }
87}
88
89impl 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#[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}