Skip to content

Commit 6e37db3

Browse files
committed
Properly handle object cleanup errors
Destructors now fail and there's a finish method returning an error.
1 parent dec566e commit 6e37db3

2 files changed

Lines changed: 273 additions & 33 deletions

File tree

src/lib.rs

Lines changed: 164 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ macro_rules! check_desync(
189189
)
190190
)
191191

192+
macro_rules! fail_unless_failing(
193+
($($t:tt)*) => (
194+
if !task::failing() {
195+
fail!($($t)*)
196+
}
197+
)
198+
)
199+
192200
static DEFAULT_PORT: Port = 5432;
193201

194202
/// Trait for types that can handle Postgres notice messages
@@ -363,11 +371,18 @@ struct InnerPostgresConnection {
363371
cancel_data: PostgresCancelData,
364372
unknown_types: HashMap<Oid, ~str>,
365373
desynchronized: bool,
374+
finished: bool,
366375
}
367376

368377
impl Drop for InnerPostgresConnection {
369378
fn drop(&mut self) {
370-
let _ = self.write_messages([Terminate]);
379+
if !self.finished {
380+
match self.finish_inner() {
381+
Ok(()) => {}
382+
Err(err) =>
383+
fail_unless_failing!("Error dropping connection: {}", err)
384+
}
385+
}
371386
}
372387
}
373388

@@ -409,6 +424,7 @@ impl InnerPostgresConnection {
409424
cancel_data: PostgresCancelData { process_id: 0, secret_key: 0 },
410425
unknown_types: HashMap::new(),
411426
desynchronized: false,
427+
finished: false,
412428
};
413429

414430
args.push((~"client_encoding", ~"UTF8"));
@@ -598,10 +614,15 @@ impl InnerPostgresConnection {
598614
name: stmt_name,
599615
param_types: param_types,
600616
result_desc: result_desc,
601-
next_portal_id: Cell::new(0)
617+
next_portal_id: Cell::new(0),
618+
finished: Cell::new(false),
602619
})
603620
}
604621

622+
fn is_desynchronized(&self) -> bool {
623+
self.desynchronized
624+
}
625+
605626
fn get_type_name(&mut self, oid: Oid) -> Result<~str, PostgresError> {
606627
match self.unknown_types.find(&oid) {
607628
Some(name) => return Ok(name.clone()),
@@ -623,6 +644,7 @@ impl InnerPostgresConnection {
623644

624645
fn quick_query(&mut self, query: &str)
625646
-> Result<~[~[Option<~str>]], PostgresError> {
647+
check_desync!(self);
626648
if_ok_pg!(self.write_messages([Query { query: query }]));
627649

628650
let mut result = ~[];
@@ -640,6 +662,11 @@ impl InnerPostgresConnection {
640662
}
641663
Ok(result)
642664
}
665+
666+
fn finish_inner(&mut self) -> Result<(), PostgresError> {
667+
check_desync!(self);
668+
Ok(if_ok_pg!(self.write_messages([Terminate])))
669+
}
643670
}
644671

645672
/// A connection to a Postgres database.
@@ -736,7 +763,8 @@ impl PostgresConnection {
736763
Ok(PostgresTransaction {
737764
conn: self,
738765
commit: Cell::new(true),
739-
nested: false
766+
nested: false,
767+
finished: false,
740768
})
741769
}
742770

@@ -790,10 +818,23 @@ impl PostgresConnection {
790818
/// If this has occurred, all further queries will immediately return an
791819
/// error.
792820
pub fn is_desynchronized(&self) -> bool {
793-
self.conn.with(|conn| conn.desynchronized)
821+
self.conn.with(|conn| conn.is_desynchronized())
794822
}
795823

796-
fn quick_query(&self, query: &str) -> Result<~[~[Option<~str>]], PostgresError> {
824+
/// Consumes the connection, closing it.
825+
///
826+
/// Functionally equivalent to the `Drop` implementation for
827+
/// `PostgresConnection` except that it returns any error encountered to
828+
/// the caller.
829+
pub fn finish(self) -> Result<(), PostgresError> {
830+
self.conn.with_mut(|conn| {
831+
conn.finished = true;
832+
conn.finish_inner()
833+
})
834+
}
835+
836+
fn quick_query(&self, query: &str)
837+
-> Result<~[~[Option<~str>]], PostgresError> {
797838
self.conn.with_mut(|conn| conn.quick_query(query))
798839
}
799840

@@ -824,25 +865,39 @@ pub enum SslMode {
824865
pub struct PostgresTransaction<'conn> {
825866
priv conn: &'conn PostgresConnection,
826867
priv commit: Cell<bool>,
827-
priv nested: bool
868+
priv nested: bool,
869+
priv finished: bool,
828870
}
829871

830872
#[unsafe_destructor]
831873
impl<'conn> Drop for PostgresTransaction<'conn> {
832874
fn drop(&mut self) {
875+
if !self.finished {
876+
match self.finish_inner() {
877+
Ok(()) => {}
878+
Err(err) =>
879+
fail_unless_failing!("Error dropping transaction: {}", err)
880+
}
881+
}
882+
}
883+
}
884+
885+
impl<'conn> PostgresTransaction<'conn> {
886+
fn finish_inner(&mut self) -> Result<(), PostgresError> {
833887
if task::failing() || !self.commit.get() {
834888
if self.nested {
835-
self.conn.quick_query("ROLLBACK TO sp");
889+
if_ok!(self.conn.quick_query("ROLLBACK TO sp"));
836890
} else {
837-
self.conn.quick_query("ROLLBACK");
891+
if_ok!(self.conn.quick_query("ROLLBACK"));
838892
}
839893
} else {
840894
if self.nested {
841-
self.conn.quick_query("RELEASE sp");
895+
if_ok!(self.conn.quick_query("RELEASE sp"));
842896
} else {
843-
self.conn.quick_query("COMMIT");
897+
if_ok!(self.conn.quick_query("COMMIT"));
844898
}
845899
}
900+
Ok(())
846901
}
847902
}
848903

@@ -884,7 +939,8 @@ impl<'conn> PostgresTransaction<'conn> {
884939
Ok(PostgresTransaction {
885940
conn: self.conn,
886941
commit: Cell::new(true),
887-
nested: true
942+
nested: true,
943+
finished: false,
888944
})
889945
}
890946

@@ -920,6 +976,15 @@ impl<'conn> PostgresTransaction<'conn> {
920976
pub fn set_rollback(&self) {
921977
self.commit.set(false);
922978
}
979+
980+
/// Consumes the transaction, commiting or rolling it back as appropriate.
981+
///
982+
/// Functionally equivalent to the `Drop` implementation of
983+
/// `PostgresTransaction` except that it returns any error to the caller.
984+
pub fn finish(mut self) -> Result<(), PostgresError> {
985+
self.finished = true;
986+
self.finish_inner()
987+
}
923988
}
924989

925990
/// A trait containing methods that can be called on a prepared statement.
@@ -974,6 +1039,12 @@ pub trait PostgresStatement {
9741039
Err(err) => fail!("Error executing query:\n{}", err.to_str())
9751040
}
9761041
}
1042+
1043+
/// Consumes the statement, clearing it from the Postgres session.
1044+
///
1045+
/// Functionally identical to the `Drop` implementation of the
1046+
/// `PostgresStatement` except that it returns any error to the caller.
1047+
fn finish(self) -> Result<(), PostgresError>;
9771048
}
9781049

9791050
/// A statement prepared outside of a transaction.
@@ -982,29 +1053,42 @@ pub struct NormalPostgresStatement<'conn> {
9821053
priv name: ~str,
9831054
priv param_types: ~[PostgresType],
9841055
priv result_desc: ~[ResultDescription],
985-
priv next_portal_id: Cell<uint>
1056+
priv next_portal_id: Cell<uint>,
1057+
priv finished: Cell<bool>,
9861058
}
9871059

9881060
#[unsafe_destructor]
9891061
impl<'conn> Drop for NormalPostgresStatement<'conn> {
9901062
fn drop(&mut self) {
991-
let _ = self.conn.write_messages([
1063+
if !self.finished.get() {
1064+
match self.finish_inner() {
1065+
Ok(()) => {}
1066+
Err(err) =>
1067+
fail_unless_failing!("Error dropping statement: {}", err)
1068+
}
1069+
}
1070+
}
1071+
}
1072+
1073+
impl<'conn> NormalPostgresStatement<'conn> {
1074+
fn finish_inner(&mut self) -> Result<(), PostgresError> {
1075+
check_desync!(self.conn);
1076+
if_ok_pg!(self.conn.write_messages([
9921077
Close {
9931078
variant: 'S' as u8,
9941079
name: self.name.as_slice()
9951080
},
996-
Sync]);
1081+
Sync]));
9971082
loop {
998-
match self.conn.read_message() {
999-
Ok(ReadyForQuery { .. }) => break,
1000-
Err(_) => break,
1083+
// TODO forward db errors
1084+
match if_ok_pg!(self.conn.read_message()) {
1085+
ReadyForQuery { .. } => break,
10011086
_ => {}
10021087
}
10031088
}
1089+
Ok(())
10041090
}
1005-
}
10061091

1007-
impl<'conn> NormalPostgresStatement<'conn> {
10081092
fn execute(&self, portal_name: &str, row_limit: uint, params: &[&ToSql])
10091093
-> Result<(), PostgresError> {
10101094
let mut formats = ~[];
@@ -1059,9 +1143,10 @@ impl<'conn> NormalPostgresStatement<'conn> {
10591143
name: portal_name,
10601144
data: RingBuf::new(),
10611145
row_limit: row_limit,
1062-
more_rows: true
1146+
more_rows: true,
1147+
finished: false,
10631148
};
1064-
result.read_rows();
1149+
if_ok!(result.read_rows())
10651150

10661151
Ok(result)
10671152
}
@@ -1114,6 +1199,11 @@ impl<'conn> PostgresStatement for NormalPostgresStatement<'conn> {
11141199
check_desync!(self.conn);
11151200
self.try_lazy_query(0, params)
11161201
}
1202+
1203+
fn finish(mut self) -> Result<(), PostgresError> {
1204+
self.finished.set(true);
1205+
self.finish_inner()
1206+
}
11171207
}
11181208

11191209
/// Information about a column of the result of a query.
@@ -1161,6 +1251,10 @@ impl<'conn> PostgresStatement for TransactionalPostgresStatement<'conn> {
11611251
-> Result<PostgresResult<'a>, PostgresError> {
11621252
self.stmt.try_query(params)
11631253
}
1254+
1255+
fn finish(self) -> Result<(), PostgresError> {
1256+
self.stmt.finish()
1257+
}
11641258
}
11651259

11661260
impl<'conn> TransactionalPostgresStatement<'conn> {
@@ -1201,28 +1295,42 @@ pub struct PostgresResult<'stmt> {
12011295
priv name: ~str,
12021296
priv data: RingBuf<~[Option<~[u8]>]>,
12031297
priv row_limit: uint,
1204-
priv more_rows: bool
1298+
priv more_rows: bool,
1299+
priv finished: bool,
12051300
}
12061301

12071302
#[unsafe_destructor]
12081303
impl<'stmt> Drop for PostgresResult<'stmt> {
12091304
fn drop(&mut self) {
1210-
let _ = self.stmt.conn.write_messages([
1305+
if !self.finished {
1306+
match self.finish_inner() {
1307+
Ok(()) => {}
1308+
Err(err) =>
1309+
fail_unless_failing!("Error dropping result: {}", err)
1310+
}
1311+
}
1312+
}
1313+
}
1314+
1315+
impl<'stmt> PostgresResult<'stmt> {
1316+
fn finish_inner(&mut self) -> Result<(), PostgresError> {
1317+
check_desync!(self.stmt.conn);
1318+
if_ok_pg!(self.stmt.conn.write_messages([
12111319
Close {
12121320
variant: 'P' as u8,
12131321
name: self.name.as_slice()
12141322
},
1215-
Sync]);
1323+
Sync]));
12161324
loop {
1217-
match self.stmt.conn.read_message() {
1218-
Ok(ReadyForQuery { .. }) => break,
1325+
// TODO forward PG errors
1326+
match if_ok_pg!(self.stmt.conn.read_message()) {
1327+
ReadyForQuery { .. } => break,
12191328
_ => {}
12201329
}
12211330
}
1331+
Ok(())
12221332
}
1223-
}
12241333

1225-
impl<'stmt> PostgresResult<'stmt> {
12261334
fn read_rows(&mut self) -> Result<(), PostgresError> {
12271335
loop {
12281336
match if_ok_pg!(self.stmt.conn.read_message()) {
@@ -1253,18 +1361,40 @@ impl<'stmt> PostgresResult<'stmt> {
12531361
}
12541362
}
12551363

1256-
impl<'stmt> Iterator<PostgresRow<'stmt>> for PostgresResult<'stmt> {
1257-
fn next(&mut self) -> Option<PostgresRow<'stmt>> {
1364+
impl<'stmt> PostgresResult<'stmt> {
1365+
/// Consumes the `PostgresResult`, cleaning up associated state.
1366+
///
1367+
/// Functionally identical to the `Drop` implementation on
1368+
/// `PostgresResult` except that it returns any error to the caller.
1369+
pub fn finish(mut self) -> Result<(), PostgresError> {
1370+
self.finished = true;
1371+
self.finish_inner()
1372+
}
1373+
1374+
/// Like `PostgresResult::next` except that it returns any errors to the
1375+
/// caller instead of failing.
1376+
pub fn try_next(&mut self)
1377+
-> Result<Option<PostgresRow<'stmt>>, PostgresError> {
12581378
if self.data.is_empty() && self.more_rows {
1259-
self.execute();
1379+
if_ok!(self.execute());
12601380
}
12611381

1262-
self.data.pop_front().map(|row| {
1382+
let row = self.data.pop_front().map(|row| {
12631383
PostgresRow {
12641384
stmt: self.stmt,
12651385
data: row
12661386
}
1267-
})
1387+
});
1388+
Ok(row)
1389+
}
1390+
}
1391+
1392+
impl<'stmt> Iterator<PostgresRow<'stmt>> for PostgresResult<'stmt> {
1393+
fn next(&mut self) -> Option<PostgresRow<'stmt>> {
1394+
match self.try_next() {
1395+
Ok(ok) => ok,
1396+
Err(err) => fail!("Error fetching rows: {}", err)
1397+
}
12681398
}
12691399

12701400
fn size_hint(&self) -> (uint, Option<uint>) {
@@ -1344,3 +1474,4 @@ impl<'a> RowIndex for &'a str {
13441474
fail!("there is no column with name {}", *self);
13451475
}
13461476
}
1477+

0 commit comments

Comments
 (0)