odbc_safe/data_source/
mod.rs

1pub use self::connected::{Connected, AutocommitOff, AutocommitOn, AutocommitMode};
2pub use self::hdbc_wrapper::HDbcWrapper;
3pub use self::unconnected::Unconnected;
4use super::*;
5use sys::*;
6use std::ops::DerefMut;
7
8mod connected;
9mod unconnected;
10mod hdbc_wrapper;
11
12/// A `DataSource` is used to query and manipulate a data source.
13///
14/// * The state of the connection
15/// * The current connection-level diagnostics
16/// * The handles of statements and descriptors currently allocated on the connection
17/// * The current settings of each connection attribute
18///
19/// # States
20///
21/// A `DataSource` is in one of two states `Connected` or `Unconnected`. These are modeled in the
22/// type at compile time. Every new `DataSource` starts out as `Unconnected`. To execute a query it
23/// needs to be `Connected`. You can achieve this by calling e.g. `connect` and capture the result
24/// in a new binding which will be of type `DataSource::<'env, Connected<'env>>`.
25///
26/// See [Connection Handles in the ODBC Reference][1]
27/// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/connection-handles
28#[derive(Debug)]
29pub struct DataSource<'env, S: HDbcWrapper<'env> = Unconnected<'env>> {
30    /// Connection handle. Either `HDbc` for `Unconnected` or `Connected` for `Connected`.
31    handle: S::Handle
32}
33
34impl<'env, Any> DataSource<'env, Any>
35where
36    Any: HDbcWrapper<'env>,
37{
38    /// Consumes the `DataSource`, returning the wrapped raw `SQLHDBC`
39    ///
40    /// Leaks the Connection Handle. This is usually done in order to pass ownership from Rust to
41    /// another language. After calling this method, the caller is responsible for invoking
42    /// `SQLFreeHandle`.
43    pub fn into_raw(self) -> SQLHDBC {
44        self.handle.into_hdbc().into_raw()
45    }
46
47    /// Provides access to the raw ODBC Connection Handle
48    pub fn as_raw(&self) -> SQLHDBC {
49        self.handle.as_raw()
50    }
51
52    /// May only be invoked with a valid Statement Handle which has been allocated using
53    /// `SQLAllocHandle`. Special care must be taken that the Connection Handle passed is in a
54    /// State which matches the type.
55    pub unsafe fn from_raw(raw: SQLHDBC) -> Self {
56        DataSource { handle: Any::from_hdbc(HDbc::from_raw(raw)) }
57    }
58
59    /// Express state transiton
60    fn transit<Other: HDbcWrapper<'env>>(self) -> DataSource<'env, Other> {
61        DataSource { handle: Other::from_hdbc(self.handle.into_hdbc()) }
62    }
63}
64
65impl<'env> DataSource<'env, Unconnected<'env>> {
66    /// Allocates a new `DataSource`. A `DataSource` may not outlive its parent `Environment`.
67    ///
68    /// See [Allocating a Connection Handle ODBC][1]
69    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/allocating-a-connection-handle-odbc
70    pub fn with_parent<V>(parent: &'env Environment<V>) -> Return<Self>
71    where
72        V: Version,
73    {
74        HDbc::allocate(parent.as_henv()).map(|handle| {
75            DataSource { handle: Unconnected::from_hdbc(handle) }
76        })
77    }
78
79    /// Establishes connections to a driver and a data source. The connection handle references
80    /// storage of all information about the connection to the data source, including status,
81    /// transaction state, and error information.
82    ///
83    /// * See [Connecting with SQLConnect][1]
84    /// * See [SQLConnectFunction][2]
85    ///
86    /// # State transition
87    /// On success this method changes the Connection handles state from `Allocated` to `Connected`
88    /// . Since this state change is expressed in the type system, the method consumes self. And
89    /// returns a new instance in the result type.
90    ///
91    /// # Arguments
92    ///
93    /// * `data_source_name` - Data source name. The data might be located on the same computer as
94    ///                        the program, or on another computer somewhere on a network.
95    /// * `user` - User identifier.
96    /// * `pwd` - Authenticatien string (typically the password).
97    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function
98    /// [2]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function
99    pub fn connect<DSN, U, P>(
100        mut self,
101        data_source_name: &DSN,
102        user: &U,
103        pwd: &P,
104    ) -> Return<Connection<'env, AutocommitOn>, DataSource<'env, Unconnected<'env>>>
105    where
106        DSN: SqlStr + ?Sized,
107        U: SqlStr + ?Sized,
108        P: SqlStr + ?Sized,
109    {
110        match self.handle.connect(data_source_name, user, pwd) {
111            Success(()) => Success(self.transit()),
112            Info(()) => Info(self.transit()),
113            Error(()) => Error(self.transit()),
114        }
115    }
116
117    /// Connects to a data source using a connection string.
118    ///
119    /// For the syntax regarding the connections string see [SQLDriverConnect][1]. This method is
120    /// equivalent of calling `odbc_sys::SQLDriverConnect` with the `SQL_DRIVER_NOPROMPT` parameter.
121    ///
122    /// See [Choosing a Data Source or Driver][2]
123    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldriverconnect-function
124    /// [2]: https://docs.microsoft.com/sql/odbc/reference/develop-app/choosing-a-data-source-or-driver
125    pub fn connect_with_connection_string<C>(
126        mut self,
127        connection_string: &C,
128    ) -> Return<Connection<'env, AutocommitOn>, Self>
129    where
130        C: SqlStr + ?Sized,
131    {
132        // We do not care for now.
133        let mut out_connection_string = [];
134        match self.handle.driver_connect(
135            connection_string,
136            &mut out_connection_string,
137            SQL_DRIVER_NOPROMPT,
138        ) {
139            Success(_) => Success(self.transit()),
140            Info(_) => Info(self.transit()),
141            Error(()) => Error(self.transit()),
142        }
143    }
144}
145
146impl<'env, AC: AutocommitMode> Connection<'env, AC> {
147    /// Used by `Statement`s constructor
148    pub(crate) fn as_hdbc(&self) -> &HDbc {
149        &self.handle
150    }
151
152    /// When an application has finished using a data source, it calls `disconnect`. `disconnect`
153    /// disconnects the driver from the data source.
154    ///
155    /// * See [Disconnecting from a Data Source or Driver][1]
156    /// * See [SQLDisconnect Function][2]
157    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/disconnecting-from-a-data-source-or-driver
158    /// [2]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldisconnect-function
159    pub fn disconnect(mut self) -> Return<DataSource<'env, Unconnected<'env>>, Connection<'env, AC>> {
160        match self.handle.disconnect() {
161            Success(()) => Success(self.transit()),
162            Info(()) => Info(self.transit()),
163            Error(()) => Error(self.transit()),
164        }
165    }
166
167    /// `true` if the data source is set to READ ONLY mode, `false` otherwise.
168    pub fn is_read_only(&mut self) -> Return<bool> {
169        self.handle.is_read_only()
170    }
171}
172
173impl<'env> Connection<'env, AutocommitOff> {
174    /// Set autocommit mode on, per ODBC spec triggers implicit commit of any running transaction
175    pub fn enable_autocommit(mut self) -> Return<Connection<'env, AutocommitOn>, Self> {
176        match self.handle.set_autocommit(true) {
177            Success(_) => Success(self.transit()),
178            Info(_) => Info(self.transit()),
179            Error(()) => Error(self.transit()),
180        }
181    }
182
183    /// Commit transaction if any, can be safely called and will be no-op if no transaction present or autocommit mode is enabled
184    pub fn commit(&mut self) -> Return<()> {
185        self.handle.commit()
186    }
187
188    /// Rollback transaction if any, can be safely called and will be no-op if no transaction present or autocommit mode is enabled
189    pub fn rollback(&mut self) -> Return<()> {
190       self.handle.rollback()
191    }
192}
193
194impl<'env> Connection<'env, AutocommitOn> {
195    /// Set autocommit mode off
196    pub fn disable_autocommit(mut self) -> Return<Connection<'env, AutocommitOff>, Self> {
197        match self.handle.set_autocommit(false) {
198            Success(_) => Success(self.transit()),
199            Info(_) => Info(self.transit()),
200            Error(()) => Error(self.transit()),
201        }
202    }
203}
204
205impl<'env, S> Diagnostics for DataSource<'env, S>
206where
207    S: HDbcWrapper<'env>,
208{
209    fn diagnostics(
210        &self,
211        rec_number: SQLSMALLINT,
212        message_text: &mut [SQLCHAR],
213    ) -> ReturnOption<DiagResult> {
214        self.handle.diagnostics(rec_number, message_text)
215    }
216}