1#![forbid(unsafe_code)]
40#![deny(clippy::all)]
41
42pub mod arena;
43pub mod codec;
44pub mod oid_map;
45pub mod pool;
46pub(crate) mod types;
47
48mod auth;
49mod conn;
50mod proto;
51mod stmt_cache;
52mod sync_io;
53#[cfg(feature = "tls")]
54mod tls_common;
55#[cfg(feature = "tls")]
56mod tls_sync;
57
58#[cfg(feature = "async")]
59pub mod async_conn;
60#[cfg(feature = "async")]
61mod async_io;
62
63pub use arena::Arena;
64#[cfg(feature = "async")]
65pub use async_conn::AsyncConnection;
66pub use codec::Encode;
67pub use conn::release_col_offsets;
68pub use conn::release_resp_buf;
69pub use conn::Connection;
70pub use pool::{Pool, PoolBuilder, PoolGuard, PoolStatus, Transaction};
71pub use types::{
72 hash_sql, ColumnDesc, Config, Notification, PgDataRow, PrepareResult, QueryResult, Row,
73 SimpleRow, SslMode, StatementCacheMode,
74};
75
76#[derive(Debug)]
102pub enum DriverError {
103 Io(std::io::Error),
105 Auth(String),
107 Protocol(String),
109 Server {
111 code: [u8; 5],
125 message: Box<str>,
127 detail: Option<Box<str>>,
129 hint: Option<Box<str>>,
131 position: Option<u32>,
133 },
134 Pool(String),
136}
137
138impl std::fmt::Display for DriverError {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match self {
141 Self::Io(e) => write!(f, "I/O error: {e}"),
142 Self::Auth(msg) => write!(f, "auth error: {msg}"),
143 Self::Protocol(msg) => write!(f, "protocol error: {msg}"),
144 Self::Server {
145 code,
146 message,
147 detail,
148 hint,
149 position,
150 } => {
151 write!(
152 f,
153 "server error [{}]: {message}",
154 std::str::from_utf8(code).unwrap_or("?????")
155 )?;
156 if let Some(pos) = position {
157 write!(f, " (at position {pos})")?;
158 }
159 if let Some(d) = detail {
160 write!(f, " DETAIL: {d}")?;
161 }
162 if let Some(h) = hint {
163 write!(f, " HINT: {h}")?;
164 }
165 Ok(())
166 }
167 Self::Pool(msg) => write!(f, "pool error: {msg}"),
168 }
169 }
170}
171
172impl std::error::Error for DriverError {
173 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
174 match self {
175 Self::Io(e) => Some(e),
176 _ => None,
177 }
178 }
179}
180
181impl From<std::io::Error> for DriverError {
182 fn from(e: std::io::Error) -> Self {
183 Self::Io(e)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn driver_error_display_io() {
193 let e = DriverError::Io(std::io::Error::new(
194 std::io::ErrorKind::ConnectionRefused,
195 "refused",
196 ));
197 assert!(e.to_string().contains("I/O error"));
198 assert!(e.to_string().contains("refused"));
199 }
200
201 #[test]
202 fn driver_error_display_auth() {
203 let e = DriverError::Auth("wrong password".into());
204 assert_eq!(e.to_string(), "auth error: wrong password");
205 }
206
207 #[test]
208 fn driver_error_display_protocol() {
209 let e = DriverError::Protocol("unexpected message".into());
210 assert_eq!(e.to_string(), "protocol error: unexpected message");
211 }
212
213 #[test]
214 fn driver_error_display_server() {
215 let e = DriverError::Server {
216 code: *b"42P01",
217 message: "relation does not exist".into(),
218 detail: Some("table was dropped".into()),
219 hint: None,
220 position: None,
221 };
222 let s = e.to_string();
223 assert!(s.contains("42P01"));
224 assert!(s.contains("relation does not exist"));
225 assert!(s.contains("table was dropped"));
226 }
227
228 #[test]
229 fn driver_error_display_server_no_detail() {
230 let e = DriverError::Server {
231 code: *b"23505",
232 message: Box::from("duplicate key"),
233 detail: None,
234 hint: None,
235 position: None,
236 };
237 assert_eq!(e.to_string(), "server error [23505]: duplicate key");
238 }
239
240 #[test]
241 fn driver_error_display_server_with_position() {
242 let e = DriverError::Server {
243 code: *b"42601",
244 message: Box::from("syntax error"),
245 detail: None,
246 hint: None,
247 position: Some(8),
248 };
249 let s = e.to_string();
250 assert!(s.contains("(at position 8)"));
251 }
252
253 #[test]
254 fn driver_error_display_pool() {
255 let e = DriverError::Pool("exhausted".into());
256 assert_eq!(e.to_string(), "pool error: exhausted");
257 }
258
259 #[test]
260 fn driver_error_source_io() {
261 let inner = std::io::Error::other("test");
262 let e = DriverError::Io(inner);
263 assert!(std::error::Error::source(&e).is_some());
264 }
265
266 #[test]
267 fn driver_error_source_non_io() {
268 let e = DriverError::Auth("test".into());
269 assert!(std::error::Error::source(&e).is_none());
270 }
271
272 #[test]
273 fn driver_error_from_io() {
274 let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
275 let e: DriverError = io_err.into();
276 assert!(matches!(e, DriverError::Io(_)));
277 }
278
279 #[test]
280 fn forbid_unsafe_code() {
281 }
284
285 #[test]
290 fn driver_error_display_server_all_none() {
291 let e = DriverError::Server {
292 code: *b"00000",
293 message: "successful completion".into(),
294 detail: None,
295 hint: None,
296 position: None,
297 };
298 let s = e.to_string();
299 assert_eq!(s, "server error [00000]: successful completion");
300 assert!(!s.contains("DETAIL"));
302 assert!(!s.contains("HINT"));
303 assert!(!s.contains("position"));
304 }
305
306 #[test]
307 fn driver_error_display_server_detail_only() {
308 let e = DriverError::Server {
309 code: *b"23505",
310 message: "duplicate key".into(),
311 detail: Some("Key (id)=(1) exists.".into()),
312 hint: None,
313 position: None,
314 };
315 let s = e.to_string();
316 assert!(s.contains("DETAIL: Key (id)=(1) exists."));
317 assert!(!s.contains("HINT"));
318 }
319
320 #[test]
321 fn driver_error_display_server_hint_only() {
322 let e = DriverError::Server {
323 code: *b"42601",
324 message: "syntax error".into(),
325 detail: None,
326 hint: Some("check SQL".into()),
327 position: None,
328 };
329 let s = e.to_string();
330 assert!(s.contains("HINT: check SQL"));
331 assert!(!s.contains("DETAIL"));
332 }
333
334 #[test]
335 fn driver_error_display_server_position_only() {
336 let e = DriverError::Server {
337 code: *b"42601",
338 message: "syntax error".into(),
339 detail: None,
340 hint: None,
341 position: Some(15),
342 };
343 let s = e.to_string();
344 assert!(s.contains("(at position 15)"));
345 }
346
347 #[test]
348 fn driver_error_display_server_all_fields() {
349 let e = DriverError::Server {
350 code: *b"42P01",
351 message: "relation does not exist".into(),
352 detail: Some("table was dropped".into()),
353 hint: Some("recreate the table".into()),
354 position: Some(42),
355 };
356 let s = e.to_string();
357 assert!(s.contains("[42P01]"));
358 assert!(s.contains("relation does not exist"));
359 assert!(s.contains("(at position 42)"));
360 assert!(s.contains("DETAIL: table was dropped"));
361 assert!(s.contains("HINT: recreate the table"));
362 }
363
364 #[test]
365 fn driver_error_io_preserves_kind() {
366 let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
367 let e = DriverError::Io(io_err);
368 match &e {
369 DriverError::Io(inner) => {
370 assert_eq!(inner.kind(), std::io::ErrorKind::ConnectionRefused);
371 }
372 _ => panic!("expected Io variant"),
373 }
374 }
375
376 #[test]
377 fn driver_error_io_timeout() {
378 let io_err = std::io::Error::new(std::io::ErrorKind::TimedOut, "connection timed out");
379 let e = DriverError::Io(io_err);
380 let s = e.to_string();
381 assert!(s.contains("timed out"));
382 }
383
384 #[test]
385 fn driver_error_io_unexpected_eof() {
386 let io_err = std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "connection closed");
387 let e: DriverError = io_err.into();
388 let s = e.to_string();
389 assert!(s.contains("connection closed"));
390 }
391
392 #[test]
393 fn driver_error_auth_empty() {
394 let e = DriverError::Auth(String::new());
395 assert_eq!(e.to_string(), "auth error: ");
396 }
397
398 #[test]
399 fn driver_error_protocol_empty() {
400 let e = DriverError::Protocol(String::new());
401 assert_eq!(e.to_string(), "protocol error: ");
402 }
403
404 #[test]
405 fn driver_error_pool_empty() {
406 let e = DriverError::Pool(String::new());
407 assert_eq!(e.to_string(), "pool error: ");
408 }
409
410 #[test]
411 fn driver_error_source_protocol_is_none() {
412 let e = DriverError::Protocol("test".into());
413 assert!(std::error::Error::source(&e).is_none());
414 }
415
416 #[test]
417 fn driver_error_source_server_is_none() {
418 let e = DriverError::Server {
419 code: *b"42601",
420 message: "err".into(),
421 detail: None,
422 hint: None,
423 position: None,
424 };
425 assert!(std::error::Error::source(&e).is_none());
426 }
427
428 #[test]
429 fn driver_error_source_pool_is_none() {
430 let e = DriverError::Pool("test".into());
431 assert!(std::error::Error::source(&e).is_none());
432 }
433
434 #[test]
435 fn driver_error_debug_all_variants() {
436 let variants: Vec<DriverError> = vec![
437 DriverError::Io(std::io::Error::other("io")),
438 DriverError::Auth("auth".into()),
439 DriverError::Protocol("proto".into()),
440 DriverError::Server {
441 code: *b"00000",
442 message: "ok".into(),
443 detail: None,
444 hint: None,
445 position: None,
446 },
447 DriverError::Pool("pool".into()),
448 ];
449 for v in &variants {
450 let dbg = format!("{v:?}");
451 assert!(!dbg.is_empty());
452 }
453 }
454}