odbc_api/
connection.rs

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