1use std::collections::HashMap;
4use std::ffi::{c_void, CStr};
5use std::sync::{Arc, Mutex};
6
7use libpq_sys::{
8 PGVerbosity, PGresult, PQresultErrorField, PG_DIAG_COLUMN_NAME, PG_DIAG_CONSTRAINT_NAME,
9 PG_DIAG_CONTEXT, PG_DIAG_DATATYPE_NAME, PG_DIAG_INTERNAL_POSITION, PG_DIAG_INTERNAL_QUERY,
10 PG_DIAG_MESSAGE_DETAIL, PG_DIAG_MESSAGE_HINT, PG_DIAG_MESSAGE_PRIMARY, PG_DIAG_SCHEMA_NAME,
11 PG_DIAG_SEVERITY, PG_DIAG_SEVERITY_NONLOCALIZED, PG_DIAG_SOURCE_FILE, PG_DIAG_SOURCE_FUNCTION,
12 PG_DIAG_SOURCE_LINE, PG_DIAG_SQLSTATE, PG_DIAG_STATEMENT_POSITION, PG_DIAG_TABLE_NAME,
13};
14
15#[derive(Debug, Clone, Copy)]
19pub enum Verbosity {
20 Terse,
21 Default,
22 Verbose,
23 Sqlstate,
24}
25
26impl From<Verbosity> for PGVerbosity {
27 fn from(verbosity: Verbosity) -> Self {
28 match verbosity {
29 Verbosity::Terse => PGVerbosity::PQERRORS_TERSE,
30 Verbosity::Default => PGVerbosity::PQERRORS_DEFAULT,
31 Verbosity::Verbose => PGVerbosity::PQERRORS_VERBOSE,
32 Verbosity::Sqlstate => PGVerbosity::PQERRORS_SQLSTATE,
33 }
34 }
35}
36
37#[derive(Debug, Clone)]
42pub struct Notice {
43 pub fields: HashMap<&'static str, String>,
51}
52
53pub type NoticeStorage = Arc<Mutex<Vec<Notice>>>;
55
56pub unsafe extern "C" fn notice_receiver(arg: *mut c_void, result: *const PGresult) {
67 if result.is_null() || arg.is_null() {
68 return;
69 }
70
71 let shared_notices = unsafe { &*(arg as *const Mutex<Vec<Notice>>) };
72
73 let verbosity = match shared_notices.lock() {
75 Ok(notices) => notices
76 .first()
78 .map(|_| Verbosity::Verbose)
79 .unwrap_or(Verbosity::Default),
80 Err(_) => Verbosity::Default,
81 };
82
83 let field_kinds: Vec<(i32, &'static str)> = match verbosity {
84 Verbosity::Terse => vec![
85 (PG_DIAG_SEVERITY as i32, "severity"),
86 (PG_DIAG_MESSAGE_PRIMARY as i32, "message"),
87 (PG_DIAG_SQLSTATE as i32, "sqlstate"),
88 ],
89 Verbosity::Default => vec![
90 (PG_DIAG_SEVERITY as i32, "severity"),
91 (PG_DIAG_SQLSTATE as i32, "sqlstate"),
92 (PG_DIAG_MESSAGE_PRIMARY as i32, "message"),
93 (PG_DIAG_MESSAGE_DETAIL as i32, "detail"),
94 (PG_DIAG_MESSAGE_HINT as i32, "hint"),
95 ],
96 Verbosity::Verbose => vec![
97 (PG_DIAG_SEVERITY as i32, "severity"),
98 (
99 PG_DIAG_SEVERITY_NONLOCALIZED as i32,
100 "severity_nonlocalized",
101 ),
102 (PG_DIAG_SQLSTATE as i32, "sqlstate"),
103 (PG_DIAG_MESSAGE_PRIMARY as i32, "message"),
104 (PG_DIAG_MESSAGE_DETAIL as i32, "detail"),
105 (PG_DIAG_MESSAGE_HINT as i32, "hint"),
106 (PG_DIAG_STATEMENT_POSITION as i32, "statement_position"),
107 (PG_DIAG_INTERNAL_POSITION as i32, "internal_position"),
108 (PG_DIAG_INTERNAL_QUERY as i32, "internal_query"),
109 (PG_DIAG_CONTEXT as i32, "context"),
110 (PG_DIAG_SCHEMA_NAME as i32, "schema_name"),
111 (PG_DIAG_TABLE_NAME as i32, "table_name"),
112 (PG_DIAG_COLUMN_NAME as i32, "column_name"),
113 (PG_DIAG_DATATYPE_NAME as i32, "datatype_name"),
114 (PG_DIAG_CONSTRAINT_NAME as i32, "constraint_name"),
115 (PG_DIAG_SOURCE_FILE as i32, "source_file"),
116 (PG_DIAG_SOURCE_LINE as i32, "source_line"),
117 (PG_DIAG_SOURCE_FUNCTION as i32, "source_function"),
118 ],
119 Verbosity::Sqlstate => vec![
120 (PG_DIAG_SEVERITY as i32, "severity"),
121 (PG_DIAG_SQLSTATE as i32, "sqlstate"),
122 ],
123 };
124
125 let mut notice = Notice {
126 fields: HashMap::new(),
127 };
128
129 for (code, label) in &field_kinds {
130 let val_ptr = unsafe { PQresultErrorField(result, *code) };
131 if !val_ptr.is_null() {
132 let val = unsafe { CStr::from_ptr(val_ptr) }
133 .to_string_lossy()
134 .into_owned();
135 notice.fields.insert(*label, val);
136 }
137 }
138
139 if let Ok(mut vec) = shared_notices.lock() {
140 vec.push(notice);
141 }
142}