Repository: diesel-rs/diesel
File: diesel/src/pg/connection/raw.rs (Rust)

Code before:

    pub(crate) fn finish_copy_from(&self, err: Option<String>) -> QueryResult<()> {
        let error = err.map(CString::new).map(|r| {
            r.unwrap_or_else(|_| {
                CString::new("Error message contains a \\0 byte")
                    .expect("Does not contain a null byte")
            })
        });
        let error = error
            .as_ref()
            .map(|l| l.as_ptr())
            .unwrap_or(std::ptr::null());
        let ret = unsafe { pq_sys::PQputCopyEnd(self.internal_connection.as_ptr(), error) };
        if ret == 1 {
            Ok(())
        } else {
            Err(Error::DatabaseError(
                DatabaseErrorKind::Unknown,
                Box::new(self.last_error_message()),
            ))
        }
    }

    pub(super) fn pqnotifies(&self) -> Option<PGNotification> {
        let conn = self.internal_connection;
        let _ = unsafe { PQconsumeInput(conn.as_ptr()) };
        let pgnotify = unsafe { PQnotifies(conn.as_ptr()) };
        if pgnotify.is_null() {
            None
        } else {
            let ret = Some(PGNotification {
                process_id: unsafe { (*pgnotify).be_pid },
                channel: unsafe { CStr::from_ptr((*pgnotify).relname) }
                    .to_str()
                    .expect("Channel name should be UTF-8")
                    .to_string(),
                payload: unsafe { CStr::from_ptr((*pgnotify).extra) }
                    .to_str()
                    .expect("Could not parse payload to UTF-8")
                    .to_string(),
            });
            unsafe { PQfreemem(pgnotify as *mut std::ffi::c_void) };
            ret
        }
    }
}

// Using the same field names as tokio-postgres
/// See Postgres documentation for SQL Commands NOTIFY and LISTEN
#[derive(Clone, Debug)]
pub struct PGNotification {

Review comment (suggestion):
we should check the returned integer here to check if an error occurred. That likely means that the notification iterator returns a `QueryResult<PgNotification>` instead of just a `PgNotification>`.

It should make it also easier to handle the possible UTF-8 errors below.

Code after:
    }

    pub(crate) fn finish_copy_from(&self, err: Option<String>) -> QueryResult<()> {
        let error = err.map(CString::new).map(|r| {
            r.unwrap_or_else(|_| {
                CString::new("Error message contains a \\0 byte")
                    .expect("Does not contain a null byte")
            })
        });
        let error = error
            .as_ref()
            .map(|l| l.as_ptr())
            .unwrap_or(std::ptr::null());
        let ret = unsafe { pq_sys::PQputCopyEnd(self.internal_connection.as_ptr(), error) };
        if ret == 1 {
            Ok(())
        } else {
            Err(Error::DatabaseError(
                DatabaseErrorKind::Unknown,
                Box::new(self.last_error_message()),
            ))
        }
    }

    pub(super) fn pq_notifies(&self) -> Result<Option<PgNotification>, Error> {
        let conn = self.internal_connection;
        let ret = unsafe { PQconsumeInput(conn.as_ptr()) };
        if ret == 0 {
            return Err(Error::DatabaseError(
                DatabaseErrorKind::Unknown,
                Box::new(self.last_error_message()),
            ));
        }

        let pgnotify = unsafe { PQnotifies(conn.as_ptr()) };
        if pgnotify.is_null() {
            Ok(None)
        } else {
            // we use a drop guard here to
            // make sure that we always free
            // the provided pointer, even if we
            // somehow return an error below
            struct Guard<'a> {
                value: &'a mut pgNotify,
            }

            impl Drop for Guard<'_> {
                fn drop(&mut self) {
                    unsafe {
                        // SAFETY: We know that this value is not null here
                        PQfreemem(self.value as *mut pgNotify as *mut std::ffi::c_void)