11use std:: sync:: { Arc , Mutex } ;
22
3- use crate :: { Connection , handles:: StatementParent } ;
3+ use crate :: {
4+ Connection , CursorImpl , Error , ParameterCollectionRef ,
5+ handles:: { StatementConnection , StatementParent } ,
6+ } ;
47
58/// A convinient type alias in case you want to use a connection from multiple threads which share
69/// ownership of it.
@@ -11,3 +14,53 @@ pub type SharedConnection<'env> = Arc<Mutex<Connection<'env>>>;
1114/// Connection is guaranteed to be alive and in connected state for the lifetime of
1215/// [`SharedConnection`].
1316unsafe impl StatementParent for SharedConnection < ' _ > { }
17+
18+ /// Similar to [`crate::Connection::into_cursor`], yet it operates on an `Arc<Mutex<Connection>>`.
19+ /// `Arc<Connection>` can be used if you want shared ownership of connections. However,
20+ /// `Arc<Connection>` is not `Send` due to `Connection` not being `Sync`. So sometimes you may want
21+ /// to wrap your `Connection` into an `Arc<Mutex<Connection>>` to allow shared ownership of the
22+ /// connection across threads. This function allows you to create a cursor from such a shared
23+ /// which also holds a strong reference to it.
24+ ///
25+ /// # Parameters
26+ ///
27+ /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
28+ /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
29+ /// represent no parameters. See the [`crate::parameter`] module level documentation for more
30+ /// information on how to pass parameters.
31+ /// * `query_timeout_sec`: Use this to limit the time the query is allowed to take, before
32+ /// responding with data to the application. The driver may replace the number of seconds you
33+ /// provide with a minimum or maximum value.
34+ ///
35+ /// For the timeout to work the driver must support this feature. E.g. PostgreSQL, and Microsoft
36+ /// SQL Server do, but SQLite or MariaDB do not.
37+ ///
38+ /// You can specify ``0``, to deactivate the timeout, this is the default. So if you want no
39+ /// timeout, just leave it at `None`. Only reason to specify ``0`` is if for some reason your
40+ /// datasource does not have ``0`` as default.
41+ ///
42+ /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
43+ ///
44+ /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
45+ pub fn shared_connection_into_cursor < ' env > (
46+ connection : SharedConnection < ' env > ,
47+ query : & str ,
48+ params : impl ParameterCollectionRef ,
49+ query_timeout_sec : Option < usize > ,
50+ ) -> Result < Option < CursorImpl < StatementConnection < SharedConnection < ' env > > > > , Error > {
51+ let guard = connection
52+ . lock ( )
53+ . expect ( "Shared connection lock must not be poisned" ) ;
54+ let Some ( cursor) = guard. execute ( query, params, query_timeout_sec) ? else {
55+ return Ok ( None ) ;
56+ } ;
57+ // Deconstrurt the cursor and construct which only borrows the connection and construct a new
58+ // one which takes ownership of the instead.
59+ let stmt_ptr = cursor. into_stmt ( ) . into_sys ( ) ;
60+ drop ( guard) ;
61+ // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
62+ let stmt = unsafe { StatementConnection :: new ( stmt_ptr, connection) } ;
63+ // Safe: `stmt` is valid and in cursor state.
64+ let cursor = unsafe { CursorImpl :: new ( stmt) } ;
65+ Ok ( Some ( cursor) )
66+ }
0 commit comments