1use crate::connection::PostgresConnection;
2use crate::converters;
3use crate::sql_generate;
4use crate::statement_renderer::PostgresStatementRenderer;
5use crate::transaction::PostgresTransaction;
6use datasources::{
7 downcast_connection, Command, Connection, DatasourceError, DefaultValueConverter,
8 DeterministicTypeConverter, Driver, Information, IsolationLevel, Mapping, Result,
9 StatementTextRenderer, Transaction, TypeConverter, Value,
10};
11use std::collections::HashMap;
12use std::sync::{Arc, LazyLock};
13
14pub struct PostgresDriver;
15
16impl PostgresDriver {
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: PostgresDriver = PostgresDriver;
22 Driver::statement_renderer(&DRIVER).expect("postgres")
23 }
24
25 pub fn types() -> &'static DeterministicTypeConverter {
26 &*sql_generate::TYPES
27 }
28
29 pub fn defaults() -> &'static DefaultValueConverter {
30 &sql_generate::DEFAULTS
31 }
32}
33
34static STATEMENTS: LazyLock<StatementTextRenderer> =
35 LazyLock::new(|| StatementTextRenderer::new(Some(Box::new(PostgresStatementRenderer)), None));
36
37static INFORMATION: LazyLock<Information> = LazyLock::new(|| {
38 Information::new(
39 [
40 "PostgreSQL server driver.",
41 "Full connection, command, transaction, and repository support.",
42 ],
43 [
44 "Overrides SelectInsertedIdStatement to SELECT setval(pg_get_serial_sequence('{table}', '{column}'), MAX(\"{column}\")) AS {column}",
45 "Overrides CreateTableStatement identity to GENERATED BY DEFAULT AS IDENTITY",
46 "Overrides CreateTableStatement boolean defaults (0/1 → TRUE/FALSE)",
47 "Overrides CreateTableStatement type mapping (int→INTEGER, uuid→UUID, string→TEXT, bool→BOOLEAN, decimal→NUMERIC, float→DOUBLE PRECISION, datetime→TIMESTAMPTZ, binary→BYTEA)",
48 "Overrides CreateCreateStoredProcedureStatement to PROCEDURE Create{Plural}",
49 "Overrides CreateReadStoredProcedureStatement to PROCEDURE Read{Plural}By{Properties}",
50 "Overrides CreateUpdateStoredProcedureStatement to PROCEDURE Update{Plural}By{Properties}",
51 "Overrides CreateDeleteStoredProcedureStatement to PROCEDURE Delete{Plural}By{Properties}",
52 "Overrides CreateBulkCreateStoredProcedureStatement to PROCEDURE BulkCreate{Plural}",
53 "Overrides CreateBulkUpdateStoredProcedureStatement to PROCEDURE BulkUpdate{Plural}",
54 "Overrides CreateBulkDeleteStoredProcedureStatement to PROCEDURE BulkDelete{Plural}",
55 ],
56 [] as [&str; 0],
57 )
58});
59
60pub(crate) fn column_sql(name: &str) -> Option<&'static str> {
61 Some(match name {
62 "int" => "INTEGER",
63 "uuid" => "UUID",
64 "string" => "TEXT",
65 "bool" => "BOOLEAN",
66 "decimal" => "NUMERIC",
67 "float" => "DOUBLE PRECISION",
68 "datetime" => "TIMESTAMPTZ",
69 "binary" => "BYTEA",
70 _ => return None,
71 })
72}
73
74impl Driver for PostgresDriver {
75 fn connection(&self, options: Option<&HashMap<String, String>>) -> Arc<dyn Connection> {
76 PostgresConnection::from_options(options)
77 }
78
79 fn command(&self, connection: Arc<dyn Connection>) -> Arc<dyn Command> {
80 connection.create_command()
81 }
82
83 fn transaction(
84 &self,
85 connection: Arc<dyn Connection>,
86 isolation: IsolationLevel,
87 ) -> Arc<dyn Transaction> {
88 let conn = downcast_connection::<PostgresConnection>(connection).expect("postgres");
89 Arc::new(PostgresTransaction::new(conn.share(), isolation))
90 }
91
92 fn row_type_converters(
93 &self,
94 ) -> HashMap<String, Box<dyn TypeConverter<Value, Value> + Send + Sync>> {
95 converters::row_type_converters()
96 }
97
98 fn column_type(&self, name: &str) -> Result<String> {
99 column_sql(name)
100 .map(str::to_string)
101 .ok_or_else(|| DatasourceError::message(format!("Unknown postgres type \"{name}\"")))
102 }
103
104 fn type_converter(&self) -> Option<&dyn Mapping<String, String>> {
105 Some(&*sql_generate::TYPES)
106 }
107
108 fn default_value_converter(&self) -> Option<&DefaultValueConverter> {
109 Some(&sql_generate::DEFAULTS)
110 }
111
112 fn statement_renderer(&self) -> Option<&StatementTextRenderer> {
113 Some(&STATEMENTS)
114 }
115
116 fn information(&self) -> Option<&Information> {
117 Some(&INFORMATION)
118 }
119}
120
121datasources::export_driver!(datasources_postgres_driver, PostgresDriver);
122
123#[cfg(test)]
124mod tests {
125 use super::PostgresDriver;
126 use datasources::Driver;
127
128 #[test]
129 fn maps_column_types() {
130 let driver = PostgresDriver;
131 assert_eq!(driver.column_type("int").unwrap(), "INTEGER");
132 assert_eq!(driver.column_type("uuid").unwrap(), "UUID");
133 assert_eq!(driver.column_type("string").unwrap(), "TEXT");
134 assert_eq!(driver.column_type("bool").unwrap(), "BOOLEAN");
135 assert_eq!(driver.column_type("decimal").unwrap(), "NUMERIC");
136 assert_eq!(driver.column_type("float").unwrap(), "DOUBLE PRECISION");
137 assert_eq!(driver.column_type("datetime").unwrap(), "TIMESTAMPTZ");
138 assert_eq!(driver.column_type("binary").unwrap(), "BYTEA");
139 assert!(driver
140 .column_type("xml")
141 .unwrap_err()
142 .to_string()
143 .contains("Unknown postgres type \"xml\""));
144 }
145
146 #[test]
147 fn maps_property_targets() {
148 use datasources::converters::RowTypeConverter;
149 use datasources::Value;
150 use std::collections::HashMap;
151
152 let driver = PostgresDriver;
153 let columns = HashMap::from([
154 ("Key".into(), "uuid".into()),
155 ("Active".into(), "bool".into()),
156 ("Login".into(), "string".into()),
157 ("Created".into(), "datetime".into()),
158 ]);
159 let converter = RowTypeConverter::from(|| driver.row_type_converters(), &columns);
160 let row = HashMap::from([
161 (
162 "Key".into(),
163 Value::from("11111111-1111-1111-1111-111111111111"),
164 ),
165 ("Active".into(), Value::Int(1)),
166 ("Login".into(), Value::from("ada")),
167 ("Created".into(), Value::from("2020-01-02T03:04:05.000Z")),
168 ]);
169 let converted = converter.convert(&row).unwrap();
170 assert!(matches!(converted.get("Key"), Some(Value::Uuid(_))));
171 assert_eq!(converted.get("Active"), Some(&Value::Bool(true)));
172 assert_eq!(converted.get("Login"), Some(&Value::from("ada")));
173 assert!(matches!(converted.get("Created"), Some(Value::DateTime(_))));
174 let stored = converter.reverse_convert(&converted).unwrap();
175 assert!(matches!(stored.get("Key"), Some(Value::Uuid(_))));
176 assert_eq!(stored.get("Active"), Some(&Value::Bool(true)));
177 assert_eq!(stored.get("Login"), Some(&Value::from("ada")));
178 assert_eq!(
179 stored.get("Created"),
180 Some(&Value::from("2020-01-02T03:04:05.000Z"))
181 );
182 }
183}