rsfbclient_diesel/fb/
connection.rs1use super::backend::Fb;
4use super::query_builder::FbQueryBuilder;
5use super::transaction::FbTransactionManager;
6use super::value::FbRow;
7use diesel::connection::*;
8use diesel::query_builder::bind_collector::RawBytesBindCollector;
9use diesel::query_builder::*;
10use diesel::result::Error::DatabaseError;
11use diesel::result::*;
12use rsfbclient::{Execute, SqlType};
13use rsfbclient::{Queryable, Row, SimpleConnection as FbRawConnection};
14
15pub struct FbConnection {
16 pub raw: FbRawConnection,
17 tr_manager: FbTransactionManager,
18}
19
20impl SimpleConnection for FbConnection {
21 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
22 self.raw
23 .execute(query, ())
24 .map_err(|e| DatabaseError(DatabaseErrorKind::Unknown, Box::new(e.to_string())))
25 .map(|_| ())
26 }
27}
28
29impl<'conn, 'query> ConnectionGatWorkaround<'conn, 'query, Fb, DefaultLoadingMode>
30 for FbConnection
31{
32 type Cursor = Box<dyn Iterator<Item = QueryResult<Self::Row>>>;
33 type Row = FbRow;
34}
35
36impl Connection for FbConnection {
37 type TransactionManager = FbTransactionManager;
38 type Backend = Fb;
39
40 fn establish(database_url: &str) -> ConnectionResult<Self> {
41 #[cfg(all(
42 feature = "pure_rust",
43 not(any(feature = "linking", feature = "dynamic_loading"))
44 ))]
45 let mut raw_builder = rsfbclient::builder_pure_rust();
46 #[cfg(any(feature = "linking", feature = "dynamic_loading"))]
47 let raw_builder = rsfbclient::builder_native();
48
49 let raw = raw_builder
50 .from_string(database_url)
51 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?
52 .connect()
53 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
54
55 FbConnection::init(raw.into())
56 }
57
58 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
59 where
60 T: QueryFragment<Self::Backend> + QueryId,
61 {
62 let mut bc = RawBytesBindCollector::<Fb>::new();
63 source.collect_binds(&mut bc, &mut (), &Fb)?;
64
65 let mut qb = FbQueryBuilder::new();
66 source.to_sql(&mut qb, &Fb)?;
67 let sql = qb.finish();
68
69 let params: Vec<SqlType> = bc
70 .metadata
71 .into_iter()
72 .zip(bc.binds)
73 .map(|(tp, val)| tp.into_param(val))
74 .collect();
75
76 self.raw
77 .execute(&sql, params)
78 .map_err(|e| DatabaseError(DatabaseErrorKind::Unknown, Box::new(e.to_string())))
79 }
80
81 fn transaction_state(
82 &mut self,
83 ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData {
84 &mut self.tr_manager
85 }
86}
87
88trait Helper {
89 fn load<'conn, 'query, T>(
90 conn: &'conn mut FbConnection,
91 source: T,
92 ) -> QueryResult<Box<dyn Iterator<Item = QueryResult<FbRow>>>>
93 where
94 T: Query + QueryFragment<Fb> + QueryId + 'query,
95 Fb: diesel::expression::QueryMetadata<T::SqlType>;
96}
97
98impl Helper for ()
99where
100 for<'b> Fb: diesel::backend::HasBindCollector<'b, BindCollector = RawBytesBindCollector<Fb>>,
101{
102 fn load<'conn, 'query, T>(
103 conn: &'conn mut FbConnection,
104 source: T,
105 ) -> QueryResult<Box<dyn Iterator<Item = QueryResult<FbRow>>>>
106 where
107 T: Query + QueryFragment<Fb> + QueryId + 'query,
108 Fb: diesel::expression::QueryMetadata<T::SqlType>,
109 {
110 let source = &source.as_query();
111 let mut bc = RawBytesBindCollector::<Fb>::new();
112 source.collect_binds(&mut bc, &mut (), &Fb)?;
113
114 let mut qb = FbQueryBuilder::new();
115 source.to_sql(&mut qb, &Fb)?;
116 let has_cursor = qb.has_cursor;
117 let sql = qb.finish();
118
119 let params: Vec<SqlType> = bc
120 .metadata
121 .into_iter()
122 .zip(bc.binds)
123 .map(|(tp, val)| tp.into_param(val))
124 .collect();
125
126 let results = if has_cursor {
127 conn.raw.query::<Vec<SqlType>, Row>(&sql, params)
128 } else {
129 match conn
130 .raw
131 .execute_returnable::<Vec<SqlType>, Row>(&sql, params)
132 {
133 Ok(result) => Ok(vec![result]),
134 Err(e) => Err(e),
135 }
136 };
137
138 Ok(Box::new(
139 results
140 .map_err(|e| DatabaseError(DatabaseErrorKind::Unknown, Box::new(e.to_string())))?
141 .into_iter()
142 .map(FbRow::new)
143 .map(Ok),
144 ))
145 }
146}
147
148impl LoadConnection<DefaultLoadingMode> for FbConnection
149where
150 (): Helper,
153{
154 fn load<'conn, 'query, T>(
155 &'conn mut self,
156 source: T,
157 ) -> QueryResult<LoadRowIter<'conn, 'query, Self, Self::Backend, DefaultLoadingMode>>
158 where
159 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
160 Self::Backend: diesel::expression::QueryMetadata<T::SqlType>,
161 {
162 <() as Helper>::load(self, source)
163 }
164}
165
166impl FbConnection {
167 pub fn init(conn: FbRawConnection) -> ConnectionResult<Self> {
169 Ok(FbConnection {
170 raw: conn,
171 tr_manager: FbTransactionManager::new(),
172 })
173 }
174}
175
176#[cfg(not(any(feature = "dynamic_loading", feature = "embedded_tests")))]
177#[cfg(test)]
178mod tests {
179
180 use crate::FbConnection;
181 use diesel::connection::SimpleConnection;
182 use diesel::prelude::*;
183 use diesel::result::Error;
184 use rsfbclient::prelude::*;
185
186 #[test]
187 fn establish() -> Result<(), ConnectionError> {
188 FbConnection::establish("firebird://SYSDBA:masterkey@localhost/test.fdb")?;
189
190 Ok(())
191 }
192
193 #[test]
194 fn execute() -> Result<(), Error> {
195 let mut conn =
196 FbConnection::establish("firebird://SYSDBA:masterkey@localhost/test.fdb").unwrap();
197
198 conn.batch_execute("drop table conn_exec").ok();
199
200 conn.batch_execute("create table conn_exec(id int, name varchar(50))")?;
201
202 let affected_rows =
203 diesel::sql_query("insert into conn_exec(id, name) values (10, 'café')")
204 .execute(&mut conn)?;
205 assert_eq!(1, affected_rows);
206
207 Ok(())
208 }
209
210 #[test]
211 fn establish_from_lib_conn() -> Result<(), String> {
212 #[cfg(all(feature = "pure_rust", not(feature = "linking")))]
213 let mut raw_builder = rsfbclient::builder_pure_rust();
214 #[cfg(feature = "linking")]
215 let raw_builder = rsfbclient::builder_native();
216
217 let conn = raw_builder
218 .from_string("firebird://SYSDBA:masterkey@localhost/test.fdb")
219 .map_err(|e| e.to_string())?
220 .transaction(TransactionConfiguration {
221 lock_resolution: TrLockResolution::NoWait,
222 ..TransactionConfiguration::default()
223 })
224 .connect()
225 .map_err(|e| e.to_string())?
226 .into();
227
228 let _ = FbConnection::init(conn).map_err(|e| e.to_string())?;
229
230 Ok(())
231 }
232}