Skip to content

Commit 9f4adf2

Browse files
committed
Add Connection.call_timeout() and Connection.set_call_timeout().
1 parent bc35a3d commit 9f4adf2

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

src/connection.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::mem;
1919
use std::ptr;
2020
use std::sync::Arc;
2121
use std::sync::Mutex;
22+
use std::time::Duration;
2223

2324
use crate::binding::*;
2425
use crate::chkerr;
@@ -30,6 +31,7 @@ use crate::sql_type::ToSql;
3031
use crate::to_odpi_str;
3132
use crate::to_rust_slice;
3233
use crate::to_rust_str;
34+
use crate::util::duration_to_msecs;
3335
use crate::AssertSend;
3436
use crate::AssertSync;
3537
use crate::Context;
@@ -986,6 +988,73 @@ impl Connection {
986988
Ok(())
987989
}
988990

991+
/// Gets the current call timeout used for round-trips to
992+
/// the database made with this connection. `None` means that no timeouts
993+
/// will take place.
994+
pub fn call_timeout(&self) -> Result<Option<Duration>> {
995+
let mut value = 0;
996+
chkerr!(
997+
self.ctxt,
998+
dpiConn_getCallTimeout(self.handle.raw(), &mut value)
999+
);
1000+
if value != 0 {
1001+
Ok(Some(Duration::from_millis(value.into())))
1002+
} else {
1003+
Ok(None)
1004+
}
1005+
}
1006+
1007+
/// Sets the call timeout to be used for round-trips to the
1008+
/// database made with this connection. None means that no timeouts
1009+
/// will take place.
1010+
///
1011+
/// The call timeout value applies to each database round-trip
1012+
/// individually, not to the sum of all round-trips. Time spent
1013+
/// processing in rust-oracle before or after the completion of each
1014+
/// round-trip is not counted.
1015+
///
1016+
/// - If the time from the start of any one round-trip to the
1017+
/// completion of that same round-trip exceeds call timeout,
1018+
/// then the operation is halted and an exception occurs.
1019+
///
1020+
/// - In the case where an rust-oracle operation requires more than one
1021+
/// round-trip and each round-trip takes less than call timeout,
1022+
/// then no timeout will occur, even if the sum of all round-trip
1023+
/// calls exceeds call timeout.
1024+
///
1025+
/// - If no round-trip is required, the operation will never be
1026+
/// interrupted.
1027+
///
1028+
/// After a timeout is triggered, rust-oracle attempts to clean up the
1029+
/// internal connection state. The cleanup is allowed to take another
1030+
/// `duration`.
1031+
///
1032+
/// If the cleanup was successful, an exception DPI-1067 will be
1033+
/// raised but the application can continue to use the connection.
1034+
///
1035+
/// For small values of call timeout, the connection cleanup may not
1036+
/// complete successfully within the additional call timeout
1037+
/// period. In this case an exception ORA-3114 is raised and the
1038+
/// connection will no longer be usable. It should be closed.
1039+
pub fn set_call_timeout(&self, dur: Option<Duration>) -> Result<()> {
1040+
if let Some(dur) = dur {
1041+
let msecs = duration_to_msecs(dur).ok_or(Error::OutOfRange(format!(
1042+
"Too large duration {:?}. It must be less than 49.7 days",
1043+
dur
1044+
)))?;
1045+
if msecs == 0 {
1046+
return Err(Error::OutOfRange(format!(
1047+
"Too short duration {:?}. It must not be submilliseconds",
1048+
dur
1049+
)));
1050+
}
1051+
chkerr!(self.ctxt, dpiConn_setCallTimeout(self.handle.raw(), msecs));
1052+
} else {
1053+
chkerr!(self.ctxt, dpiConn_setCallTimeout(self.handle.raw(), 0));
1054+
}
1055+
Ok(())
1056+
}
1057+
9891058
/// Gets current schema associated with the connection
9901059
pub fn current_schema(&self) -> Result<String> {
9911060
let mut s = new_odpi_str();

src/util.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use std::fmt;
1717
use std::result;
1818
use std::str;
19+
use std::time::Duration;
1920

2021
use crate::sql_type::OracleType;
2122
use crate::Error;
@@ -194,6 +195,18 @@ pub fn write_literal(
194195
}
195196
}
196197

198+
pub fn duration_to_msecs(dur: Duration) -> Option<u32> {
199+
let msecs = dur
200+
.as_secs()
201+
.checked_mul(1000)?
202+
.checked_add(dur.subsec_nanos() as u64 / 1_000_000)?;
203+
if msecs <= u32::max_value() as u64 {
204+
Some(msecs as u32)
205+
} else {
206+
None
207+
}
208+
}
209+
197210
#[cfg(test)]
198211
mod tests {
199212
use super::*;
@@ -256,4 +269,20 @@ mod tests {
256269
Ok(vec![0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0])
257270
);
258271
}
272+
273+
#[test]
274+
fn test_duration_to_msecs() {
275+
assert_eq!(duration_to_msecs(Duration::new(0, 0)), Some(0));
276+
assert_eq!(duration_to_msecs(Duration::from_nanos(999_999)), Some(0));
277+
assert_eq!(duration_to_msecs(Duration::from_nanos(1_000_000)), Some(1));
278+
assert_eq!(
279+
duration_to_msecs(Duration::from_millis(u32::max_value() as u64)),
280+
Some(u32::max_value())
281+
);
282+
assert_eq!(
283+
duration_to_msecs(Duration::from_millis(u32::max_value() as u64 + 1)),
284+
None
285+
);
286+
assert_eq!(duration_to_msecs(Duration::new(50 * 24 * 60 * 60, 0)), None);
287+
}
259288
}

0 commit comments

Comments
 (0)