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 #[allow(unsafe_code, reason = "FFI calls")]
309 pub(super) fn consume_results(&mut self) -> QueryResult<()> {
310 if self.get_status() != pq_sys::ConnStatusType::CONNECTION_OK {
311 return Err(crate::result::Error::DeserializationError(
312 "Database connection lost".into(),
313 ));
314 }
315 if let Some(result) = self.get_next_result()? {
320 match result.result_status() {
321 pq_sys::ExecStatusType::PGRES_COPY_OUT => {
322 let mut ptr: *mut libc::c_char = core::ptr::null_mut();
323 let mut ret = Ok(());
324 loop {
325 if self.get_status() != pq_sys::ConnStatusType::CONNECTION_OK {
326 return Err(crate::result::Error::DeserializationError(
327 "Database connection lost".into(),
328 ));
329 }
330 let len = unsafe {
331 pq_sys::PQgetCopyData(self.internal_connection.as_ptr(), &mut ptr, 0)
332 };
333 match len {
334 len if len >= 0 => {
335 if !ptr.is_null() {
336 unsafe {
337 pq_sys::PQfreemem(ptr as *mut libc::c_void);
338 }
339 ptr = core::ptr::null_mut();
340 }
341 }
342 -1 => {
343 break;
344 }
345 _ => {
346 if ret.is_ok() {
347 let error = self.last_error_message();
348 ret = Err(crate::result::Error::DeserializationError(
349 error.into(),
350 ));
351 }
352 }
353 }
354 }
355 ret?;
356 self.consume_results()
357 }
358 pq_sys::ExecStatusType::PGRES_COPY_IN => {
359 self.finish_copy_from(Some(String::from("Unexpected error")))?;
360 self.consume_results()
361 }
362 _ => {
363 let mut ret = Ok(());
364 loop {
365 if self.get_status() != pq_sys::ConnStatusType::CONNECTION_OK {
366 return Err(crate::result::Error::DeserializationError(
367 "Database connection lost".into(),
368 ));
369 }
370 match self.get_next_result() {
371 Ok(None) => break,
372 Err(e) if ret.is_ok() => ret = Err(e),
373 Ok(_) | Err(_) => {}
374 }
375 }
376 ret
377 }
378 }
379 } else {
380 Ok(())
381 }
382 }
383}
384
385#[derive(#[automatically_derived]impl ::core::fmt::Debug for PgTransactionStatus {
#[inline]
#[rustc_trivial_field_reads]
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]
#[rustc_trivial_field_reads]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for PgTransactionStatus { }Copy)]
387pub(super) enum PgTransactionStatus {
388 Idle,
390 Active,
392 InTransaction,
394 InError,
396 Unknown,
398}
399
400impl From<PGTransactionStatusType> for PgTransactionStatus {
401 fn from(trans_status_type: PGTransactionStatusType) -> Self {
402 match trans_status_type {
403 PGTransactionStatusType::PQTRANS_IDLE => PgTransactionStatus::Idle,
404 PGTransactionStatusType::PQTRANS_ACTIVE => PgTransactionStatus::Active,
405 PGTransactionStatusType::PQTRANS_INTRANS => PgTransactionStatus::InTransaction,
406 PGTransactionStatusType::PQTRANS_INERROR => PgTransactionStatus::InError,
407 PGTransactionStatusType::PQTRANS_UNKNOWN => PgTransactionStatus::Unknown,
408 }
409 }
410}
411
412pub(super) type NoticeProcessor =
413 extern "C" fn(arg: *mut libc::c_void, message: *const libc::c_char);
414
415impl Drop for RawConnection {
416 fn drop(&mut self) {
417 unsafe { PQfinish(self.internal_connection.as_ptr()) };
418 }
419}
420
421fn last_error_message(conn: *const PGconn) -> String {
422 unsafe {
423 let error_ptr = PQerrorMessage(conn);
424 let bytes = CStr::from_ptr(error_ptr).to_bytes();
425 String::from_utf8_lossy(bytes).to_string()
426 }
427}
428
429#[allow(missing_debug_implementations)]
435pub(super) struct RawResult(NonNull<PGresult>);
436
437unsafe impl Send for RawResult {}
448unsafe impl Sync for RawResult {}
449
450impl RawResult {
452 #[allow(clippy::new_ret_no_self)]
453 fn new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self> {
454 NonNull::new(ptr).map(RawResult).ok_or_else(|| {
455 Error::DatabaseError(
456 DatabaseErrorKind::UnableToSendCommand,
457 Box::new(conn.last_error_message()),
458 )
459 })
460 }
461
462 pub(super) fn error_message(&self) -> &str {
463 let ptr = unsafe { PQresultErrorMessage(self.0.as_ptr()) };
464 let cstr = unsafe { CStr::from_ptr(ptr) };
465 cstr.to_str().unwrap_or_default()
466 }
467
468 pub(super) fn get_result_field(&self, field: ResultField) -> Option<&str> {
469 let ptr = unsafe { PQresultErrorField(self.0.as_ptr(), field as libc::c_int) };
470 if ptr.is_null() {
471 return None;
472 }
473
474 let c_str = unsafe { CStr::from_ptr(ptr) };
475 c_str.to_str().ok()
476 }
477
478 pub(super) fn result_status(&self) -> pq_sys::ExecStatusType {
479 unsafe {
480 PQresultStatus(self.0.as_ptr())
483 }
484 }
485
486 pub(super) fn column_count(&self) -> libc::c_int {
487 unsafe {
488 PQnfields(self.0.as_ptr())
491 }
492 }
493
494 pub(super) fn row_count(&self) -> libc::c_int {
495 unsafe {
496 PQntuples(self.0.as_ptr())
499 }
500 }
501
502 pub(super) fn rows_affected(&self) -> &CStr {
503 unsafe {
504 let count_char_ptr = PQcmdTuples(self.0.as_ptr());
507 CStr::from_ptr(count_char_ptr)
508 }
509 }
510
511 pub(super) fn get_bytes(&self, row_idx: i32, col_idx: i32) -> &[u8] {
512 unsafe {
513 let value_ptr = PQgetvalue(self.0.as_ptr(), row_idx, col_idx) as *const u8;
516 let num_bytes = PQgetlength(self.0.as_ptr(), row_idx, col_idx);
517 if value_ptr.is_null() {
518 &[]
519 } else {
520 core::slice::from_raw_parts(
523 value_ptr,
524 num_bytes
525 .try_into()
526 .expect("Diesel expects at least a 32 bit operating system"),
527 )
528 }
529 }
530 }
531
532 pub(super) fn is_null(&self, row_idx: libc::c_int, col_idx: libc::c_int) -> libc::c_int {
533 unsafe {
534 PQgetisnull(self.0.as_ptr(), row_idx, col_idx)
537 }
538 }
539
540 pub(super) fn column_type(&self, col_idx: libc::c_int) -> pq_sys::Oid {
541 unsafe {
542 PQftype(self.0.as_ptr(), col_idx)
545 }
546 }
547
548 pub(super) fn column_name(&self, col_idx: libc::c_int) -> Option<&CStr> {
549 unsafe {
550 let ptr = PQfname(self.0.as_ptr(), col_idx);
554 if ptr.is_null() {
555 None
556 } else {
557 Some(CStr::from_ptr(ptr))
558 }
559 }
560 }
561}
562
563impl Drop for RawResult {
564 fn drop(&mut self) {
565 unsafe { PQclear(self.0.as_ptr()) }
566 }
567}
568
569#[repr(i32)]
574pub(super) enum ResultField {
575 SqlState = 'C' as i32,
576 MessagePrimary = 'M' as i32,
577 MessageDetail = 'D' as i32,
578 MessageHint = 'H' as i32,
579 TableName = 't' as i32,
580 ColumnName = 'c' as i32,
581 ConstraintName = 'n' as i32,
582 StatementPosition = 'P' as i32,
583}