odbc_api/
sync_connection.rs

1use std::sync::{Arc, Mutex};
2
3use crate::{
4    Connection, CursorImpl, Error, ParameterCollectionRef,
5    handles::{StatementConnection, StatementParent},
6};
7
8/// A convinient type alias in case you want to use a connection from multiple threads which share
9/// ownership of it.
10pub type SharedConnection<'env> = Arc<Mutex<Connection<'env>>>;
11
12/// # Safety:
13///
14/// Connection is guaranteed to be alive and in connected state for the lifetime of
15/// [`SharedConnection`].
16unsafe 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>
45pub 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}