Skip to content

Commit 705cdbe

Browse files
committed
Replace most raw pointer usage with NonNull
This newly stabilized feature indicates that a pointer cannot be null. This allows certain compiler optimizations, including but not limited to the "null pointer optimization", which means that `Option<NonNull<T>>` has the same size as `NonNull<T>`. Using `NonNull` over raw pointers as much as possible in our code potentially enables additional compiler optimizations, and better expresses intent in several places. Since this type was only stabilized in Rust 1.25, I have stuck the use of the real thing behind the unstable flag, and provided a polyfill by default. This keeps with our policy of "only bump the minimum Rust version if you have a good reason". I don't think this is a good enough reason to bump on its own. The polyfill lets us structure our code the same regardless, and it should be trivial to switch to always using the real thing once we no longer support Rust 1.24 and earlier.
1 parent 7d4aefa commit 705cdbe

8 files changed

Lines changed: 152 additions & 89 deletions

File tree

diesel/src/mysql/connection/raw.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ use std::sync::{Once, ONCE_INIT};
88
use result::{ConnectionError, ConnectionResult, QueryResult};
99
use super::url::ConnectionOptions;
1010
use super::stmt::Statement;
11+
use util::NonNull;
1112

12-
pub struct RawConnection(*mut ffi::MYSQL);
13+
pub struct RawConnection(NonNull<ffi::MYSQL>);
1314

1415
impl RawConnection {
1516
pub fn new() -> Self {
1617
perform_thread_unsafe_library_initialization();
1718
let raw_connection = unsafe { ffi::mysql_init(ptr::null_mut()) };
18-
if raw_connection.is_null() {
19-
// We're trusting https://dev.mysql.com/doc/refman/5.7/en/mysql-init.html
20-
// that null return always means OOM
21-
panic!("Insufficient memory to allocate connection");
22-
}
19+
// We're trusting https://dev.mysql.com/doc/refman/5.7/en/mysql-init.html
20+
// that null return always means OOM
21+
let raw_connection =
22+
NonNull::new(raw_connection).expect("Insufficient memory to allocate connection");
2323
let result = RawConnection(raw_connection);
2424

2525
// This is only non-zero for unrecognized options, which should never happen.
2626
let charset_result = unsafe {
2727
ffi::mysql_options(
28-
result.0,
28+
result.0.as_ptr(),
2929
ffi::mysql_option::MYSQL_SET_CHARSET_NAME,
3030
b"utf8mb4\0".as_ptr() as *const libc::c_void,
3131
)
@@ -50,7 +50,7 @@ impl RawConnection {
5050
unsafe {
5151
// Make sure you don't use the fake one!
5252
ffi::mysql_real_connect(
53-
self.0,
53+
self.0.as_ptr(),
5454
host.map(CStr::as_ptr).unwrap_or_else(|| ptr::null_mut()),
5555
user.as_ptr(),
5656
password
@@ -74,7 +74,7 @@ impl RawConnection {
7474
}
7575

7676
pub fn last_error_message(&self) -> String {
77-
unsafe { CStr::from_ptr(ffi::mysql_error(self.0)) }
77+
unsafe { CStr::from_ptr(ffi::mysql_error(self.0.as_ptr())) }
7878
.to_string_lossy()
7979
.into_owned()
8080
}
@@ -83,7 +83,7 @@ impl RawConnection {
8383
unsafe {
8484
// Make sure you don't use the fake one!
8585
ffi::mysql_real_query(
86-
self.0,
86+
self.0.as_ptr(),
8787
query.as_ptr() as *const libc::c_char,
8888
query.len() as libc::c_ulong,
8989
);
@@ -99,7 +99,7 @@ impl RawConnection {
9999
{
100100
unsafe {
101101
ffi::mysql_set_server_option(
102-
self.0,
102+
self.0.as_ptr(),
103103
ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_ON,
104104
);
105105
}
@@ -109,7 +109,7 @@ impl RawConnection {
109109

110110
unsafe {
111111
ffi::mysql_set_server_option(
112-
self.0,
112+
self.0.as_ptr(),
113113
ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_OFF,
114114
);
115115
}
@@ -119,16 +119,16 @@ impl RawConnection {
119119
}
120120

121121
pub fn affected_rows(&self) -> usize {
122-
let affected_rows = unsafe { ffi::mysql_affected_rows(self.0) };
122+
let affected_rows = unsafe { ffi::mysql_affected_rows(self.0.as_ptr()) };
123123
affected_rows as usize
124124
}
125125

126126
pub fn prepare(&self, query: &str) -> QueryResult<Statement> {
127-
let stmt = unsafe { ffi::mysql_stmt_init(self.0) };
127+
let stmt = unsafe { ffi::mysql_stmt_init(self.0.as_ptr()) };
128128
// It is documented that the only reason `mysql_stmt_init` will fail
129129
// is because of OOM.
130130
// https://dev.mysql.com/doc/refman/5.7/en/mysql-stmt-init.html
131-
assert!(!stmt.is_null(), "Out of memory creating prepared statement");
131+
let stmt = NonNull::new(stmt).expect("Out of memory creating prepared statement");
132132
let stmt = Statement::new(stmt);
133133
try!(stmt.prepare(query));
134134
Ok(stmt)
@@ -163,7 +163,7 @@ impl RawConnection {
163163

164164
fn consume_current_result(&self) -> QueryResult<()> {
165165
unsafe {
166-
let res = ffi::mysql_store_result(self.0);
166+
let res = ffi::mysql_store_result(self.0.as_ptr());
167167
if !res.is_null() {
168168
ffi::mysql_free_result(res);
169169
}
@@ -174,7 +174,7 @@ impl RawConnection {
174174
/// Calls `mysql_next_result` and returns whether there are more results
175175
/// after this one.
176176
fn next_result(&self) -> QueryResult<bool> {
177-
let more_results = unsafe { ffi::mysql_next_result(self.0) == 0 };
177+
let more_results = unsafe { ffi::mysql_next_result(self.0.as_ptr()) == 0 };
178178
self.did_an_error_occur()?;
179179
Ok(more_results)
180180
}
@@ -183,7 +183,7 @@ impl RawConnection {
183183
impl Drop for RawConnection {
184184
fn drop(&mut self) {
185185
unsafe {
186-
ffi::mysql_close(self.0);
186+
ffi::mysql_close(self.0.as_ptr());
187187
}
188188
}
189189
}

diesel/src/mysql/connection/stmt/iterator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ fn execute_statement(stmt: &mut Statement, binds: &mut Binds) -> QueryResult<()>
131131
}
132132

133133
fn populate_row_buffers(stmt: &Statement, binds: &mut Binds) -> QueryResult<Option<()>> {
134-
let next_row_result = unsafe { ffi::mysql_stmt_fetch(stmt.stmt) };
134+
let next_row_result = unsafe { ffi::mysql_stmt_fetch(stmt.stmt.as_ptr()) };
135135
match next_row_result as libc::c_uint {
136136
ffi::MYSQL_NO_DATA => Ok(None),
137137
ffi::MYSQL_DATA_TRUNCATED => binds.populate_dynamic_buffers(stmt).map(Some),

diesel/src/mysql/connection/stmt/mod.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ use result::{DatabaseErrorKind, QueryResult};
1111
use self::iterator::*;
1212
use self::metadata::*;
1313
use super::bind::Binds;
14+
use util::NonNull;
1415

1516
pub struct Statement {
16-
stmt: *mut ffi::MYSQL_STMT,
17+
stmt: NonNull<ffi::MYSQL_STMT>,
1718
input_binds: Option<Binds>,
1819
}
1920

2021
impl Statement {
21-
pub fn new(stmt: *mut ffi::MYSQL_STMT) -> Self {
22+
pub(crate) fn new(stmt: NonNull<ffi::MYSQL_STMT>) -> Self {
2223
Statement {
2324
stmt: stmt,
2425
input_binds: None,
@@ -28,7 +29,7 @@ impl Statement {
2829
pub fn prepare(&self, query: &str) -> QueryResult<()> {
2930
unsafe {
3031
ffi::mysql_stmt_prepare(
31-
self.stmt,
32+
self.stmt.as_ptr(),
3233
query.as_ptr() as *const libc::c_char,
3334
query.len() as libc::c_ulong,
3435
);
@@ -45,7 +46,7 @@ impl Statement {
4546
// This relies on the invariant that the current value of `self.input_binds`
4647
// will not change without this function being called
4748
unsafe {
48-
ffi::mysql_stmt_bind_param(self.stmt, bind_ptr);
49+
ffi::mysql_stmt_bind_param(self.stmt.as_ptr(), bind_ptr);
4950
}
5051
});
5152
self.input_binds = Some(input_binds);
@@ -56,15 +57,15 @@ impl Statement {
5657
/// have no return value. It should never be called on a statement on
5758
/// which `results` has previously been called?
5859
pub unsafe fn execute(&self) -> QueryResult<()> {
59-
ffi::mysql_stmt_execute(self.stmt);
60+
ffi::mysql_stmt_execute(self.stmt.as_ptr());
6061
self.did_an_error_occur()?;
61-
ffi::mysql_stmt_store_result(self.stmt);
62+
ffi::mysql_stmt_store_result(self.stmt.as_ptr());
6263
self.did_an_error_occur()?;
6364
Ok(())
6465
}
6566

6667
pub fn affected_rows(&self) -> usize {
67-
let affected_rows = unsafe { ffi::mysql_stmt_affected_rows(self.stmt) };
68+
let affected_rows = unsafe { ffi::mysql_stmt_affected_rows(self.stmt.as_ptr()) };
6869
affected_rows as usize
6970
}
7071

@@ -83,15 +84,15 @@ impl Statement {
8384
}
8485

8586
fn last_error_message(&self) -> String {
86-
unsafe { CStr::from_ptr(ffi::mysql_stmt_error(self.stmt)) }
87+
unsafe { CStr::from_ptr(ffi::mysql_stmt_error(self.stmt.as_ptr())) }
8788
.to_string_lossy()
8889
.into_owned()
8990
}
9091

9192
/// If the pointers referenced by the `MYSQL_BIND` structures are invalidated,
9293
/// you must call this function again before calling `mysql_stmt_fetch`.
9394
pub unsafe fn bind_result(&self, binds: *mut ffi::MYSQL_BIND) -> QueryResult<()> {
94-
ffi::mysql_stmt_bind_result(self.stmt, binds);
95+
ffi::mysql_stmt_bind_result(self.stmt.as_ptr(), binds);
9596
self.did_an_error_occur()
9697
}
9798

@@ -102,7 +103,7 @@ impl Statement {
102103
offset: usize,
103104
) -> QueryResult<()> {
104105
ffi::mysql_stmt_fetch_column(
105-
self.stmt,
106+
self.stmt.as_ptr(),
106107
bind,
107108
idx as libc::c_uint,
108109
offset as libc::c_ulong,
@@ -113,7 +114,7 @@ impl Statement {
113114
fn metadata(&self) -> QueryResult<StatementMetadata> {
114115
use result::Error::DeserializationError;
115116

116-
let result_ptr = unsafe { ffi::mysql_stmt_result_metadata(self.stmt).as_mut() };
117+
let result_ptr = unsafe { ffi::mysql_stmt_result_metadata(self.stmt.as_ptr()).as_mut() };
117118
self.did_an_error_occur()?;
118119
result_ptr
119120
.map(StatementMetadata::new)
@@ -135,7 +136,7 @@ impl Statement {
135136
}
136137

137138
fn last_error_type(&self) -> DatabaseErrorKind {
138-
let last_error_number = unsafe { ffi::mysql_stmt_errno(self.stmt) };
139+
let last_error_number = unsafe { ffi::mysql_stmt_errno(self.stmt.as_ptr()) };
139140
// These values are not exposed by the C API, but are documented
140141
// at https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
141142
// and are from the ANSI SQLSTATE standard
@@ -149,6 +150,6 @@ impl Statement {
149150

150151
impl Drop for Statement {
151152
fn drop(&mut self) {
152-
unsafe { ffi::mysql_stmt_close(self.stmt) };
153+
unsafe { ffi::mysql_stmt_close(self.stmt.as_ptr()) };
153154
}
154155
}

diesel/src/pg/connection/raw.rs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use std::os::raw as libc;
88
use std::{ptr, str};
99

1010
use result::*;
11+
use util::NonNull;
1112

1213
#[allow(missing_debug_implementations, missing_copy_implementations)]
1314
pub struct RawConnection {
14-
internal_connection: *mut PGconn,
15+
internal_connection: NonNull<PGconn>,
1516
}
1617

1718
impl RawConnection {
@@ -23,9 +24,12 @@ impl RawConnection {
2324
let connection_status = unsafe { PQstatus(connection_ptr) };
2425

2526
match connection_status {
26-
CONNECTION_OK => Ok(RawConnection {
27-
internal_connection: connection_ptr,
28-
}),
27+
CONNECTION_OK => {
28+
let connection_ptr = unsafe { NonNull::new_unchecked(connection_ptr) };
29+
Ok(RawConnection {
30+
internal_connection: connection_ptr,
31+
})
32+
}
2933
_ => {
3034
let message = last_error_message(connection_ptr);
3135
Err(ConnectionError::BadConnection(message))
@@ -34,21 +38,21 @@ impl RawConnection {
3438
}
3539

3640
pub fn last_error_message(&self) -> String {
37-
last_error_message(self.internal_connection)
41+
last_error_message(self.internal_connection.as_ptr())
3842
}
3943

4044
pub fn set_notice_processor(&self, notice_processor: NoticeProcessor) {
4145
unsafe {
4246
PQsetNoticeProcessor(
43-
self.internal_connection,
47+
self.internal_connection.as_ptr(),
4448
Some(notice_processor),
4549
ptr::null_mut(),
4650
);
4751
}
4852
}
4953

5054
pub unsafe fn exec(&self, query: *const libc::c_char) -> QueryResult<RawResult> {
51-
RawResult::new(PQexec(self.internal_connection, query), self)
55+
RawResult::new(PQexec(self.internal_connection.as_ptr(), query), self)
5256
}
5357

5458
pub unsafe fn exec_prepared(
@@ -61,7 +65,7 @@ impl RawConnection {
6165
result_format: libc::c_int,
6266
) -> QueryResult<RawResult> {
6367
let ptr = PQexecPrepared(
64-
self.internal_connection,
68+
self.internal_connection.as_ptr(),
6569
stmt_name,
6670
param_count,
6771
param_values,
@@ -80,7 +84,7 @@ impl RawConnection {
8084
param_types: *const Oid,
8185
) -> QueryResult<RawResult> {
8286
let ptr = PQprepare(
83-
self.internal_connection,
87+
self.internal_connection.as_ptr(),
8488
stmt_name,
8589
query,
8690
param_count,
@@ -94,7 +98,7 @@ pub type NoticeProcessor = extern "C" fn(arg: *mut libc::c_void, message: *const
9498

9599
impl Drop for RawConnection {
96100
fn drop(&mut self) {
97-
unsafe { PQfinish(self.internal_connection) };
101+
unsafe { PQfinish(self.internal_connection.as_ptr()) };
98102
}
99103
}
100104

@@ -112,36 +116,34 @@ fn last_error_message(conn: *const PGconn) -> String {
112116
///
113117
/// If `Unique` is ever stabilized, we should use it here.
114118
#[allow(missing_debug_implementations)]
115-
pub struct RawResult(*mut PGresult);
119+
pub struct RawResult(NonNull<PGresult>);
116120

117121
unsafe impl Send for RawResult {}
118122
unsafe impl Sync for RawResult {}
119123

120124
impl RawResult {
121125
fn new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self> {
122-
if ptr.is_null() {
123-
Err(Error::DatabaseError(
126+
NonNull::new(ptr).map(RawResult).ok_or_else(|| {
127+
Error::DatabaseError(
124128
DatabaseErrorKind::UnableToSendCommand,
125129
Box::new(conn.last_error_message()),
126-
))
127-
} else {
128-
Ok(RawResult(ptr))
129-
}
130+
)
131+
})
130132
}
131133

132134
pub fn as_ptr(&self) -> *mut PGresult {
133-
self.0
135+
self.0.as_ptr()
134136
}
135137

136138
pub fn error_message(&self) -> &str {
137-
let ptr = unsafe { PQresultErrorMessage(self.0) };
139+
let ptr = unsafe { PQresultErrorMessage(self.0.as_ptr()) };
138140
let cstr = unsafe { CStr::from_ptr(ptr) };
139141
cstr.to_str().unwrap_or_default()
140142
}
141143
}
142144

143145
impl Drop for RawResult {
144146
fn drop(&mut self) {
145-
unsafe { PQclear(self.0) }
147+
unsafe { PQclear(self.0.as_ptr()) }
146148
}
147149
}

0 commit comments

Comments
 (0)