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}