odbc_api/connection.rs
1use crate::{
2 BlockCursorIterator, ColumnsRow, CursorImpl, CursorPolling, Error, ForeignKeysRow,
3 ParameterCollectionRef, Preallocated, Prepared, PrimaryKeysRow, Sleep, TablesRow,
4 buffers::BufferDesc,
5 execute::execute_with_parameters_polling,
6 handles::{
7 self, SqlText, State, Statement, StatementConnection, StatementImpl, StatementParent,
8 slice_to_utf8,
9 },
10};
11use log::error;
12use std::{
13 borrow::Cow,
14 fmt::{self, Debug, Display},
15 mem::{ManuallyDrop, MaybeUninit},
16 ptr, str,
17 sync::Arc,
18 thread::panicking,
19};
20
21impl Drop for Connection<'_> {
22 fn drop(&mut self) {
23 match self.connection.disconnect().into_result(&self.connection) {
24 Ok(()) => (),
25 Err(Error::Diagnostics {
26 record,
27 function: _,
28 }) if record.state == State::INVALID_STATE_TRANSACTION => {
29 // Invalid transaction state. Let's rollback the current transaction and try again.
30 if let Err(e) = self.rollback() {
31 // Connection might be in a suspended state. See documentation about suspended
32 // state here:
33 // <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlendtran-function>
34 //
35 // See also issue:
36 // <https://github.com/pacman82/odbc-api/issues/574#issuecomment-2286449125>
37
38 #[cfg(not(feature = "structured_logging"))]
39 error!(
40 "Error rolling back transaction (in order to recover from invalid \
41 transaction state during disconnect): {e}"
42 );
43 #[cfg(feature = "structured_logging")]
44 error!(
45 target: "odbc_api",
46 error:err = e;
47 "Failed rollback on disconnect"
48 );
49 }
50 // Transaction might be rolled back or suspended. Now let's try again to disconnect.
51 if let Err(e) = self.connection.disconnect().into_result(&self.connection) {
52 // Avoid panicking, if we already have a panic. We don't want to mask the
53 // original error.
54 if !panicking() {
55 panic!("Unexpected error disconnecting (after rollback attempt): {e:?}")
56 }
57 }
58 }
59 Err(e) => {
60 // Avoid panicking, if we already have a panic. We don't want to mask the original
61 // error.
62 if !panicking() {
63 panic!("Unexpected error disconnecting: {e:?}")
64 }
65 }
66 }
67 }
68}
69
70/// The connection handle references storage of all information about the connection to the data
71/// source, including status, transaction state, and error information.
72///
73/// If you want to enable the connection pooling support build into the ODBC driver manager have a
74/// look at [`crate::Environment::set_connection_pooling`].
75///
76/// In order to create multiple statements with the same connection and for other use cases,
77/// operations like [`Self::execute`] or [`Self::prepare`] are taking a shared reference of `self`
78/// rather than `&mut self`. However, since error handling is done through state changes of the
79/// underlying connection managed by the ODBC driver, this implies that `Connection` must not be
80/// `Sync`.
81pub struct Connection<'c> {
82 connection: handles::Connection<'c>,
83}
84
85impl<'c> Connection<'c> {
86 pub(crate) fn new(connection: handles::Connection<'c>) -> Self {
87 Self { connection }
88 }
89
90 /// Transfer ownership of this open connection to a wrapper around the raw ODBC pointer. The
91 /// wrapper allows you to call ODBC functions on the handle, but doesn't care if the connection
92 /// is in the right state.
93 ///
94 /// You should not have a need to call this method if your use case is covered by this library,
95 /// but, in case it is not, this may help you to break out of the type structure which might be
96 /// to rigid for you, while simultaneously abondoning its safeguards.
97 pub fn into_handle(self) -> handles::Connection<'c> {
98 // We do not want the compiler to invoke `Drop`, since drop would disconnect, yet we want to
99 // transfer ownership to the connection handle.
100 let dont_drop_me = MaybeUninit::new(self);
101 let self_ptr = dont_drop_me.as_ptr();
102
103 // Safety: We know `dont_drop_me` is (still) valid at this point so reading the ptr is okay
104 unsafe { ptr::read(&(*self_ptr).connection) }
105 }
106
107 /// Executes an SQL statement. This is the fastest way to submit an SQL statement for one-time
108 /// execution. In case you do **not** want to execute more statements on this connection, you
109 /// may want to use [`Self::into_cursor`] instead, which would create a cursor taking ownership
110 /// of the connection.
111 ///
112 /// # Parameters
113 ///
114 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
115 /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
116 /// represent no parameters. See the [`crate::parameter`] module level documentation for more
117 /// information on how to pass parameters.
118 /// * `query_timeout_sec`: Use this to limit the time the query is allowed to take, before
119 /// responding with data to the application. The driver may replace the number of seconds you
120 /// provide with a minimum or maximum value.
121 ///
122 /// For the timeout to work the driver must support this feature. E.g. PostgreSQL, and
123 /// Microsoft SQL Server do, but SQLite or MariaDB do not.
124 ///
125 /// You can specify ``0``, to deactivate the timeout, this is the default. So if you want no
126 /// timeout, just leave it at `None`. Only reason to specify ``0`` is if for some reason your
127 /// datasource does not have ``0`` as default.
128 ///
129 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
130 ///
131 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
132 ///
133 /// # Return
134 ///
135 /// Returns `Some` if a cursor is created. If `None` is returned no cursor has been created (
136 /// e.g. the query came back empty). Note that an empty query may also create a cursor with zero
137 /// rows.
138 ///
139 /// # Example
140 ///
141 /// ```no_run
142 /// use odbc_api::{Environment, ConnectionOptions};
143 ///
144 /// let env = Environment::new()?;
145 ///
146 /// let mut conn = env.connect(
147 /// "YourDatabase", "SA", "My@Test@Password1",
148 /// ConnectionOptions::default()
149 /// )?;
150 /// // This query does not use any parameters.
151 /// let query_params = ();
152 /// let timeout_sec = None;
153 /// if let Some(cursor) = conn.execute(
154 /// "SELECT year, name FROM Birthdays;",
155 /// query_params,
156 /// timeout_sec)?
157 /// {
158 /// // Use cursor to process query results.
159 /// }
160 /// # Ok::<(), odbc_api::Error>(())
161 /// ```
162 pub fn execute(
163 &self,
164 query: &str,
165 params: impl ParameterCollectionRef,
166 query_timeout_sec: Option<usize>,
167 ) -> Result<Option<CursorImpl<StatementImpl<'_>>>, Error> {
168 // Only allocate the statement, if we know we are going to execute something.
169 if params.parameter_set_size() == 0 {
170 return Ok(None);
171 }
172 let mut statement = self.preallocate()?;
173 if let Some(seconds) = query_timeout_sec {
174 statement.set_query_timeout_sec(seconds)?;
175 }
176 statement.into_cursor(query, params)
177 }
178
179 /// Executes an SQL statement asynchronously using polling mode. ⚠️**Attention**⚠️: Please read
180 /// [Asynchronous execution using polling
181 /// mode](crate::guide#asynchronous-execution-using-polling-mode) before using this
182 /// functions.
183 ///
184 /// Asynchronous sibling of [`Self::execute`]. Each time the driver returns control to your
185 /// application the future returned by `sleep` is awaited, before the driver is polled again.
186 /// This avoids a busy loop. `sleep` is a synchronous factor for a future which is awaited.
187 /// `sleep` should not be implemented using a sleep which blocks the system thread, but rather
188 /// use methods provided by your asynchronous runtime. E.g.:
189 ///
190 /// ```
191 /// use odbc_api::{Connection, IntoParameter, Error};
192 /// use std::time::Duration;
193 ///
194 /// async fn insert_post<'a>(
195 /// connection: &'a Connection<'a>,
196 /// user: &str,
197 /// post: &str,
198 /// ) -> Result<(), Error> {
199 /// // Poll every 50 ms.
200 /// let sleep = || tokio::time::sleep(Duration::from_millis(50));
201 /// let sql = "INSERT INTO POSTS (user, post) VALUES (?, ?)";
202 /// // Execute query using ODBC polling method
203 /// let params = (&user.into_parameter(), &post.into_parameter());
204 /// connection.execute_polling(&sql, params, sleep).await?;
205 /// Ok(())
206 /// }
207 /// ```
208 ///
209 /// **Attention**: This feature requires driver support, otherwise the calls will just block
210 /// until they are finished. At the time of writing this out of Microsoft SQL Server,
211 /// PostgerSQL, SQLite and MariaDB this worked only with Microsoft SQL Server. For code generic
212 /// over every driver you may still use this. The functions will return with the correct results
213 /// just be aware that may block until they are finished.
214 ///
215 /// This uses the ODBC polling mode under the hood. See:
216 /// <https://learn.microsoft.com/sql/odbc/reference/develop-app/asynchronous-execution-polling-method>
217 pub async fn execute_polling(
218 &self,
219 query: &str,
220 params: impl ParameterCollectionRef,
221 sleep: impl Sleep,
222 ) -> Result<Option<CursorPolling<StatementImpl<'_>>>, Error> {
223 // Only allocate the statement, if we know we are going to execute something.
224 if params.parameter_set_size() == 0 {
225 return Ok(None);
226 }
227 let query = SqlText::new(query);
228 let mut statement = self.allocate_statement()?;
229 statement.set_async_enable(true).into_result(&statement)?;
230 execute_with_parameters_polling(statement, Some(&query), params, sleep).await
231 }
232
233 /// Similar to [`Self::execute`], but takes ownership of the connection. This is useful if e.g.
234 /// youwant to open a connection and execute a query in a function and return a self containing
235 /// cursor.
236 ///
237 /// # Parameters
238 ///
239 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
240 /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
241 /// represent no parameters. See the [`crate::parameter`] module level documentation for more
242 /// information on how to pass parameters.
243 /// * `query_timeout_sec`: Use this to limit the time the query is allowed to take, before
244 /// responding with data to the application. The driver may replace the number of seconds you
245 /// provide with a minimum or maximum value.
246 ///
247 /// For the timeout to work the driver must support this feature. E.g. PostgreSQL, and
248 /// Microsoft SQL Server do, but SQLite or MariaDB do not.
249 ///
250 /// You can specify ``0``, to deactivate the timeout, this is the default. So if you want no
251 /// timeout, just leave it at `None`. Only reason to specify ``0`` is if for some reason your
252 /// datasource does not have ``0`` as default.
253 ///
254 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
255 ///
256 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
257 ///
258 /// ```no_run
259 /// use odbc_api::{environment, Error, Cursor, ConnectionOptions};
260 ///
261 ///
262 /// const CONNECTION_STRING: &str =
263 /// "Driver={ODBC Driver 18 for SQL Server};\
264 /// Server=localhost;UID=SA;\
265 /// PWD=My@Test@Password1;";
266 ///
267 /// fn execute_query(query: &str) -> Result<Option<impl Cursor>, Error> {
268 /// let env = environment()?;
269 /// let conn = env.connect_with_connection_string(
270 /// CONNECTION_STRING,
271 /// ConnectionOptions::default()
272 /// )?;
273 ///
274 /// // connect.execute(&query, (), None) // Compiler error: Would return local ref to
275 /// // `conn`.
276 ///
277 /// let maybe_cursor = conn.into_cursor(&query, (), None)?;
278 /// Ok(maybe_cursor)
279 /// }
280 /// ```
281 pub fn into_cursor(
282 self,
283 query: &str,
284 params: impl ParameterCollectionRef,
285 query_timeout_sec: Option<usize>,
286 ) -> Result<Option<CursorImpl<StatementConnection<Connection<'c>>>>, ConnectionAndError<'c>>
287 {
288 // With the current Rust version the borrow checker needs some convincing, so that it allows
289 // us to return the Connection, even though the Result of execute borrows it.
290 let mut error = None;
291 let mut cursor = None;
292 match self.execute(query, params, query_timeout_sec) {
293 Ok(Some(c)) => cursor = Some(c),
294 Ok(None) => return Ok(None),
295 Err(e) => error = Some(e),
296 };
297 if let Some(e) = error {
298 drop(cursor);
299 return Err(ConnectionAndError {
300 error: e,
301 previous: self,
302 });
303 }
304 let cursor = cursor.unwrap();
305 // The rust compiler needs some help here. It assumes otherwise that the lifetime of the
306 // resulting cursor would depend on the lifetime of `params`.
307 let mut cursor = ManuallyDrop::new(cursor);
308 let handle = cursor.as_sys();
309 // Safe: `handle` is a valid statement, and we are giving up ownership of `self`.
310 let statement = unsafe { StatementConnection::new(handle, self) };
311 // Safe: `statement is in the cursor state`.
312 let cursor = unsafe { CursorImpl::new(statement) };
313 Ok(Some(cursor))
314 }
315
316 /// Prepares an SQL statement. This is recommended for repeated execution of similar queries.
317 ///
318 /// Should your use case require you to execute the same query several times with different
319 /// parameters, prepared queries are the way to go. These give the database a chance to cache
320 /// the access plan associated with your SQL statement. It is not unlike compiling your program
321 /// once and executing it several times.
322 ///
323 /// ```
324 /// use odbc_api::{Connection, Error, IntoParameter};
325 /// use std::io::{self, stdin, Read};
326 ///
327 /// fn interactive(conn: &Connection) -> io::Result<()>{
328 /// let mut prepared = conn.prepare("SELECT * FROM Movies WHERE title=?;").unwrap();
329 /// let mut title = String::new();
330 /// stdin().read_line(&mut title)?;
331 /// while !title.is_empty() {
332 /// match prepared.execute(&title.as_str().into_parameter()) {
333 /// Err(e) => println!("{}", e),
334 /// // Most drivers would return a result set even if no Movie with the title is found,
335 /// // the result set would just be empty. Well, most drivers.
336 /// Ok(None) => println!("No result set generated."),
337 /// Ok(Some(cursor)) => {
338 /// // ...print cursor contents...
339 /// }
340 /// }
341 /// stdin().read_line(&mut title)?;
342 /// }
343 /// Ok(())
344 /// }
345 /// ```
346 ///
347 /// # Parameters
348 ///
349 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;". `?`
350 /// may be used as a placeholder in the statement text, to be replaced with parameters during
351 /// execution.
352 pub fn prepare(&self, query: &str) -> Result<Prepared<StatementImpl<'_>>, Error> {
353 let query = SqlText::new(query);
354 let mut stmt = self.allocate_statement()?;
355 stmt.prepare(&query).into_result(&stmt)?;
356 Ok(Prepared::new(stmt))
357 }
358
359 /// Prepares an SQL statement which takes ownership of the connection. The advantage over
360 /// [`Self::prepare`] is, that you do not need to keep track of the lifetime of the connection
361 /// seperatly and can create types which do own the prepared query and only depend on the
362 /// lifetime of the environment. The downside is that you can not use the connection for
363 /// anything else anymore.
364 ///
365 /// # Parameters
366 ///
367 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;". `?`
368 /// may be used as a placeholder in the statement text, to be replaced with parameters during
369 /// execution.
370 ///
371 /// ```no_run
372 /// use odbc_api::{
373 /// environment, Error, ColumnarBulkInserter, handles::StatementConnection, BindParamDesc,
374 /// buffers::AnyBuffer, ConnectionOptions, Connection, parameter::WithDataType,
375 /// };
376 ///
377 /// const CONNECTION_STRING: &str =
378 /// "Driver={ODBC Driver 18 for SQL Server};\
379 /// Server=localhost;UID=SA;\
380 /// PWD=My@Test@Password1;";
381 ///
382 /// /// Supports columnar bulk inserts on a heterogenous schema (columns have different types),
383 /// /// takes ownership of a connection created using an environment with static lifetime.
384 /// type Inserter = ColumnarBulkInserter<
385 /// StatementConnection<Connection<'static>>,
386 /// WithDataType<AnyBuffer>
387 /// >;
388 ///
389 /// /// Creates an inserter which can be reused to bulk insert birthyears with static lifetime.
390 /// fn make_inserter(query: &str) -> Result<Inserter, Error> {
391 /// let env = environment()?;
392 /// let conn = env.connect_with_connection_string(
393 /// CONNECTION_STRING,
394 /// ConnectionOptions::default()
395 /// )?;
396 /// let prepared = conn.into_prepared("INSERT INTO Birthyear (name, year) VALUES (?, ?)")?;
397 /// let params = [
398 /// BindParamDesc::text(255),
399 /// BindParamDesc::i16(false),
400 /// ];
401 /// let capacity = 400;
402 /// prepared.into_column_inserter(capacity, params)
403 /// }
404 /// ```
405 pub fn into_prepared(
406 self,
407 query: &str,
408 ) -> Result<Prepared<StatementConnection<Connection<'c>>>, Error> {
409 let query = SqlText::new(query);
410 let mut stmt = self.allocate_statement()?;
411 stmt.prepare(&query).into_result(&stmt)?;
412 // Safe: `handle` is a valid statement, and we are giving up ownership of `self`.
413 let stmt = unsafe { StatementConnection::new(stmt.into_sys(), self) };
414 Ok(Prepared::new(stmt))
415 }
416
417 /// Allocates an SQL statement handle. This is recommended if you want to sequentially execute
418 /// different queries over the same connection, as you avoid the overhead of allocating a
419 /// statement handle for each query.
420 ///
421 /// Should you want to repeatedly execute the same query with different parameters try
422 /// [`Self::prepare`] instead.
423 ///
424 /// # Example
425 ///
426 /// ```
427 /// use odbc_api::{Connection, Error};
428 /// use std::io::{self, stdin, Read};
429 ///
430 /// fn interactive(conn: &Connection) -> io::Result<()>{
431 /// let mut statement = conn.preallocate().unwrap();
432 /// let mut query = String::new();
433 /// stdin().read_line(&mut query)?;
434 /// while !query.is_empty() {
435 /// match statement.execute(&query, ()) {
436 /// Err(e) => println!("{}", e),
437 /// Ok(None) => println!("No results set generated."),
438 /// Ok(Some(cursor)) => {
439 /// // ...print cursor contents...
440 /// },
441 /// }
442 /// stdin().read_line(&mut query)?;
443 /// }
444 /// Ok(())
445 /// }
446 /// ```
447 pub fn preallocate(&self) -> Result<Preallocated<StatementImpl<'_>>, Error> {
448 let stmt = self.allocate_statement()?;
449 unsafe { Ok(Preallocated::new(stmt)) }
450 }
451
452 /// Creates a preallocated statement handle like [`Self::preallocate`]. Yet the statement handle
453 /// also takes ownership of the connection.
454 pub fn into_preallocated(
455 self,
456 ) -> Result<Preallocated<StatementConnection<Connection<'c>>>, Error> {
457 let stmt = self.allocate_statement()?;
458 // Safe: We know `stmt` is a valid statement handle and self is the connection which has
459 // been used to allocate it.
460 unsafe {
461 let stmt = StatementConnection::new(stmt.into_sys(), self);
462 Ok(Preallocated::new(stmt))
463 }
464 }
465
466 /// Specify the transaction mode. By default, ODBC transactions are in auto-commit mode.
467 /// Switching from manual-commit mode to auto-commit mode automatically commits any open
468 /// transaction on the connection. There is no open or begin transaction method. Each statement
469 /// execution automatically starts a new transaction or adds to the existing one.
470 ///
471 /// In manual commit mode you can use [`Connection::commit`] or [`Connection::rollback`]. Keep
472 /// in mind, that even `SELECT` statements can open new transactions. This library will rollback
473 /// open transactions if a connection goes out of SCOPE. This however will log an error, since
474 /// the transaction state is only discovered during a failed disconnect. It is preferable that
475 /// the application makes sure all transactions are closed if in manual commit mode.
476 pub fn set_autocommit(&self, enabled: bool) -> Result<(), Error> {
477 self.connection
478 .set_autocommit(enabled)
479 .into_result(&self.connection)
480 }
481
482 /// To commit a transaction in manual-commit mode.
483 pub fn commit(&self) -> Result<(), Error> {
484 self.connection.commit().into_result(&self.connection)
485 }
486
487 /// To rollback a transaction in manual-commit mode.
488 pub fn rollback(&self) -> Result<(), Error> {
489 self.connection.rollback().into_result(&self.connection)
490 }
491
492 /// Indicates the state of the connection. If `true` the connection has been lost. If `false`,
493 /// the connection is still active.
494 pub fn is_dead(&self) -> Result<bool, Error> {
495 self.connection.is_dead().into_result(&self.connection)
496 }
497
498 /// Network packet size in bytes. Requries driver support.
499 pub fn packet_size(&self) -> Result<u32, Error> {
500 self.connection.packet_size().into_result(&self.connection)
501 }
502
503 /// Get the name of the database management system used by the connection.
504 pub fn database_management_system_name(&self) -> Result<String, Error> {
505 let mut buf = Vec::new();
506 self.connection
507 .fetch_database_management_system_name(&mut buf)
508 .into_result(&self.connection)?;
509 let name = slice_to_utf8(&buf).unwrap();
510 Ok(name)
511 }
512
513 /// Maximum length of catalog names.
514 pub fn max_catalog_name_len(&self) -> Result<u16, Error> {
515 self.connection
516 .max_catalog_name_len()
517 .into_result(&self.connection)
518 }
519
520 /// Maximum length of schema names.
521 pub fn max_schema_name_len(&self) -> Result<u16, Error> {
522 self.connection
523 .max_schema_name_len()
524 .into_result(&self.connection)
525 }
526
527 /// Maximum length of table names.
528 pub fn max_table_name_len(&self) -> Result<u16, Error> {
529 self.connection
530 .max_table_name_len()
531 .into_result(&self.connection)
532 }
533
534 /// Maximum length of column names.
535 pub fn max_column_name_len(&self) -> Result<u16, Error> {
536 self.connection
537 .max_column_name_len()
538 .into_result(&self.connection)
539 }
540
541 /// Get the name of the current catalog being used by the connection.
542 pub fn current_catalog(&self) -> Result<String, Error> {
543 let mut buf = Vec::new();
544 self.connection
545 .fetch_current_catalog(&mut buf)
546 .into_result(&self.connection)?;
547 let name = slice_to_utf8(&buf).expect("Return catalog must be correctly encoded");
548 Ok(name)
549 }
550
551 /// An iterator over the columns of tables matching the patterns. Patterns support `%` for
552 /// multiple characters or `_` for a single character. Use `\` to escape.
553 ///
554 /// Returns an iterator over [`ColumnsRow`] items. If you need the raw cursor (e.g. to access
555 /// driver-specific columns beyond the standard 18), use [`Preallocated::columns_cursor`]
556 /// instead.
557 pub fn columns(
558 &self,
559 catalog_name: &str,
560 schema_name: &str,
561 table_name: &str,
562 column_name: &str,
563 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, ColumnsRow>, Error> {
564 let stmt = self.preallocate()?;
565 stmt.into_columns(catalog_name, schema_name, table_name, column_name)
566 }
567
568 /// List tables, schemas, views and catalogs of a datasource.
569 ///
570 /// # Parameters
571 ///
572 /// * `catalog_name`: Filter result by catalog name. Accept search patterns. Use `%` to match
573 /// any number of characters. Use `_` to match exactly on character. Use `\` to escape
574 /// characeters.
575 /// * `schema_name`: Filter result by schema. Accepts patterns in the same way as
576 /// `catalog_name`.
577 /// * `table_name`: Filter result by table. Accepts patterns in the same way as `catalog_name`.
578 /// * `table_type`: Filters results by table type. E.g: 'TABLE', 'VIEW'. This argument accepts a
579 /// comma separeted list of table types. Omit it to not filter the result by table type at
580 /// all.
581 ///
582 /// # Example
583 ///
584 /// ```
585 /// use odbc_api::{Connection, Error, TablesRow};
586 ///
587 /// fn print_all_tables(conn: &Connection<'_>) -> Result<(), Error> {
588 /// for row in conn.tables("", "", "", "")? {
589 /// let row: TablesRow = row?;
590 /// let table = row.table.as_str().unwrap().unwrap_or("NULL");
591 /// let catalog = row.catalog.as_str().unwrap().unwrap_or("NULL");
592 /// let schema = row.schema.as_str().unwrap().unwrap_or("NULL");
593 /// println!("{catalog}.{schema}.{table}");
594 /// }
595 /// Ok(())
596 /// }
597 /// ```
598 pub fn tables(
599 &self,
600 catalog_name: &str,
601 schema_name: &str,
602 table_name: &str,
603 table_type: &str,
604 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, TablesRow>, Error> {
605 let statement = self.preallocate()?;
606 statement.into_tables(catalog_name, schema_name, table_name, table_type)
607 }
608
609 /// Create a result set which contains the column names that make up the primary key for the
610 /// table.
611 ///
612 /// # Parameters
613 ///
614 /// * `catalog_name`: Catalog name. If a driver supports catalogs for some tables but not for
615 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
616 /// denotes those tables that do not have catalogs. `catalog_name` must not contain a string
617 /// search pattern.
618 /// * `schema_name`: Schema name. If a driver supports schemas for some tables but not for
619 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
620 /// denotes those tables that do not have schemas. `schema_name` must not contain a string
621 /// search pattern.
622 /// * `table_name`: Table name. `table_name` must not contain a string search pattern.
623 ///
624 /// The resulting result set contains the following columns:
625 ///
626 /// * `TABLE_CAT`: Primary key table catalog name. NULL if not applicable to the data source. If
627 /// a driver supports catalogs for some tables but not for others, such as when the driver
628 /// retrieves data from different DBMSs, it returns an empty string ("") for those tables that
629 /// do not have catalogs. `VARCHAR`
630 /// * `TABLE_SCHEM`: Primary key table schema name; NULL if not applicable to the data source.
631 /// If a driver supports schemas for some tables but not for others, such as when the driver
632 /// retrieves data from different DBMSs, it returns an empty string ("") for those tables that
633 /// do not have schemas. `VARCHAR`
634 /// * `TABLE_NAME`: Primary key table name. `VARCHAR NOT NULL`
635 /// * `COLUMN_NAME`: Primary key column name. The driver returns an empty string for a column
636 /// that does not have a name. `VARCHAR NOT NULL`
637 /// * `KEY_SEQ`: Column sequence number in key (starting with 1). `SMALLINT NOT NULL`
638 /// * `PK_NAME`: Primary key name. NULL if not applicable to the data source. `VARCHAR`
639 ///
640 /// The maximum length of the VARCHAR columns is driver specific.
641 ///
642 /// If [`crate::sys::StatementAttribute::MetadataId`] statement attribute is set to true,
643 /// catalog, schema and table name parameters are treated as an identifiers and their case is
644 /// not significant. If it is false, they are ordinary arguments. As such they treated literally
645 /// and their case is significant.
646 ///
647 /// See: <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlprimarykeys-function>
648 pub fn primary_keys(
649 &self,
650 catalog_name: Option<&str>,
651 schema_name: Option<&str>,
652 table_name: &str,
653 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, PrimaryKeysRow>, Error> {
654 let stmt = self.preallocate()?;
655 stmt.into_primary_keys(catalog_name, schema_name, table_name)
656 }
657
658 /// This can be used to retrieve either a list of foreign keys in the specified table or a list
659 /// of foreign keys in other table that refer to the primary key of the specified table.
660 ///
661 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function>
662 pub fn foreign_keys(
663 &self,
664 pk_catalog_name: &str,
665 pk_schema_name: &str,
666 pk_table_name: &str,
667 fk_catalog_name: &str,
668 fk_schema_name: &str,
669 fk_table_name: &str,
670 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, ForeignKeysRow>, Error> {
671 let statement = self.preallocate()?;
672 statement.into_foreign_keys(
673 pk_catalog_name,
674 pk_schema_name,
675 pk_table_name,
676 fk_catalog_name,
677 fk_schema_name,
678 fk_table_name,
679 )
680 }
681
682 /// The buffer descriptions for all standard buffers (not including extensions) returned in the
683 /// columns query (e.g. [`Preallocated::columns_cursor`]).
684 ///
685 /// # Arguments
686 ///
687 /// * `type_name_max_len` - The maximum expected length of type names.
688 /// * `remarks_max_len` - The maximum expected length of remarks.
689 /// * `column_default_max_len` - The maximum expected length of column defaults.
690 #[deprecated(
691 note = "Use `Connection::columns` or `Preallocated::columns` which return strongly typed \
692 `ColumnsRow` items instead."
693 )]
694 pub fn columns_buffer_descs(
695 &self,
696 type_name_max_len: usize,
697 remarks_max_len: usize,
698 column_default_max_len: usize,
699 ) -> Result<Vec<BufferDesc>, Error> {
700 let null_i16 = BufferDesc::I16 { nullable: true };
701
702 let not_null_i16 = BufferDesc::I16 { nullable: false };
703
704 let null_i32 = BufferDesc::I32 { nullable: true };
705
706 // The definitions for these descriptions are taken from the documentation of `SQLColumns`
707 // located at https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcolumns-function
708 let catalog_name_desc = BufferDesc::Text {
709 max_str_len: self.max_catalog_name_len()? as usize,
710 };
711
712 let schema_name_desc = BufferDesc::Text {
713 max_str_len: self.max_schema_name_len()? as usize,
714 };
715
716 let table_name_desc = BufferDesc::Text {
717 max_str_len: self.max_table_name_len()? as usize,
718 };
719
720 let column_name_desc = BufferDesc::Text {
721 max_str_len: self.max_column_name_len()? as usize,
722 };
723
724 let data_type_desc = not_null_i16;
725
726 let type_name_desc = BufferDesc::Text {
727 max_str_len: type_name_max_len,
728 };
729
730 let column_size_desc = null_i32;
731 let buffer_len_desc = null_i32;
732 let decimal_digits_desc = null_i16;
733 let precision_radix_desc = null_i16;
734 let nullable_desc = not_null_i16;
735
736 let remarks_desc = BufferDesc::Text {
737 max_str_len: remarks_max_len,
738 };
739
740 let column_default_desc = BufferDesc::Text {
741 max_str_len: column_default_max_len,
742 };
743
744 let sql_data_type_desc = not_null_i16;
745 let sql_datetime_sub_desc = null_i16;
746 let char_octet_len_desc = null_i32;
747 let ordinal_pos_desc = BufferDesc::I32 { nullable: false };
748
749 // We expect strings to be `YES`, `NO`, or a zero-length string, so `3` should be
750 // sufficient.
751 const IS_NULLABLE_LEN_MAX_LEN: usize = 3;
752 let is_nullable_desc = BufferDesc::Text {
753 max_str_len: IS_NULLABLE_LEN_MAX_LEN,
754 };
755
756 Ok(vec![
757 catalog_name_desc,
758 schema_name_desc,
759 table_name_desc,
760 column_name_desc,
761 data_type_desc,
762 type_name_desc,
763 column_size_desc,
764 buffer_len_desc,
765 decimal_digits_desc,
766 precision_radix_desc,
767 nullable_desc,
768 remarks_desc,
769 column_default_desc,
770 sql_data_type_desc,
771 sql_datetime_sub_desc,
772 char_octet_len_desc,
773 ordinal_pos_desc,
774 is_nullable_desc,
775 ])
776 }
777
778 fn allocate_statement(&self) -> Result<StatementImpl<'_>, Error> {
779 self.connection
780 .allocate_statement()
781 .into_result(&self.connection)
782 }
783}
784
785/// Implement `Debug` for [`Connection`], in order to play nice with derive Debugs for struct
786/// holding a [`Connection`].
787impl Debug for Connection<'_> {
788 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
789 write!(f, "Connection")
790 }
791}
792
793/// We need to implement [`StatementParent`] for [`Connection`] in order to express ownership of a
794/// connection for a statement handle. This is e.g. needed for [`Connection::into_cursor`].
795///
796/// # Safety:
797///
798/// Connection wraps an open Connection. It keeps the handle alive and valid during its lifetime.
799unsafe impl StatementParent for Connection<'_> {}
800
801/// We need to implement [`StatementParent`] for `Arc<Connection>` in order to be able to express
802/// ownership of a shared connection from a statement handle. This is e.g. needed for
803/// [`ConnectionTransitions::into_cursor`].
804///
805/// # Safety:
806///
807/// `Arc<Connection>` wraps an open Connection. It keeps the handle alive and valid during its
808/// lifetime.
809unsafe impl StatementParent for Arc<Connection<'_>> {}
810
811/// Options to be passed then opening a connection to a datasource.
812#[derive(Default, Clone, Copy)]
813pub struct ConnectionOptions {
814 /// Number of seconds to wait for a login request to complete before returning to the
815 /// application. The default is driver-dependent. If `0` the timeout is disabled and a
816 /// connection attempt will wait indefinitely.
817 ///
818 /// If the specified timeout exceeds the maximum login timeout in the data source, the driver
819 /// substitutes that value and uses the maximum login timeout instead.
820 ///
821 /// This corresponds to the `SQL_ATTR_LOGIN_TIMEOUT` attribute in the ODBC specification.
822 ///
823 /// See:
824 /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetconnectattr-function>
825 pub login_timeout_sec: Option<u32>,
826 /// Packet size in bytes. Not all drivers support this option.
827 pub packet_size: Option<u32>,
828}
829
830impl ConnectionOptions {
831 /// Set the attributes corresponding to the connection options to an allocated connection
832 /// handle. Usually you would rather provide the options then creating the connection with e.g.
833 /// [`crate::Environment::connect_with_connection_string`] rather than calling this method
834 /// yourself.
835 pub fn apply(&self, handle: &handles::Connection) -> Result<(), Error> {
836 if let Some(timeout) = self.login_timeout_sec {
837 handle.set_login_timeout_sec(timeout).into_result(handle)?;
838 }
839 if let Some(packet_size) = self.packet_size {
840 handle.set_packet_size(packet_size).into_result(handle)?;
841 }
842 Ok(())
843 }
844}
845
846/// You can use this method to escape a password so it is suitable to be appended to an ODBC
847/// connection string as the value for the `PWD` attribute. This method is only of interest for
848/// application in need to create their own connection strings.
849///
850/// See:
851///
852/// * <https://stackoverflow.com/questions/22398212/escape-semicolon-in-odbc-connection-string-in-app-config-file>
853/// * <https://docs.microsoft.com/en-us/dotnet/api/system.data.odbc.odbcconnection.connectionstring>
854///
855/// # Example
856///
857/// ```
858/// use odbc_api::escape_attribute_value;
859///
860/// let password = "abc;123}";
861/// let user = "SA";
862/// let mut connection_string_without_credentials =
863/// "Driver={ODBC Driver 18 for SQL Server};Server=localhost;";
864///
865/// let connection_string = format!(
866/// "{}UID={};PWD={};",
867/// connection_string_without_credentials,
868/// user,
869/// escape_attribute_value(password)
870/// );
871///
872/// assert_eq!(
873/// "Driver={ODBC Driver 18 for SQL Server};Server=localhost;UID=SA;PWD={abc;123}}};",
874/// connection_string
875/// );
876/// ```
877///
878/// ```
879/// use odbc_api::escape_attribute_value;
880/// assert_eq!("abc", escape_attribute_value("abc"));
881/// assert_eq!("ab}c", escape_attribute_value("ab}c"));
882/// assert_eq!("{ab;c}", escape_attribute_value("ab;c"));
883/// assert_eq!("{a}}b;c}", escape_attribute_value("a}b;c"));
884/// assert_eq!("{ab+c}", escape_attribute_value("ab+c"));
885/// ```
886pub fn escape_attribute_value(unescaped: &str) -> Cow<'_, str> {
887 // Search the string for semicolon (';') if we do not find any, nothing is to do and we can work
888 // without an extra allocation.
889 //
890 // * We escape ';' because it serves as a separator between key=value pairs
891 // * We escape '+' because passwords with `+` must be escaped on PostgreSQL for some reason.
892 if unescaped.contains(&[';', '+'][..]) {
893 // Surround the string with curly braces ('{','}') and escape every closing curly brace by
894 // repeating it.
895 let escaped = unescaped.replace('}', "}}");
896 Cow::Owned(format!("{{{escaped}}}"))
897 } else {
898 Cow::Borrowed(unescaped)
899 }
900}
901
902/// A pair of the error and the previous state, before the operation caused the error.
903///
904/// Some functions in this crate take a `self` and return another type in the result to express a
905/// state transitions in the underlying ODBC handle. In order to make such operations retryable, or
906/// offer other alternatives of recovery, they may return this error type instead of a plain
907/// [`Error`].
908#[derive(Debug)]
909pub struct FailedStateTransition<S> {
910 /// The ODBC error which caused the state transition to fail.
911 pub error: Error,
912 /// The state before the transition failed. This is useful to e.g. retry the operation, or
913 /// recover in another way.
914 pub previous: S,
915}
916
917impl<S> From<FailedStateTransition<S>> for Error {
918 fn from(value: FailedStateTransition<S>) -> Self {
919 value.error
920 }
921}
922
923impl<S> Display for FailedStateTransition<S> {
924 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
925 write!(f, "{}", self.error)
926 }
927}
928
929impl<S> std::error::Error for FailedStateTransition<S>
930where
931 S: Debug,
932{
933 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
934 self.error.source()
935 }
936}
937
938/// An error type wrapping an [`Error`] and a [`Connection`]. It is used by
939/// [`Connection::into_cursor`], so that in case of failure the user can reuse the connection to try
940/// again. [`Connection::into_cursor`] could achieve the same by returning a tuple in case of an
941/// error, but this type causes less friction in most scenarios because [`Error`] implements
942/// [`From`] [`ConnectionAndError`] and it therfore works with the question mark operater (`?`).
943type ConnectionAndError<'conn> = FailedStateTransition<Connection<'conn>>;
944
945/// Ability to transition ownership of the connection to various children which represent statement
946/// handles in various states. E.g. [`crate::Prepared`] or [`crate::Cursor`]. Transfering ownership
947/// of the connection could e.g. be useful if you want to clean the connection after you are done
948/// with the child.
949///
950/// Having this in a trait rather than directly on [`Connection`] allows us to be generic over the
951/// type of ownership we express. E.g. we can express shared ownership of a connection by
952/// using an `Arc<Mutex<Connection>>` or `Arc<Connection>`. Or a still exclusive ownership using
953/// a plain [`Connection`].
954pub trait ConnectionTransitions: Sized {
955 // Note to self. This might eveolve into a `Connection` trait. Which expresses ownership
956 // of a connection (shared or not). It could allow to get a dereferened borrowed conection
957 // which does not allow for state transtions as of now (like StatementRef). I may not want to
958 // rock the boat that much right now.
959
960 /// The type passed to [crate::handles::StatementConnection] to express ownership of the
961 /// connection.
962 type StatementParent: StatementParent;
963
964 /// Similar to [`crate::Connection::into_cursor`], yet it operates on an
965 /// `Arc<Mutex<Connection>>`. `Arc<Connection>` can be used if you want shared ownership of
966 /// connections. However, `Arc<Connection>` is not `Send` due to `Connection` not being `Sync`.
967 /// So sometimes you may want to wrap your `Connection` into an `Arc<Mutex<Connection>>` to
968 /// allow shared ownership of the connection across threads. This function allows you to create
969 /// a cursor from such a shared which also holds a strong reference to it.
970 ///
971 /// # Parameters
972 ///
973 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
974 /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
975 /// represent no parameters. See the [`crate::parameter`] module level documentation for more
976 /// information on how to pass parameters.
977 /// * `query_timeout_sec`: Use this to limit the time the query is allowed to take, before
978 /// responding with data to the application. The driver may replace the number of seconds you
979 /// provide with a minimum or maximum value.
980 ///
981 /// For the timeout to work the driver must support this feature. E.g. PostgreSQL, and
982 /// Microsoft SQL Server do, but SQLite or MariaDB do not.
983 ///
984 /// You can specify ``0``, to deactivate the timeout, this is the default. So if you want no
985 /// timeout, just leave it at `None`. Only reason to specify ``0`` is if for some reason your
986 /// datasource does not have ``0`` as default.
987 ///
988 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
989 ///
990 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
991 fn into_cursor(
992 self,
993 query: &str,
994 params: impl ParameterCollectionRef,
995 query_timeout_sec: Option<usize>,
996 ) -> Result<
997 Option<CursorImpl<StatementConnection<Self::StatementParent>>>,
998 FailedStateTransition<Self>,
999 >;
1000
1001 /// Prepares an SQL statement which takes ownership of the connection. The advantage over
1002 /// [`Connection::prepare`] is, that you do not need to keep track of the lifetime of the
1003 /// connection seperatly and can create types which do own the prepared query and only depend on
1004 /// the lifetime of the environment.
1005 ///
1006 /// # Parameters
1007 ///
1008 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;". `?`
1009 /// may be used as a placeholder in the statement text, to be replaced with parameters during
1010 /// execution.
1011 ///
1012 /// ```no_run
1013 /// use odbc_api::{
1014 /// environment, Error, ColumnarBulkInserter, ConnectionTransitions, Connection,
1015 /// handles::StatementConnection, buffers::AnyBuffer, ConnectionOptions, BindParamDesc,
1016 /// parameter::WithDataType,
1017 ///
1018 /// };
1019 ///
1020 /// const CONNECTION_STRING: &str =
1021 /// "Driver={ODBC Driver 18 for SQL Server};\
1022 /// Server=localhost;UID=SA;\
1023 /// PWD=My@Test@Password1;";
1024 ///
1025 /// /// Supports columnar bulk inserts on a heterogenous schema (columns have different types),
1026 /// /// takes ownership of a connection created using an environment with static lifetime.
1027 /// type Inserter = ColumnarBulkInserter<
1028 /// StatementConnection<Connection<'static>>,
1029 /// WithDataType<AnyBuffer>
1030 /// >;
1031 ///
1032 /// /// Creates an inserter which can be reused to bulk insert birthyears with static lifetime.
1033 /// fn make_inserter(query: &str) -> Result<Inserter, Error> {
1034 /// let env = environment()?;
1035 /// let conn = env.connect_with_connection_string(
1036 /// CONNECTION_STRING,
1037 /// ConnectionOptions::default()
1038 /// )?;
1039 /// let prepared = conn.into_prepared("INSERT INTO Birthyear (name, year) VALUES (?, ?)")?;
1040 /// let buffers = [
1041 /// BindParamDesc::text(255),
1042 /// BindParamDesc::i16(false),
1043 /// ];
1044 /// let capacity = 400;
1045 /// prepared.into_column_inserter(capacity, buffers)
1046 /// }
1047 /// ```
1048 fn into_prepared(
1049 self,
1050 query: &str,
1051 ) -> Result<Prepared<StatementConnection<Self::StatementParent>>, Error>;
1052
1053 /// Creates a preallocated statement handle like [`Connection::preallocate`]. Yet the statement
1054 /// also takes ownership of the connection.
1055 fn into_preallocated(
1056 self,
1057 ) -> Result<Preallocated<StatementConnection<Self::StatementParent>>, Error>;
1058}
1059
1060impl<'env> ConnectionTransitions for Connection<'env> {
1061 type StatementParent = Self;
1062
1063 fn into_cursor(
1064 self,
1065 query: &str,
1066 params: impl ParameterCollectionRef,
1067 query_timeout_sec: Option<usize>,
1068 ) -> Result<Option<CursorImpl<StatementConnection<Self>>>, FailedStateTransition<Self>> {
1069 self.into_cursor(query, params, query_timeout_sec)
1070 }
1071
1072 fn into_prepared(self, query: &str) -> Result<Prepared<StatementConnection<Self>>, Error> {
1073 self.into_prepared(query)
1074 }
1075
1076 fn into_preallocated(self) -> Result<Preallocated<StatementConnection<Self>>, Error> {
1077 self.into_preallocated()
1078 }
1079}
1080
1081impl<'env> ConnectionTransitions for Arc<Connection<'env>> {
1082 type StatementParent = Self;
1083
1084 fn into_cursor(
1085 self,
1086 query: &str,
1087 params: impl ParameterCollectionRef,
1088 query_timeout_sec: Option<usize>,
1089 ) -> Result<Option<CursorImpl<StatementConnection<Self>>>, FailedStateTransition<Self>> {
1090 // Result borrows the connection. We convert the cursor into a raw pointer, to not confuse
1091 // the borrow checker.
1092 let result = self.execute(query, params, query_timeout_sec);
1093 let maybe_stmt_ptr = result
1094 .map(|opt| opt.map(|cursor| cursor.into_stmt().into_sys()))
1095 .map_err(|error| {
1096 // If the execute fails, we return a FailedStateTransition with the error and the
1097 // connection.
1098 FailedStateTransition {
1099 error,
1100 previous: Arc::clone(&self),
1101 }
1102 })?;
1103 let Some(stmt_ptr) = maybe_stmt_ptr else {
1104 return Ok(None);
1105 };
1106 // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
1107 let stmt = unsafe { StatementConnection::new(stmt_ptr, self) };
1108 // Safe: `stmt` is valid and in cursor state.
1109 let cursor = unsafe { CursorImpl::new(stmt) };
1110 Ok(Some(cursor))
1111 }
1112
1113 fn into_prepared(self, query: &str) -> Result<Prepared<StatementConnection<Self>>, Error> {
1114 let stmt = self.prepare(query)?;
1115 let stmt_ptr = stmt.into_handle().into_sys();
1116 // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
1117 let stmt = unsafe { StatementConnection::new(stmt_ptr, self) };
1118 // `stmt` is valid and in prepared state.
1119 let prepared = Prepared::new(stmt);
1120 Ok(prepared)
1121 }
1122
1123 fn into_preallocated(self) -> Result<Preallocated<StatementConnection<Self>>, Error> {
1124 let stmt = self.preallocate()?;
1125 let stmt_ptr = stmt.into_handle().into_sys();
1126 // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
1127 let stmt = unsafe { StatementConnection::new(stmt_ptr, self) };
1128 // Safe: `stmt` is valid and its state is allocated.
1129 let preallocated = unsafe { Preallocated::new(stmt) };
1130 Ok(preallocated)
1131 }
1132}