datasources_sqlite/
driver.rs1use crate::connection::SqliteConnection;
2use crate::converters;
3use crate::statement_renderer::SqliteStatementRenderer;
4use crate::transaction::SqliteTransaction;
5use crate::sql_generate;
6use datasources::{
7 downcast_connection, Command, Connection, DatasourceError, DefaultValueConverter, Driver,
8 Information, IsolationLevel, Mapping, Result, StatementTextRenderer, Transaction, TypeConverter,
9 Value,
10};
11use std::collections::HashMap;
12use std::sync::{Arc, LazyLock};
13
14pub struct SqliteDriver;
15
16impl SqliteDriver {
17 pub const QUOTE_LEFT: &'static str = sql_generate::QUOTE_LEFT;
18 pub const QUOTE_RIGHT: &'static str = sql_generate::QUOTE_RIGHT;
19
20 pub fn statement_text_renderer() -> &'static StatementTextRenderer {
21 static DRIVER: SqliteDriver = SqliteDriver;
22 Driver::statement_renderer(&DRIVER).expect("sqlite")
23 }
24}
25
26static STATEMENTS: LazyLock<StatementTextRenderer> =
27 LazyLock::new(|| StatementTextRenderer::new(Some(Box::new(SqliteStatementRenderer)), None));
28
29static INFORMATION: LazyLock<Information> = LazyLock::new(|| {
30 Information::new(
31 [
32 "Embedded SQL database for local development and tests.",
33 "Full connection, command, transaction, and repository support.",
34 ],
35 [
36 "Overrides SelectInsertedIdStatement to SELECT last_insert_rowid() AS {column}",
37 "Overrides CreateTableStatement type mapping (int→INTEGER, bool→BOOLEAN, uuid/string/datetime→TEXT, decimal→NUMERIC, float→REAL, binary→BLOB)",
38 ],
39 [] as [&str; 0],
40 )
41});
42
43pub(crate) fn column_sql(name: &str) -> Option<&'static str> {
44 Some(match name {
45 "int" => "INTEGER",
46 "bool" => "BOOLEAN",
47 "uuid" | "string" | "datetime" => "TEXT",
48 "decimal" => "NUMERIC",
49 "float" => "REAL",
50 "binary" => "BLOB",
51 _ => return None,
52 })
53}
54
55impl Driver for SqliteDriver {
56 fn connection(&self, options: Option<&HashMap<String, String>>) -> Arc<dyn Connection> {
57 SqliteConnection::from_options(options)
58 }
59
60 fn command(&self, connection: Arc<dyn Connection>) -> Arc<dyn Command> {
61 connection.create_command()
62 }
63
64 fn transaction(
65 &self,
66 connection: Arc<dyn Connection>,
67 isolation: IsolationLevel,
68 ) -> Arc<dyn Transaction> {
69 let conn = downcast_connection::<SqliteConnection>(connection).expect("sqlite");
70 Arc::new(SqliteTransaction::new(conn.share(), isolation))
71 }
72
73 fn row_type_converters(
74 &self,
75 ) -> HashMap<String, Box<dyn TypeConverter<Value, Value> + Send + Sync>> {
76 converters::row_type_converters()
77 }
78
79 fn column_type(&self, name: &str) -> Result<String> {
80 column_sql(name)
81 .map(str::to_string)
82 .ok_or_else(|| DatasourceError::message(format!("Unknown sqlite type \"{name}\"")))
83 }
84
85 fn type_converter(&self) -> Option<&dyn Mapping<String, String>> {
86 Some(&*sql_generate::TYPES)
87 }
88
89 fn default_value_converter(&self) -> Option<&DefaultValueConverter> {
90 Some(&sql_generate::DEFAULTS)
91 }
92
93 fn statement_renderer(&self) -> Option<&StatementTextRenderer> {
94 Some(&STATEMENTS)
95 }
96
97 fn information(&self) -> Option<&Information> {
98 Some(&INFORMATION)
99 }
100}
101
102datasources::export_driver!(datasources_sqlite_driver, SqliteDriver);
103
104#[cfg(test)]
105mod tests {
106 use super::SqliteDriver;
107 use datasources::Driver;
108
109 #[test]
110 fn maps_column_types() {
111 let driver = SqliteDriver;
112 assert_eq!(driver.column_type("int").unwrap(), "INTEGER");
113 assert_eq!(driver.column_type("uuid").unwrap(), "TEXT");
114 assert_eq!(driver.column_type("string").unwrap(), "TEXT");
115 assert_eq!(driver.column_type("bool").unwrap(), "BOOLEAN");
116 assert_eq!(driver.column_type("decimal").unwrap(), "NUMERIC");
117 assert_eq!(driver.column_type("float").unwrap(), "REAL");
118 assert_eq!(driver.column_type("datetime").unwrap(), "TEXT");
119 assert_eq!(driver.column_type("binary").unwrap(), "BLOB");
120 assert!(driver
121 .column_type("xml")
122 .unwrap_err()
123 .to_string()
124 .contains("Unknown sqlite type \"xml\""));
125 }
126
127 #[test]
128 fn maps_property_targets() {
129 use datasources::converters::RowTypeConverter;
130 use datasources::Value;
131 use std::collections::HashMap;
132
133 let driver = SqliteDriver;
134 let columns = HashMap::from([
135 ("Key".into(), "uuid".into()),
136 ("Active".into(), "bool".into()),
137 ("Login".into(), "string".into()),
138 ("Created".into(), "datetime".into()),
139 ]);
140 let converter = RowTypeConverter::from(|| driver.row_type_converters(), &columns);
141 let row = HashMap::from([
142 (
143 "Key".into(),
144 Value::from("11111111-1111-1111-1111-111111111111"),
145 ),
146 ("Active".into(), Value::Int(1)),
147 ("Login".into(), Value::from("ada")),
148 ("Created".into(), Value::from("2020-01-02T03:04:05.000Z")),
149 ]);
150 let converted = converter.convert(&row).unwrap();
151 assert!(matches!(converted.get("Key"), Some(Value::Uuid(_))));
152 assert_eq!(converted.get("Active"), Some(&Value::Bool(true)));
153 assert_eq!(converted.get("Login"), Some(&Value::from("ada")));
154 assert!(matches!(converted.get("Created"), Some(Value::DateTime(_))));
155 let stored = converter.reverse_convert(&converted).unwrap();
156 assert_eq!(
157 stored.get("Key"),
158 Some(&Value::from("11111111-1111-1111-1111-111111111111"))
159 );
160 assert_eq!(stored.get("Active"), Some(&Value::Int(1)));
161 assert_eq!(stored.get("Login"), Some(&Value::from("ada")));
162 assert_eq!(
163 stored.get("Created"),
164 Some(&Value::from("2020-01-02T03:04:05.000Z"))
165 );
166 }
167}