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