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