1#![allow(clippy::too_many_arguments)]
2#![allow(unsafe_code)] extern crate pq_sys;
5
6use self::pq_sys::*;
7use alloc::ffi::CString;
8use core::ffi as libc;
9use core::ffi::CStr;
10use core::ptr::NonNull;
11use core::{ptr, str};
12
13use crate::result::*;
14
15use super::result::PgResult;
16use crate::pg::PgNotification;
17
18#[allow(missing_debug_implementations, missing_copy_implementations)]
19pub(super) struct RawConnection {
20 pub(super) internal_connection: NonNull<PGconn>,
21}
22
23impl RawConnection {
24 pub(super) fn establish(database_url: &str) -> ConnectionResult<Self> {
25 let connection_string = CString::new(database_url)?;
26 let connection_ptr = unsafe { PQconnectdb(connection_string.as_ptr()) };
27 let connection_status = unsafe { PQstatus(connection_ptr) };
28
29 match connection_status {
30 ConnStatusType::CONNECTION_OK => {
31 let connection_ptr = unsafe { NonNull::new_unchecked(connection_ptr) };
32 Ok(RawConnection {
33 internal_connection: connection_ptr,
34 })
35 }
36 _ => {
37 let message = last_error_message(connection_ptr);
38
39 if !connection_ptr.is_null() {
40 unsafe { PQfinish(connection_ptr) }
44 }
45
46 Err(ConnectionError::BadConnection(message))
47 }
48 }
49 }
50
51 pub(super) fn last_error_message(&self) -> String {
52 last_error_message(self.internal_connection.as_ptr())
53 }
54
55 pub(super) fn set_notice_processor(&self, notice_processor: NoticeProcessor) {
56 unsafe {
57 PQsetNoticeProcessor(
58 self.internal_connection.as_ptr(),
59 Some(notice_processor),
60 ptr::null_mut(),
61 );
62 }
63 }
64
65 pub(super) unsafe fn exec(&self, query: *const libc::c_char) -> QueryResult<RawResult> {
66 let result_ptr = unsafe { PQexec(self.internal_connection.as_ptr(), query) };
67 RawResult::new(result_ptr, self)
68 }
69
70 pub(super) unsafe fn send_query_params(
75 &self,
76 query: *const libc::c_char,
77 param_count: libc::c_int,
78 param_types: *const Oid,
79 param_values: *const *const libc::c_char,
80 param_lengths: *const libc::c_int,
81 param_formats: *const libc::c_int,
82 result_format: libc::c_int,
83 ) -> QueryResult<()> {
84 let res = unsafe {
85 PQsendQueryParams(
86 self.internal_connection.as_ptr(),
87 query,
88 param_count,
89 param_types,
90 param_values,
91 param_lengths,
92 param_formats,
93 result_format,
94 )
95 };
96 if res == 1 {
97 Ok(())
98 } else {
99 Err(Error::DatabaseError(
100 DatabaseErrorKind::UnableToSendCommand,
101 Box::new(self.last_error_message()),
102 ))
103 }
104 }
105
106 pub(super) unsafe fn send_query_prepared(
107 &self,
108 stmt_name: *const libc::c_char,
109 param_count: libc::c_int,
110 param_values: *const *const libc::c_char,
111 param_lengths: *const libc::c_int,
112 param_formats: *const libc::c_int,
113 result_format: libc::c_int,
114 ) -> QueryResult<()> {
115 let res = unsafe {
116 PQsendQueryPrepared(
117 self.internal_connection.as_ptr(),
118 stmt_name,
119 param_count,
120 param_values,
121 param_lengths,
122 param_formats,
123 result_format,
124 )
125 };
126 if res == 1 {
127 Ok(())
128 } else {
129 Err(Error::DatabaseError(
130 DatabaseErrorKind::UnableToSendCommand,
131 Box::new(self.last_error_message()),
132 ))
133 }
134 }
135
136 pub(super) unsafe fn prepare(
137 &self,
138 stmt_name: *const libc::c_char,
139 query: *const libc::c_char,
140 param_count: libc::c_int,
141 param_types: *const Oid,
142 ) -> QueryResult<RawResult> {
143 let ptr = unsafe {
144 PQprepare(
145 self.internal_connection.as_ptr(),
146 stmt_name,
147 query,
148 param_count,
149 param_types,
150 )
151 };
152 RawResult::new(ptr, self)
153 }
154
155 pub(super) fn transaction_status(&self) -> PgTransactionStatus {
158 unsafe { PQtransactionStatus(self.internal_connection.as_ptr()) }.into()
159 }
160
161 pub(super) fn server_version(&self) -> libc::c_int {
163 unsafe { PQserverVersion(self.internal_connection.as_ptr()) }
166 }
167
168 pub(super) fn get_status(&self) -> ConnStatusType {
169 unsafe { PQstatus(self.internal_connection.as_ptr()) }
170 }
171
172 pub(crate) fn get_next_result(&self) -> Result<Option<PgResult>, Error> {
173 let res = unsafe { PQgetResult(self.internal_connection.as_ptr()) };
174 if res.is_null() {
175 Ok(None)
176 } else {
177 let raw = RawResult::new(res, self)?;
178 Ok(Some(PgResult::new(raw, self)?))
179 }
180 }
181
182 pub(crate) fn enable_row_by_row_mode(&self) -> QueryResult<()> {
183 let res = unsafe { PQsetSingleRowMode(self.internal_connection.as_ptr()) };
184 if res == 1 {
185 Ok(())
186 } else {
187 Err(Error::DatabaseError(
188 DatabaseErrorKind::Unknown,
189 Box::new(self.last_error_message()),
190 ))
191 }
192 }
193
194 pub(super) fn put_copy_data(&mut self, buf: &[u8]) -> QueryResult<()> {
195 for c in buf.chunks(i32::MAX as usize) {
196 let res = unsafe {
197 pq_sys::PQputCopyData(
198 self.internal_connection.as_ptr(),
199 c.as_ptr() as *const libc::c_char,
200 c.len()
201 .try_into()
202 .map_err(|e| Error::SerializationError(Box::new(e)))?,
203 )
204 };
205 if res != 1 {
206 return Err(Error::DatabaseError(
207 DatabaseErrorKind::Unknown,
208 Box::new(self.last_error_message()),
209 ));
210 }
211 }
212 Ok(())
213 }
214
215 pub(crate) fn finish_copy_from(&self, err: Option<String>) -> QueryResult<()> {
216 let error = err.map(CString::new).map(|r| {
217 r.unwrap_or_else(|_| {
218 CString::new("Error message contains a \\0 byte")
219 .expect("Does not contain a null byte")
220 })
221 });
222 let error = error
223 .as_ref()
224 .map(|l| l.as_ptr())
225 .unwrap_or(core::ptr::null());
226 let ret = unsafe { pq_sys::PQputCopyEnd(self.internal_connection.as_ptr(), error) };
227 if ret == 1 {
228 Ok(())
229 } else {
230 Err(Error::DatabaseError(
231 DatabaseErrorKind::Unknown,
232 Box::new(self.last_error_message()),
233 ))
234 }
235 }
236
237 pub(super) fn pq_notifies(&self) -> Result<Option<PgNotification>, Error> {
238 let conn = self.internal_connection;
239 let ret = unsafe { PQconsumeInput(conn.as_ptr()) };
240 if ret == 0 {
241 return Err(Error::DatabaseError(
242 DatabaseErrorKind::Unknown,
243 Box::new(self.last_error_message()),
244 ));
245 }
246
247 let pgnotify = unsafe { PQnotifies(conn.as_ptr()) };
248 if pgnotify.is_null() {
249 Ok(None)
250 } else {
251 struct Guard<'a> {
256 value: &'a mut pgNotify,
257 }
258
259 impl Drop for Guard<'_> {
260 fn drop(&mut self) {
261 unsafe {
262 PQfreemem(self.value as *mut pgNotify as *mut core::ffi::c_void)
264 };
265 }
266 }
267
268 let pgnotify = unsafe {
269 Guard {
271 value: &mut *pgnotify,
272 }
273 };
274 if pgnotify.value.relname.is_null() {
275 return Err(Error::DeserializationError(
276 "Received an unexpected null value for `relname` from the notification".into(),
277 ));
278 }
279 if pgnotify.value.extra.is_null() {
280 return Err(Error::DeserializationError(
281 "Received an unexpected null value for `extra` from the notification".into(),
282 ));
283 }
284
285 let channel = unsafe {
286 CStr::from_ptr(pgnotify.value.relname)
288 }
289 .to_str()
290 .map_err(|e| Error::DeserializationError(e.into()))?
291 .to_string();
292 let payload = unsafe {
293 CStr::from_ptr(pgnotify.value.extra)
295 }
296 .to_str()
297 .map_err(|e| Error::DeserializationError(e.into()))?
298 .to_string();
299 let ret = PgNotification {
300 process_id: pgnotify.value.be_pid,
301 channel,
302 payload,
303 };
304 Ok(Some(ret))
305 }
306 }
307}
308
309#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PgTransactionStatus {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_c_like_enum_write_str(f,
"Idle Active InTransaction InError Unknown",
match self {
PgTransactionStatus::Idle => 0usize,
PgTransactionStatus::Active => 5usize,
PgTransactionStatus::InTransaction => 12usize,
PgTransactionStatus::InError => 26usize,
PgTransactionStatus::Unknown => 34usize,
})
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for PgTransactionStatus { }
#[automatically_derived]
impl ::core::cmp::PartialEq for PgTransactionStatus {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for PgTransactionStatus { }Eq, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PgTransactionStatus { }
#[automatically_derived]
impl ::core::clone::Clone for PgTransactionStatus {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PgTransactionStatus { }Copy)]
311pub(super) enum PgTransactionStatus {
312 Idle,
314 Active,
316 InTransaction,
318 InError,
320 Unknown,
322}
323
324impl From<PGTransactionStatusType> for PgTransactionStatus {
325 fn from(trans_status_type: PGTransactionStatusType) -> Self {
326 match trans_status_type {
327 PGTransactionStatusType::PQTRANS_IDLE => PgTransactionStatus::Idle,
328 PGTransactionStatusType::PQTRANS_ACTIVE => PgTransactionStatus::Active,
329 PGTransactionStatusType::PQTRANS_INTRANS => PgTransactionStatus::InTransaction,
330 PGTransactionStatusType::PQTRANS_INERROR => PgTransactionStatus::InError,
331 PGTransactionStatusType::PQTRANS_UNKNOWN => PgTransactionStatus::Unknown,
332 }
333 }
334}
335
336pub(super) type NoticeProcessor =
337 extern "C" fn(arg: *mut libc::c_void, message: *const libc::c_char);
338
339impl Drop for RawConnection {
340 fn drop(&mut self) {
341 unsafe { PQfinish(self.internal_connection.as_ptr()) };
342 }
343}
344
345fn last_error_message(conn: *const PGconn) -> String {
346 unsafe {
347 let error_ptr = PQerrorMessage(conn);
348 let bytes = CStr::from_ptr(error_ptr).to_bytes();
349 String::from_utf8_lossy(bytes).to_string()
350 }
351}
352
353#[allow(missing_debug_implementations)]
359pub(super) struct RawResult(NonNull<PGresult>);
360
361unsafe impl Send for RawResult {}
372unsafe impl Sync for RawResult {}
373
374impl RawResult {
376 #[allow(clippy::new_ret_no_self)]
377 fn new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self> {
378 NonNull::new(ptr).map(RawResult).ok_or_else(|| {
379 Error::DatabaseError(
380 DatabaseErrorKind::UnableToSendCommand,
381 Box::new(conn.last_error_message()),
382 )
383 })
384 }
385
386 pub(super) fn error_message(&self) -> &str {
387 let ptr = unsafe { PQresultErrorMessage(self.0.as_ptr()) };
388 let cstr = unsafe { CStr::from_ptr(ptr) };
389 cstr.to_str().unwrap_or_default()
390 }
391
392 pub(super) fn get_result_field(&self, field: ResultField) -> Option<&str> {
393 let ptr = unsafe { PQresultErrorField(self.0.as_ptr(), field as libc::c_int) };
394 if ptr.is_null() {
395 return None;
396 }
397
398 let c_str = unsafe { CStr::from_ptr(ptr) };
399 c_str.to_str().ok()
400 }
401
402 pub(super) fn result_status(&self) -> pq_sys::ExecStatusType {
403 unsafe {
404 PQresultStatus(self.0.as_ptr())
407 }
408 }
409
410 pub(super) fn column_count(&self) -> libc::c_int {
411 unsafe {
412 PQnfields(self.0.as_ptr())
415 }
416 }
417
418 pub(super) fn row_count(&self) -> libc::c_int {
419 unsafe {
420 PQntuples(self.0.as_ptr())
423 }
424 }
425
426 pub(super) fn rows_affected(&self) -> &CStr {
427 unsafe {
428 let count_char_ptr = PQcmdTuples(self.0.as_ptr());
431 CStr::from_ptr(count_char_ptr)
432 }
433 }
434
435 pub(super) fn get_bytes(&self, row_idx: i32, col_idx: i32) -> &[u8] {
436 unsafe {
437 let value_ptr = PQgetvalue(self.0.as_ptr(), row_idx, col_idx) as *const u8;
440 let num_bytes = PQgetlength(self.0.as_ptr(), row_idx, col_idx);
441 if value_ptr.is_null() {
442 &[]
443 } else {
444 core::slice::from_raw_parts(
447 value_ptr,
448 num_bytes
449 .try_into()
450 .expect("Diesel expects at least a 32 bit operating system"),
451 )
452 }
453 }
454 }
455
456 pub(super) fn is_null(&self, row_idx: libc::c_int, col_idx: libc::c_int) -> libc::c_int {
457 unsafe {
458 PQgetisnull(self.0.as_ptr(), row_idx, col_idx)
461 }
462 }
463
464 pub(super) fn column_type(&self, col_idx: libc::c_int) -> pq_sys::Oid {
465 unsafe {
466 PQftype(self.0.as_ptr(), col_idx)
469 }
470 }
471
472 pub(super) fn column_name(&self, col_idx: libc::c_int) -> Option<&CStr> {
473 unsafe {
474 let ptr = PQfname(self.0.as_ptr(), col_idx);
478 if ptr.is_null() {
479 None
480 } else {
481 Some(CStr::from_ptr(ptr))
482 }
483 }
484 }
485}
486
487impl Drop for RawResult {
488 fn drop(&mut self) {
489 unsafe { PQclear(self.0.as_ptr()) }
490 }
491}
492
493#[repr(i32)]
498pub(super) enum ResultField {
499 SqlState = 'C' as i32,
500 MessagePrimary = 'M' as i32,
501 MessageDetail = 'D' as i32,
502 MessageHint = 'H' as i32,
503 TableName = 't' as i32,
504 ColumnName = 'c' as i32,
505 ConstraintName = 'n' as i32,
506 StatementPosition = 'P' as i32,
507}