1 #![allow(clippy::too_many_arguments)]
2 
3 extern crate pq_sys;
4 
5 use self::pq_sys::*;
6 use std::ffi::{CStr, CString};
7 use std::os::raw as libc;
8 use std::ptr::NonNull;
9 use std::{ptr, str};
10 
11 use result::*;
12 
13 #[allow(missing_debug_implementations, missing_copy_implementations)]
14 pub struct RawConnection {
15     internal_connection: NonNull<PGconn>,
16 }
17 
18 impl RawConnection {
establish(database_url: &str) -> ConnectionResult<Self>19     pub fn establish(database_url: &str) -> ConnectionResult<Self> {
20         use self::ConnStatusType::*;
21 
22         let connection_string = CString::new(database_url)?;
23         let connection_ptr = unsafe { PQconnectdb(connection_string.as_ptr()) };
24         let connection_status = unsafe { PQstatus(connection_ptr) };
25 
26         match connection_status {
27             CONNECTION_OK => {
28                 let connection_ptr = unsafe { NonNull::new_unchecked(connection_ptr) };
29                 Ok(RawConnection {
30                     internal_connection: connection_ptr,
31                 })
32             }
33             _ => {
34                 let message = last_error_message(connection_ptr);
35 
36                 if !connection_ptr.is_null() {
37                     // Note that even if the server connection attempt fails (as indicated by PQstatus),
38                     // the application should call PQfinish to free the memory used by the PGconn object.
39                     // https://www.postgresql.org/docs/current/libpq-connect.html
40                     unsafe { PQfinish(connection_ptr) }
41                 }
42 
43                 Err(ConnectionError::BadConnection(message))
44             }
45         }
46     }
47 
last_error_message(&self) -> String48     pub fn last_error_message(&self) -> String {
49         last_error_message(self.internal_connection.as_ptr())
50     }
51 
set_notice_processor(&self, notice_processor: NoticeProcessor)52     pub fn set_notice_processor(&self, notice_processor: NoticeProcessor) {
53         unsafe {
54             PQsetNoticeProcessor(
55                 self.internal_connection.as_ptr(),
56                 Some(notice_processor),
57                 ptr::null_mut(),
58             );
59         }
60     }
61 
exec(&self, query: *const libc::c_char) -> QueryResult<RawResult>62     pub unsafe fn exec(&self, query: *const libc::c_char) -> QueryResult<RawResult> {
63         RawResult::new(PQexec(self.internal_connection.as_ptr(), query), self)
64     }
65 
exec_prepared( &self, stmt_name: *const libc::c_char, param_count: libc::c_int, param_values: *const *const libc::c_char, param_lengths: *const libc::c_int, param_formats: *const libc::c_int, result_format: libc::c_int, ) -> QueryResult<RawResult>66     pub unsafe fn exec_prepared(
67         &self,
68         stmt_name: *const libc::c_char,
69         param_count: libc::c_int,
70         param_values: *const *const libc::c_char,
71         param_lengths: *const libc::c_int,
72         param_formats: *const libc::c_int,
73         result_format: libc::c_int,
74     ) -> QueryResult<RawResult> {
75         let ptr = PQexecPrepared(
76             self.internal_connection.as_ptr(),
77             stmt_name,
78             param_count,
79             param_values,
80             param_lengths,
81             param_formats,
82             result_format,
83         );
84         RawResult::new(ptr, self)
85     }
86 
prepare( &self, stmt_name: *const libc::c_char, query: *const libc::c_char, param_count: libc::c_int, param_types: *const Oid, ) -> QueryResult<RawResult>87     pub unsafe fn prepare(
88         &self,
89         stmt_name: *const libc::c_char,
90         query: *const libc::c_char,
91         param_count: libc::c_int,
92         param_types: *const Oid,
93     ) -> QueryResult<RawResult> {
94         let ptr = PQprepare(
95             self.internal_connection.as_ptr(),
96             stmt_name,
97             query,
98             param_count,
99             param_types,
100         );
101         RawResult::new(ptr, self)
102     }
103 }
104 
105 pub type NoticeProcessor = extern "C" fn(arg: *mut libc::c_void, message: *const libc::c_char);
106 
107 impl Drop for RawConnection {
drop(&mut self)108     fn drop(&mut self) {
109         unsafe { PQfinish(self.internal_connection.as_ptr()) };
110     }
111 }
112 
last_error_message(conn: *const PGconn) -> String113 fn last_error_message(conn: *const PGconn) -> String {
114     unsafe {
115         let error_ptr = PQerrorMessage(conn);
116         let bytes = CStr::from_ptr(error_ptr).to_bytes();
117         String::from_utf8_lossy(bytes).to_string()
118     }
119 }
120 
121 /// Internal wrapper around a `*mut PGresult` which is known to be not-null, and
122 /// have no aliases.  This wrapper is to ensure that it's always properly
123 /// dropped.
124 ///
125 /// If `Unique` is ever stabilized, we should use it here.
126 #[allow(missing_debug_implementations)]
127 pub struct RawResult(NonNull<PGresult>);
128 
129 unsafe impl Send for RawResult {}
130 unsafe impl Sync for RawResult {}
131 
132 impl RawResult {
133     #[allow(clippy::new_ret_no_self)]
new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self>134     fn new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self> {
135         NonNull::new(ptr).map(RawResult).ok_or_else(|| {
136             Error::DatabaseError(
137                 DatabaseErrorKind::UnableToSendCommand,
138                 Box::new(conn.last_error_message()),
139             )
140         })
141     }
142 
as_ptr(&self) -> *mut PGresult143     pub fn as_ptr(&self) -> *mut PGresult {
144         self.0.as_ptr()
145     }
146 
error_message(&self) -> &str147     pub fn error_message(&self) -> &str {
148         let ptr = unsafe { PQresultErrorMessage(self.0.as_ptr()) };
149         let cstr = unsafe { CStr::from_ptr(ptr) };
150         cstr.to_str().unwrap_or_default()
151     }
152 }
153 
154 impl Drop for RawResult {
drop(&mut self)155     fn drop(&mut self) {
156         unsafe { PQclear(self.0.as_ptr()) }
157     }
158 }
159