Skip to content

Commit 09de03b

Browse files
committed
Remove the lifetime from the PgQueryBuilder
As we work to make `QueryFragment` generic over the backend, I would prefer to not have a lifetime on the `Pg` backend. As such, that means that `PgQueryBuilder` needs to have no associated lifetime. I don't want to require that you put the actual connection in an `Rc`, but I do need to pass something that isn't a reference to the query builder. As such, I've isolated all methods that work with PQ directly into another structure, which we keep inside of the `Connection`. This does introduce a potential subtlety, which is that we are `Send` even though we have a non-atomic reference counter. The reason this is safe is because we *create* the `Rc` internally, and the only place we hand it to anybody else is for a `query_builder`, which is only created in a place that does not outlive a reference to the `Connection`. That means that in order to send the `Connection`, you'd have to be in a context where there are no other references to the `RawConnection`. However, this is no longer enforced by the type system.
1 parent 0e74092 commit 09de03b

4 files changed

Lines changed: 161 additions & 100 deletions

File tree

diesel/src/connection/mod.rs

Lines changed: 20 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
extern crate pq_sys;
21
extern crate libc;
32

43
mod cursor;
4+
#[doc(hidden)]
5+
pub mod raw;
56

67
pub use self::cursor::Cursor;
78

9+
use std::cell::Cell;
10+
use std::ffi::{CString, CStr};
11+
use std::rc::Rc;
12+
use std::ptr;
13+
814
use db_result::DbResult;
915
use expression::{AsExpression, Expression, NonAggregate};
1016
use expression::expression_methods::*;
@@ -16,14 +22,11 @@ use query_builder::pg::PgQueryBuilder;
1622
use query_dsl::{FilterDsl, LimitDsl};
1723
use query_source::{Table, Queryable};
1824
use result::*;
19-
use self::pq_sys::*;
20-
use std::cell::Cell;
21-
use std::ffi::{CString, CStr};
22-
use std::{str, ptr};
25+
use self::raw::RawConnection;
2326
use types::{NativeSqlType, ToSql};
2427

2528
pub struct Connection {
26-
internal_connection: *mut PGconn,
29+
raw_connection: Rc<RawConnection>,
2730
transaction_depth: Cell<i32>,
2831
}
2932

@@ -41,21 +44,12 @@ impl Connection {
4144
/// should be a PostgreSQL connection string, as documented at
4245
/// http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING
4346
pub fn establish(database_url: &str) -> ConnectionResult<Connection> {
44-
let connection_string = try!(CString::new(database_url));
45-
let connection_ptr = unsafe { PQconnectdb(connection_string.as_ptr()) };
46-
let connection_status = unsafe { PQstatus(connection_ptr) };
47-
match connection_status {
48-
CONNECTION_OK => {
49-
Ok(Connection {
50-
internal_connection: connection_ptr,
51-
transaction_depth: Cell::new(0),
52-
})
53-
},
54-
_ => {
55-
let message = last_error_message(connection_ptr);
56-
Err(ConnectionError::BadConnection(message))
47+
RawConnection::establish(database_url).map(|raw_conn| {
48+
Connection {
49+
raw_connection: Rc::new(raw_conn),
50+
transaction_depth: Cell::new(0),
5751
}
58-
}
52+
})
5953
}
6054

6155
/// Executes the given function inside of a database transaction. When
@@ -112,7 +106,7 @@ impl Connection {
112106
pub fn batch_execute(&self, query: &str) -> QueryResult<()> {
113107
let query = try!(CString::new(query));
114108
let inner_result = unsafe {
115-
PQexec(self.internal_connection, query.as_ptr())
109+
self.raw_connection.exec(query.as_ptr())
116110
};
117111
try!(DbResult::new(self, inner_result));
118112
Ok(())
@@ -152,8 +146,7 @@ impl Connection {
152146
let param_formats = vec![1; param_data.len()];
153147

154148
let internal_res = unsafe {
155-
PQexecParams(
156-
self.internal_connection,
149+
self.raw_connection.exec_params(
157150
query.as_ptr(),
158151
params_pointer.len() as libc::c_int,
159152
param_types_ptr,
@@ -216,7 +209,7 @@ impl Connection {
216209
fn prepare_query<T: QueryFragment>(&self, source: &T)
217210
-> (String, Vec<Option<Vec<u8>>>, Vec<u32>)
218211
{
219-
let mut query_builder = PgQueryBuilder::new(self);
212+
let mut query_builder = PgQueryBuilder::new(&self.raw_connection);
220213
source.to_sql(&mut query_builder).unwrap();
221214
(query_builder.sql, query_builder.binds, query_builder.bind_types)
222215
}
@@ -227,7 +220,7 @@ impl Connection {
227220

228221
#[doc(hidden)]
229222
pub fn last_error_message(&self) -> String {
230-
last_error_message(self.internal_connection)
223+
self.raw_connection.last_error_message()
231224
}
232225

233226
fn begin_transaction(&self) -> QueryResult<usize> {
@@ -266,28 +259,11 @@ impl Connection {
266259
query
267260
}
268261

269-
#[doc(hidden)]
270-
pub fn escape_identifier(&self, identifier: &str) -> QueryResult<PgString> {
271-
let result_ptr = unsafe { PQescapeIdentifier(
272-
self.internal_connection,
273-
identifier.as_ptr() as *const libc::c_char,
274-
identifier.len() as libc::size_t,
275-
) };
276-
277-
if result_ptr.is_null() {
278-
Err(Error::DatabaseError(last_error_message(self.internal_connection)))
279-
} else {
280-
unsafe {
281-
Ok(PgString::new(result_ptr))
282-
}
283-
}
284-
}
285-
286262
#[doc(hidden)]
287263
pub fn silence_notices<F: FnOnce() -> T, T>(&self, f: F) -> T {
288-
unsafe { PQsetNoticeProcessor(self.internal_connection, Some(noop_notice_processor), ptr::null_mut()) };
264+
self.raw_connection.set_notice_processor(noop_notice_processor);
289265
let result = f();
290-
unsafe { PQsetNoticeProcessor(self.internal_connection, Some(default_notice_processor), ptr::null_mut()) };
266+
self.raw_connection.set_notice_processor(default_notice_processor);
291267
result
292268
}
293269
}
@@ -300,49 +276,3 @@ extern "C" fn default_notice_processor(_: *mut libc::c_void, message: *const lib
300276
let c_str = unsafe { CStr::from_ptr(message) };
301277
::std::io::stderr().write(c_str.to_bytes()).unwrap();
302278
}
303-
304-
fn last_error_message(conn: *const PGconn) -> String {
305-
unsafe {
306-
let error_ptr = PQerrorMessage(conn);
307-
let bytes = CStr::from_ptr(error_ptr).to_bytes();
308-
str::from_utf8_unchecked(bytes).to_string()
309-
}
310-
}
311-
312-
impl Drop for Connection {
313-
fn drop(&mut self) {
314-
unsafe { PQfinish(self.internal_connection) };
315-
}
316-
}
317-
318-
#[doc(hidden)]
319-
pub struct PgString {
320-
pg_str: *mut libc::c_char,
321-
}
322-
323-
impl PgString {
324-
unsafe fn new(ptr: *mut libc::c_char) -> Self {
325-
PgString {
326-
pg_str: ptr,
327-
}
328-
}
329-
}
330-
331-
impl ::std::ops::Deref for PgString {
332-
type Target = str;
333-
334-
fn deref(&self) -> &str {
335-
unsafe {
336-
let c_string = CStr::from_ptr(self.pg_str);
337-
str::from_utf8_unchecked(c_string.to_bytes())
338-
}
339-
}
340-
}
341-
342-
impl Drop for PgString {
343-
fn drop(&mut self) {
344-
unsafe {
345-
PQfreemem(self.pg_str as *mut libc::c_void)
346-
}
347-
}
348-
}

diesel/src/connection/raw.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
extern crate pq_sys;
2+
extern crate libc;
3+
4+
use self::pq_sys::*;
5+
use std::ffi::{CString, CStr};
6+
use std::{str, ptr};
7+
8+
use result::*;
9+
10+
pub struct RawConnection {
11+
internal_connection: *mut PGconn,
12+
}
13+
14+
impl RawConnection {
15+
pub fn establish(database_url: &str) -> ConnectionResult<Self> {
16+
let connection_string = try!(CString::new(database_url));
17+
let connection_ptr = unsafe { PQconnectdb(connection_string.as_ptr()) };
18+
let connection_status = unsafe { PQstatus(connection_ptr) };
19+
20+
match connection_status {
21+
CONNECTION_OK => {
22+
Ok(RawConnection {
23+
internal_connection: connection_ptr,
24+
})
25+
}
26+
_ => {
27+
let message = last_error_message(connection_ptr);
28+
Err(ConnectionError::BadConnection(message))
29+
}
30+
}
31+
}
32+
33+
pub fn last_error_message(&self) -> String {
34+
last_error_message(self.internal_connection)
35+
}
36+
37+
pub fn escape_identifier(&self, identifier: &str) -> QueryResult<PgString> {
38+
let result_ptr = unsafe { PQescapeIdentifier(
39+
self.internal_connection,
40+
identifier.as_ptr() as *const libc::c_char,
41+
identifier.len() as libc::size_t,
42+
) };
43+
44+
if result_ptr.is_null() {
45+
Err(Error::DatabaseError(last_error_message(self.internal_connection)))
46+
} else {
47+
unsafe {
48+
Ok(PgString::new(result_ptr))
49+
}
50+
}
51+
}
52+
53+
pub fn set_notice_processor(&self, notice_processor: NoticeProcessor) {
54+
unsafe {
55+
PQsetNoticeProcessor(self.internal_connection, Some(notice_processor), ptr::null_mut());
56+
}
57+
}
58+
59+
pub unsafe fn exec(&self, query: *const libc::c_char) -> *mut PGresult {
60+
PQexec(self.internal_connection, query)
61+
}
62+
63+
pub unsafe fn exec_params(
64+
&self,
65+
query: *const libc::c_char,
66+
param_count: libc::c_int,
67+
param_types: *const Oid,
68+
param_values: *const *const libc::c_char,
69+
param_lengths: *const libc::c_int,
70+
param_formats: *const libc::c_int,
71+
result_format: libc::c_int,
72+
) -> *mut PGresult {
73+
PQexecParams(
74+
self.internal_connection,
75+
query,
76+
param_count,
77+
param_types,
78+
param_values,
79+
param_lengths,
80+
param_formats,
81+
result_format,
82+
)
83+
}
84+
}
85+
86+
pub type NoticeProcessor = extern "C" fn(arg: *mut libc::c_void, message: *const libc::c_char);
87+
88+
impl Drop for RawConnection {
89+
fn drop(&mut self) {
90+
unsafe { PQfinish(self.internal_connection) };
91+
}
92+
}
93+
94+
fn last_error_message(conn: *const PGconn) -> String {
95+
unsafe {
96+
let error_ptr = PQerrorMessage(conn);
97+
let bytes = CStr::from_ptr(error_ptr).to_bytes();
98+
str::from_utf8_unchecked(bytes).to_string()
99+
}
100+
}
101+
102+
pub struct PgString {
103+
pg_str: *mut libc::c_char,
104+
}
105+
106+
impl PgString {
107+
unsafe fn new(ptr: *mut libc::c_char) -> Self {
108+
PgString {
109+
pg_str: ptr,
110+
}
111+
}
112+
}
113+
114+
impl ::std::ops::Deref for PgString {
115+
type Target = str;
116+
117+
fn deref(&self) -> &str {
118+
unsafe {
119+
let c_string = CStr::from_ptr(self.pg_str);
120+
str::from_utf8_unchecked(c_string.to_bytes())
121+
}
122+
}
123+
}
124+
125+
impl Drop for PgString {
126+
fn drop(&mut self) {
127+
unsafe {
128+
PQfreemem(self.pg_str as *mut libc::c_void)
129+
}
130+
}
131+
}

diesel/src/query_builder/pg.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
use connection::Connection;
1+
use std::rc::Rc;
2+
3+
use connection::raw::RawConnection;
24
use super::{QueryBuilder, Binds, BuildQueryResult, Context};
35
use types::NativeSqlType;
46

5-
pub struct PgQueryBuilder<'a> {
6-
conn: &'a Connection,
7+
pub struct PgQueryBuilder {
8+
conn: Rc<RawConnection>,
79
pub sql: String,
810
pub binds: Binds,
911
pub bind_types: Vec<u32>,
1012
bind_idx: u32,
1113
context_stack: Vec<Context>,
1214
}
1315

14-
impl<'a> PgQueryBuilder<'a> {
15-
pub fn new(conn: &'a Connection) -> Self {
16+
impl PgQueryBuilder {
17+
pub fn new(conn: &Rc<RawConnection>) -> Self {
1618
PgQueryBuilder {
17-
conn: conn,
19+
conn: conn.clone(),
1820
sql: String::new(),
1921
binds: Vec::new(),
2022
bind_types: Vec::new(),
@@ -24,7 +26,7 @@ impl<'a> PgQueryBuilder<'a> {
2426
}
2527
}
2628

27-
impl<'a> QueryBuilder for PgQueryBuilder<'a> {
29+
impl QueryBuilder for PgQueryBuilder {
2830
fn push_sql(&mut self, sql: &str) {
2931
self.sql.push_str(sql);
3032
}

diesel_tests/tests/expressions/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ fn test_count_star() {
2727
assert_eq!(Ok(1), source.first(&connection));
2828

2929
// Ensure we're doing COUNT(*) instead of COUNT(table.*) which is going to be more efficient
30-
let mut query_builder = ::diesel::query_builder::pg::PgQueryBuilder::new(&connection);
31-
QueryFragment::to_sql(&source.as_query(), &mut query_builder).unwrap();
32-
assert!(query_builder.sql.starts_with("SELECT COUNT(*) FROM"));
30+
assert!(debug_sql!(source).starts_with("SELECT COUNT(*) FROM"));
3331
}
3432

3533
use diesel::types::VarChar;

0 commit comments

Comments
 (0)