Skip to main content

diesel/pg/connection/
raw.rs

1#![allow(clippy::too_many_arguments)]
2#![allow(unsafe_code)] // ffi code
3
4extern 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                    // Note that even if the server connection attempt fails (as indicated by PQstatus),
41                    // the application should call PQfinish to free the memory used by the PGconn object.
42                    // https://www.postgresql.org/docs/current/libpq-connect.html
43                    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    /// Sends a query and parameters to the server without using the prepare/bind cycle.
71    ///
72    /// This method uses PQsendQueryParams which combines the prepare and bind steps
73    /// and is more compatible with connection poolers like PgBouncer.
74    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    /// This is reasonably inexpensive as it just accesses variables internal to the connection
156    /// that are kept up to date by the `ReadyForQuery` messages from the PG server
157    pub(super) fn transaction_status(&self) -> PgTransactionStatus {
158        unsafe { PQtransactionStatus(self.internal_connection.as_ptr()) }.into()
159    }
160
161    /// The server version in `PQserverVersion` encoding, zero once the connection is bad.
162    pub(super) fn server_version(&self) -> libc::c_int {
163        // SAFETY: `internal_connection` is the live `PGconn` owned by `self`, and
164        // `PQserverVersion` only reads the version cached during connection startup.
165        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            // we use a drop guard here to
252            // make sure that we always free
253            // the provided pointer, even if we
254            // somehow return an error below
255            struct Guard<'a> {
256                value: &'a mut pgNotify,
257            }
258
259            impl Drop for Guard<'_> {
260                fn drop(&mut self) {
261                    unsafe {
262                        // SAFETY: We know that this value is not null here
263                        PQfreemem(self.value as *mut pgNotify as *mut core::ffi::c_void)
264                    };
265                }
266            }
267
268            let pgnotify = unsafe {
269                // SAFETY: We checked for null values above
270                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                // SAFETY: We checked for null values above
287                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                // SAFETY: We checked for null values above
294                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/// Represents the current in-transaction status of the connection
310#[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    /// Currently idle
313    Idle,
314    /// A command is in progress (sent to the server but not yet completed)
315    Active,
316    /// Idle, in a valid transaction block
317    InTransaction,
318    /// Idle, in a failed transaction block
319    InError,
320    /// Bad connection
321    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/// Internal wrapper around a `*mut PGresult` which is known to be not-null, and
354/// have no aliases.  This wrapper is to ensure that it's always properly
355/// dropped.
356///
357/// If `Unique` is ever stabilized, we should use it here.
358#[allow(missing_debug_implementations)]
359pub(super) struct RawResult(NonNull<PGresult>);
360
361// SAFETY:
362// https://www.postgresql.org/docs/current/libpq-threading.html
363//
364// PGresult objects are normally read-only after creation,
365// and so can be passed around freely between threads. However,
366// if you use any of the PGresult-modifying functions described in
367// Section 31.12 or Section 31.14, it's up to you to avoid concurrent operations on the same PGresult, too.
368//
369// The type doesn't expose the raw pointer
370// and we don't call such Pgresult-modifying below
371unsafe impl Send for RawResult {}
372unsafe impl Sync for RawResult {}
373
374// Make sure to only call non-modifying functions here
375impl 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            // SAFETY:
405            // We have a unique not null pointer here
406            PQresultStatus(self.0.as_ptr())
407        }
408    }
409
410    pub(super) fn column_count(&self) -> libc::c_int {
411        unsafe {
412            // SAFETY:
413            // We have a unique not null pointer here
414            PQnfields(self.0.as_ptr())
415        }
416    }
417
418    pub(super) fn row_count(&self) -> libc::c_int {
419        unsafe {
420            // SAFETY:
421            // We have a unique not null pointer here
422            PQntuples(self.0.as_ptr())
423        }
424    }
425
426    pub(super) fn rows_affected(&self) -> &CStr {
427        unsafe {
428            // SAFETY:
429            // We have a unique not null pointer here
430            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            // SAFETY:
438            // We have a unique not null pointer here
439            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                // SAFETY:
445                // * we rely on correct information from libpq here, if the provided length and pointer are invalid
446                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            // SAFETY:
459            // We have a unique not null pointer here
460            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            // SAFETY:
467            // We have a unique not null pointer here
468            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            // https://www.postgresql.org/docs/13/libpq-exec.html#LIBPQ-PQFNAME
475            // states that the returned ptr is valid till the underlying result is freed
476            // That means we can couple the lifetime to self
477            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/// Represents valid options to
494/// [`PQresultErrorField`](https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQRESULTERRORFIELD)
495/// Their values are defined as C preprocessor macros, and therefore are not exported by libpq-sys.
496/// Their values can be found in `postgres_ext.h`
497#[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}