Skip to main content

odbc_api/
connection.rs

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