odbc_api/connection.rs
1use crate::{
2 BlockCursorIterator, ColumnsRow, CursorImpl, CursorPolling, Error, ForeignKeysRow,
3 ParameterCollectionRef, Preallocated, Prepared, PrimaryKeysRow, Sleep, TablesRow,
4 buffers::BufferDesc,
5 execute::execute_with_parameters_polling,
6 handles::{
7 self, SqlText, State, Statement, StatementConnection, StatementImpl, StatementParent,
8 slice_to_utf8,
9 },
10};
11use log::error;
12use std::{
13 borrow::Cow,
14 fmt::{self, Debug, Display},
15 mem::{ManuallyDrop, MaybeUninit},
16 ptr, str,
17 sync::Arc,
18 thread::panicking,
19};
20
21impl Drop for Connection<'_> {
22 fn drop(&mut self) {
23 match self.connection.disconnect().into_result(&self.connection) {
24 Ok(()) => (),
25 Err(Error::Diagnostics {
26 record,
27 function: _,
28 }) if record.state == State::INVALID_STATE_TRANSACTION => {
29 // Invalid transaction state. Let's rollback the current transaction and try again.
30 if let Err(e) = self.rollback() {
31 // Connection might be in a suspended state. See documentation about suspended
32 // state here:
33 // <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlendtran-function>
34 //
35 // See also issue:
36 // <https://github.com/pacman82/odbc-api/issues/574#issuecomment-2286449125>
37
38 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 /// An iterator over the columns of tables matching the patterns. Patterns support `%` for
546 /// multiple characters or `_` for a single character. Use `\` to escape.
547 ///
548 /// Returns an iterator over [`ColumnsRow`] items. If you need the raw cursor (e.g. to access
549 /// driver-specific columns beyond the standard 18), use [`Preallocated::columns_cursor`]
550 /// instead.
551 pub fn columns(
552 &self,
553 catalog_name: &str,
554 schema_name: &str,
555 table_name: &str,
556 column_name: &str,
557 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, ColumnsRow>, Error> {
558 let stmt = self.preallocate()?;
559 stmt.into_columns(catalog_name, schema_name, table_name, column_name)
560 }
561
562 /// List tables, schemas, views and catalogs of a datasource.
563 ///
564 /// # Parameters
565 ///
566 /// * `catalog_name`: Filter result by catalog name. Accept search patterns. Use `%` to match
567 /// any number of characters. Use `_` to match exactly on character. Use `\` to escape
568 /// characeters.
569 /// * `schema_name`: Filter result by schema. Accepts patterns in the same way as
570 /// `catalog_name`.
571 /// * `table_name`: Filter result by table. Accepts patterns in the same way as `catalog_name`.
572 /// * `table_type`: Filters results by table type. E.g: 'TABLE', 'VIEW'. This argument accepts a
573 /// comma separeted list of table types. Omit it to not filter the result by table type at
574 /// all.
575 ///
576 /// # Example
577 ///
578 /// ```
579 /// use odbc_api::{Connection, Error, TablesRow};
580 ///
581 /// fn print_all_tables(conn: &Connection<'_>) -> Result<(), Error> {
582 /// for row in conn.tables("", "", "", "")? {
583 /// let row: TablesRow = row?;
584 /// let table = row.table.as_str().unwrap().unwrap_or("NULL");
585 /// let catalog = row.catalog.as_str().unwrap().unwrap_or("NULL");
586 /// let schema = row.schema.as_str().unwrap().unwrap_or("NULL");
587 /// println!("{catalog}.{schema}.{table}");
588 /// }
589 /// Ok(())
590 /// }
591 /// ```
592 pub fn tables(
593 &self,
594 catalog_name: &str,
595 schema_name: &str,
596 table_name: &str,
597 table_type: &str,
598 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, TablesRow>, Error> {
599 let statement = self.preallocate()?;
600 statement.into_tables(catalog_name, schema_name, table_name, table_type)
601 }
602
603 /// Create a result set which contains the column names that make up the primary key for the
604 /// table.
605 ///
606 /// # Parameters
607 ///
608 /// * `catalog_name`: Catalog name. If a driver supports catalogs for some tables but not for
609 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
610 /// denotes those tables that do not have catalogs. `catalog_name` must not contain a string
611 /// search pattern.
612 /// * `schema_name`: Schema name. If a driver supports schemas for some tables but not for
613 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
614 /// denotes those tables that do not have schemas. `schema_name` must not contain a string
615 /// search pattern.
616 /// * `table_name`: Table name. `table_name` must not contain a string search pattern.
617 ///
618 /// The resulting result set contains the following columns:
619 ///
620 /// * `TABLE_CAT`: Primary key table catalog name. NULL if not applicable to the data source. If
621 /// a driver supports catalogs for some tables but not for others, such as when the driver
622 /// retrieves data from different DBMSs, it returns an empty string ("") for those tables that
623 /// do not have catalogs. `VARCHAR`
624 /// * `TABLE_SCHEM`: Primary key table schema name; NULL if not applicable to the data source.
625 /// If a driver supports schemas for some tables but not for others, such as when the driver
626 /// retrieves data from different DBMSs, it returns an empty string ("") for those tables that
627 /// do not have schemas. `VARCHAR`
628 /// * `TABLE_NAME`: Primary key table name. `VARCHAR NOT NULL`
629 /// * `COLUMN_NAME`: Primary key column name. The driver returns an empty string for a column
630 /// that does not have a name. `VARCHAR NOT NULL`
631 /// * `KEY_SEQ`: Column sequence number in key (starting with 1). `SMALLINT NOT NULL`
632 /// * `PK_NAME`: Primary key name. NULL if not applicable to the data source. `VARCHAR`
633 ///
634 /// The maximum length of the VARCHAR columns is driver specific.
635 ///
636 /// If [`crate::sys::StatementAttribute::MetadataId`] statement attribute is set to true,
637 /// catalog, schema and table name parameters are treated as an identifiers and their case is
638 /// not significant. If it is false, they are ordinary arguments. As such they treated literally
639 /// and their case is significant.
640 ///
641 /// See: <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlprimarykeys-function>
642 pub fn primary_keys(
643 &self,
644 catalog_name: Option<&str>,
645 schema_name: Option<&str>,
646 table_name: &str,
647 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, PrimaryKeysRow>, Error> {
648 let stmt = self.preallocate()?;
649 stmt.into_primary_keys(catalog_name, schema_name, table_name)
650 }
651
652 /// This can be used to retrieve either a list of foreign keys in the specified table or a list
653 /// of foreign keys in other table that refer to the primary key of the specified table.
654 ///
655 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function>
656 pub fn foreign_keys(
657 &self,
658 pk_catalog_name: &str,
659 pk_schema_name: &str,
660 pk_table_name: &str,
661 fk_catalog_name: &str,
662 fk_schema_name: &str,
663 fk_table_name: &str,
664 ) -> Result<BlockCursorIterator<CursorImpl<StatementImpl<'_>>, ForeignKeysRow>, Error> {
665 let statement = self.preallocate()?;
666 statement.into_foreign_keys(
667 pk_catalog_name,
668 pk_schema_name,
669 pk_table_name,
670 fk_catalog_name,
671 fk_schema_name,
672 fk_table_name,
673 )
674 }
675
676 /// The buffer descriptions for all standard buffers (not including extensions) returned in the
677 /// columns query (e.g. [`Preallocated::columns_cursor`]).
678 ///
679 /// # Arguments
680 ///
681 /// * `type_name_max_len` - The maximum expected length of type names.
682 /// * `remarks_max_len` - The maximum expected length of remarks.
683 /// * `column_default_max_len` - The maximum expected length of column defaults.
684 #[deprecated(
685 note = "Use `Connection::columns` or `Preallocated::columns` which return strongly typed \
686 `ColumnsRow` items instead."
687 )]
688 pub fn columns_buffer_descs(
689 &self,
690 type_name_max_len: usize,
691 remarks_max_len: usize,
692 column_default_max_len: usize,
693 ) -> Result<Vec<BufferDesc>, Error> {
694 let null_i16 = BufferDesc::I16 { nullable: true };
695
696 let not_null_i16 = BufferDesc::I16 { nullable: false };
697
698 let null_i32 = BufferDesc::I32 { nullable: true };
699
700 // The definitions for these descriptions are taken from the documentation of `SQLColumns`
701 // located at https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcolumns-function
702 let catalog_name_desc = BufferDesc::Text {
703 max_str_len: self.max_catalog_name_len()? as usize,
704 };
705
706 let schema_name_desc = BufferDesc::Text {
707 max_str_len: self.max_schema_name_len()? as usize,
708 };
709
710 let table_name_desc = BufferDesc::Text {
711 max_str_len: self.max_table_name_len()? as usize,
712 };
713
714 let column_name_desc = BufferDesc::Text {
715 max_str_len: self.max_column_name_len()? as usize,
716 };
717
718 let data_type_desc = not_null_i16;
719
720 let type_name_desc = BufferDesc::Text {
721 max_str_len: type_name_max_len,
722 };
723
724 let column_size_desc = null_i32;
725 let buffer_len_desc = null_i32;
726 let decimal_digits_desc = null_i16;
727 let precision_radix_desc = null_i16;
728 let nullable_desc = not_null_i16;
729
730 let remarks_desc = BufferDesc::Text {
731 max_str_len: remarks_max_len,
732 };
733
734 let column_default_desc = BufferDesc::Text {
735 max_str_len: column_default_max_len,
736 };
737
738 let sql_data_type_desc = not_null_i16;
739 let sql_datetime_sub_desc = null_i16;
740 let char_octet_len_desc = null_i32;
741 let ordinal_pos_desc = BufferDesc::I32 { nullable: false };
742
743 // We expect strings to be `YES`, `NO`, or a zero-length string, so `3` should be
744 // sufficient.
745 const IS_NULLABLE_LEN_MAX_LEN: usize = 3;
746 let is_nullable_desc = BufferDesc::Text {
747 max_str_len: IS_NULLABLE_LEN_MAX_LEN,
748 };
749
750 Ok(vec![
751 catalog_name_desc,
752 schema_name_desc,
753 table_name_desc,
754 column_name_desc,
755 data_type_desc,
756 type_name_desc,
757 column_size_desc,
758 buffer_len_desc,
759 decimal_digits_desc,
760 precision_radix_desc,
761 nullable_desc,
762 remarks_desc,
763 column_default_desc,
764 sql_data_type_desc,
765 sql_datetime_sub_desc,
766 char_octet_len_desc,
767 ordinal_pos_desc,
768 is_nullable_desc,
769 ])
770 }
771
772 fn allocate_statement(&self) -> Result<StatementImpl<'_>, Error> {
773 self.connection
774 .allocate_statement()
775 .into_result(&self.connection)
776 }
777}
778
779/// Implement `Debug` for [`Connection`], in order to play nice with derive Debugs for struct
780/// holding a [`Connection`].
781impl Debug for Connection<'_> {
782 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
783 write!(f, "Connection")
784 }
785}
786
787/// We need to implement [`StatementParent`] for [`Connection`] in order to express ownership of a
788/// connection for a statement handle. This is e.g. needed for [`Connection::into_cursor`].
789///
790/// # Safety:
791///
792/// Connection wraps an open Connection. It keeps the handle alive and valid during its lifetime.
793unsafe impl StatementParent for Connection<'_> {}
794
795/// We need to implement [`StatementParent`] for `Arc<Connection>` in order to be able to express
796/// ownership of a shared connection from a statement handle. This is e.g. needed for
797/// [`ConnectionTransitions::into_cursor`].
798///
799/// # Safety:
800///
801/// `Arc<Connection>` wraps an open Connection. It keeps the handle alive and valid during its
802/// lifetime.
803unsafe impl StatementParent for Arc<Connection<'_>> {}
804
805/// Options to be passed then opening a connection to a datasource.
806#[derive(Default, Clone, Copy)]
807pub struct ConnectionOptions {
808 /// Number of seconds to wait for a login request to complete before returning to the
809 /// application. The default is driver-dependent. If `0` the timeout is disabled and a
810 /// connection attempt will wait indefinitely.
811 ///
812 /// If the specified timeout exceeds the maximum login timeout in the data source, the driver
813 /// substitutes that value and uses the maximum login timeout instead.
814 ///
815 /// This corresponds to the `SQL_ATTR_LOGIN_TIMEOUT` attribute in the ODBC specification.
816 ///
817 /// See:
818 /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetconnectattr-function>
819 pub login_timeout_sec: Option<u32>,
820 /// Packet size in bytes. Not all drivers support this option.
821 pub packet_size: Option<u32>,
822}
823
824impl ConnectionOptions {
825 /// Set the attributes corresponding to the connection options to an allocated connection
826 /// handle. Usually you would rather provide the options then creating the connection with e.g.
827 /// [`crate::Environment::connect_with_connection_string`] rather than calling this method
828 /// yourself.
829 pub fn apply(&self, handle: &handles::Connection) -> Result<(), Error> {
830 if let Some(timeout) = self.login_timeout_sec {
831 handle.set_login_timeout_sec(timeout).into_result(handle)?;
832 }
833 if let Some(packet_size) = self.packet_size {
834 handle.set_packet_size(packet_size).into_result(handle)?;
835 }
836 Ok(())
837 }
838}
839
840/// You can use this method to escape a password so it is suitable to be appended to an ODBC
841/// connection string as the value for the `PWD` attribute. This method is only of interest for
842/// application in need to create their own connection strings.
843///
844/// See:
845///
846/// * <https://stackoverflow.com/questions/22398212/escape-semicolon-in-odbc-connection-string-in-app-config-file>
847/// * <https://docs.microsoft.com/en-us/dotnet/api/system.data.odbc.odbcconnection.connectionstring>
848///
849/// # Example
850///
851/// ```
852/// use odbc_api::escape_attribute_value;
853///
854/// let password = "abc;123}";
855/// let user = "SA";
856/// let mut connection_string_without_credentials =
857/// "Driver={ODBC Driver 18 for SQL Server};Server=localhost;";
858///
859/// let connection_string = format!(
860/// "{}UID={};PWD={};",
861/// connection_string_without_credentials,
862/// user,
863/// escape_attribute_value(password)
864/// );
865///
866/// assert_eq!(
867/// "Driver={ODBC Driver 18 for SQL Server};Server=localhost;UID=SA;PWD={abc;123}}};",
868/// connection_string
869/// );
870/// ```
871///
872/// ```
873/// use odbc_api::escape_attribute_value;
874/// assert_eq!("abc", escape_attribute_value("abc"));
875/// assert_eq!("ab}c", escape_attribute_value("ab}c"));
876/// assert_eq!("{ab;c}", escape_attribute_value("ab;c"));
877/// assert_eq!("{a}}b;c}", escape_attribute_value("a}b;c"));
878/// assert_eq!("{ab+c}", escape_attribute_value("ab+c"));
879/// ```
880pub fn escape_attribute_value(unescaped: &str) -> Cow<'_, str> {
881 // Search the string for semicolon (';') if we do not find any, nothing is to do and we can work
882 // without an extra allocation.
883 //
884 // * We escape ';' because it serves as a separator between key=value pairs
885 // * We escape '+' because passwords with `+` must be escaped on PostgreSQL for some reason.
886 if unescaped.contains(&[';', '+'][..]) {
887 // Surround the string with curly braces ('{','}') and escape every closing curly brace by
888 // repeating it.
889 let escaped = unescaped.replace('}', "}}");
890 Cow::Owned(format!("{{{escaped}}}"))
891 } else {
892 Cow::Borrowed(unescaped)
893 }
894}
895
896/// A pair of the error and the previous state, before the operation caused the error.
897///
898/// Some functions in this crate take a `self` and return another type in the result to express a
899/// state transitions in the underlying ODBC handle. In order to make such operations retryable, or
900/// offer other alternatives of recovery, they may return this error type instead of a plain
901/// [`Error`].
902#[derive(Debug)]
903pub struct FailedStateTransition<S> {
904 /// The ODBC error which caused the state transition to fail.
905 pub error: Error,
906 /// The state before the transition failed. This is useful to e.g. retry the operation, or
907 /// recover in another way.
908 pub previous: S,
909}
910
911impl<S> From<FailedStateTransition<S>> for Error {
912 fn from(value: FailedStateTransition<S>) -> Self {
913 value.error
914 }
915}
916
917impl<S> Display for FailedStateTransition<S> {
918 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
919 write!(f, "{}", self.error)
920 }
921}
922
923impl<S> std::error::Error for FailedStateTransition<S>
924where
925 S: Debug,
926{
927 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
928 self.error.source()
929 }
930}
931
932/// An error type wrapping an [`Error`] and a [`Connection`]. It is used by
933/// [`Connection::into_cursor`], so that in case of failure the user can reuse the connection to try
934/// again. [`Connection::into_cursor`] could achieve the same by returning a tuple in case of an
935/// error, but this type causes less friction in most scenarios because [`Error`] implements
936/// [`From`] [`ConnectionAndError`] and it therfore works with the question mark operater (`?`).
937type ConnectionAndError<'conn> = FailedStateTransition<Connection<'conn>>;
938
939/// Ability to transition ownership of the connection to various children which represent statement
940/// handles in various states. E.g. [`crate::Prepared`] or [`crate::Cursor`]. Transfering ownership
941/// of the connection could e.g. be useful if you want to clean the connection after you are done
942/// with the child.
943///
944/// Having this in a trait rather than directly on [`Connection`] allows us to be generic over the
945/// type of ownership we express. E.g. we can express shared ownership of a connection by
946/// using an `Arc<Mutex<Connection>>` or `Arc<Connection>`. Or a still exclusive ownership using
947/// a plain [`Connection`].
948pub trait ConnectionTransitions: Sized {
949 // Note to self. This might eveolve into a `Connection` trait. Which expresses ownership
950 // of a connection (shared or not). It could allow to get a dereferened borrowed conection
951 // which does not allow for state transtions as of now (like StatementRef). I may not want to
952 // rock the boat that much right now.
953
954 /// The type passed to [crate::handles::StatementConnection] to express ownership of the
955 /// connection.
956 type StatementParent: StatementParent;
957
958 /// Similar to [`crate::Connection::into_cursor`], yet it operates on an
959 /// `Arc<Mutex<Connection>>`. `Arc<Connection>` can be used if you want shared ownership of
960 /// connections. However, `Arc<Connection>` is not `Send` due to `Connection` not being `Sync`.
961 /// So sometimes you may want to wrap your `Connection` into an `Arc<Mutex<Connection>>` to
962 /// allow shared ownership of the connection across threads. This function allows you to create
963 /// a cursor from such a shared which also holds a strong reference to it.
964 ///
965 /// # Parameters
966 ///
967 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
968 /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
969 /// represent no parameters. See the [`crate::parameter`] module level documentation for more
970 /// information on how to pass parameters.
971 /// * `query_timeout_sec`: Use this to limit the time the query is allowed to take, before
972 /// responding with data to the application. The driver may replace the number of seconds you
973 /// provide with a minimum or maximum value.
974 ///
975 /// For the timeout to work the driver must support this feature. E.g. PostgreSQL, and
976 /// Microsoft SQL Server do, but SQLite or MariaDB do not.
977 ///
978 /// You can specify ``0``, to deactivate the timeout, this is the default. So if you want no
979 /// timeout, just leave it at `None`. Only reason to specify ``0`` is if for some reason your
980 /// datasource does not have ``0`` as default.
981 ///
982 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
983 ///
984 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
985 fn into_cursor(
986 self,
987 query: &str,
988 params: impl ParameterCollectionRef,
989 query_timeout_sec: Option<usize>,
990 ) -> Result<
991 Option<CursorImpl<StatementConnection<Self::StatementParent>>>,
992 FailedStateTransition<Self>,
993 >;
994
995 /// Prepares an SQL statement which takes ownership of the connection. The advantage over
996 /// [`Connection::prepare`] is, that you do not need to keep track of the lifetime of the
997 /// connection seperatly and can create types which do own the prepared query and only depend on
998 /// the lifetime of the environment.
999 ///
1000 /// # Parameters
1001 ///
1002 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;". `?`
1003 /// may be used as a placeholder in the statement text, to be replaced with parameters during
1004 /// execution.
1005 ///
1006 /// ```no_run
1007 /// use odbc_api::{
1008 /// environment, Error, ColumnarBulkInserter, ConnectionTransitions, Connection,
1009 /// handles::StatementConnection, buffers::AnyBuffer, ConnectionOptions, BindParamDesc,
1010 /// parameter::WithDataType,
1011 ///
1012 /// };
1013 ///
1014 /// const CONNECTION_STRING: &str =
1015 /// "Driver={ODBC Driver 18 for SQL Server};\
1016 /// Server=localhost;UID=SA;\
1017 /// PWD=My@Test@Password1;";
1018 ///
1019 /// /// Supports columnar bulk inserts on a heterogenous schema (columns have different types),
1020 /// /// takes ownership of a connection created using an environment with static lifetime.
1021 /// type Inserter = ColumnarBulkInserter<
1022 /// StatementConnection<Connection<'static>>,
1023 /// WithDataType<AnyBuffer>
1024 /// >;
1025 ///
1026 /// /// Creates an inserter which can be reused to bulk insert birthyears with static lifetime.
1027 /// fn make_inserter(query: &str) -> Result<Inserter, Error> {
1028 /// let env = environment()?;
1029 /// let conn = env.connect_with_connection_string(
1030 /// CONNECTION_STRING,
1031 /// ConnectionOptions::default()
1032 /// )?;
1033 /// let prepared = conn.into_prepared("INSERT INTO Birthyear (name, year) VALUES (?, ?)")?;
1034 /// let buffers = [
1035 /// BindParamDesc::text(255),
1036 /// BindParamDesc::i16(false),
1037 /// ];
1038 /// let capacity = 400;
1039 /// prepared.into_column_inserter(capacity, buffers)
1040 /// }
1041 /// ```
1042 fn into_prepared(
1043 self,
1044 query: &str,
1045 ) -> Result<Prepared<StatementConnection<Self::StatementParent>>, Error>;
1046
1047 /// Creates a preallocated statement handle like [`Connection::preallocate`]. Yet the statement
1048 /// also takes ownership of the connection.
1049 fn into_preallocated(
1050 self,
1051 ) -> Result<Preallocated<StatementConnection<Self::StatementParent>>, Error>;
1052}
1053
1054impl<'env> ConnectionTransitions for Connection<'env> {
1055 type StatementParent = Self;
1056
1057 fn into_cursor(
1058 self,
1059 query: &str,
1060 params: impl ParameterCollectionRef,
1061 query_timeout_sec: Option<usize>,
1062 ) -> Result<Option<CursorImpl<StatementConnection<Self>>>, FailedStateTransition<Self>> {
1063 self.into_cursor(query, params, query_timeout_sec)
1064 }
1065
1066 fn into_prepared(self, query: &str) -> Result<Prepared<StatementConnection<Self>>, Error> {
1067 self.into_prepared(query)
1068 }
1069
1070 fn into_preallocated(self) -> Result<Preallocated<StatementConnection<Self>>, Error> {
1071 self.into_preallocated()
1072 }
1073}
1074
1075impl<'env> ConnectionTransitions for Arc<Connection<'env>> {
1076 type StatementParent = Self;
1077
1078 fn into_cursor(
1079 self,
1080 query: &str,
1081 params: impl ParameterCollectionRef,
1082 query_timeout_sec: Option<usize>,
1083 ) -> Result<Option<CursorImpl<StatementConnection<Self>>>, FailedStateTransition<Self>> {
1084 // Result borrows the connection. We convert the cursor into a raw pointer, to not confuse
1085 // the borrow checker.
1086 let result = self.execute(query, params, query_timeout_sec);
1087 let maybe_stmt_ptr = result
1088 .map(|opt| opt.map(|cursor| cursor.into_stmt().into_sys()))
1089 .map_err(|error| {
1090 // If the execute fails, we return a FailedStateTransition with the error and the
1091 // connection.
1092 FailedStateTransition {
1093 error,
1094 previous: Arc::clone(&self),
1095 }
1096 })?;
1097 let Some(stmt_ptr) = maybe_stmt_ptr else {
1098 return Ok(None);
1099 };
1100 // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
1101 let stmt = unsafe { StatementConnection::new(stmt_ptr, self) };
1102 // Safe: `stmt` is valid and in cursor state.
1103 let cursor = unsafe { CursorImpl::new(stmt) };
1104 Ok(Some(cursor))
1105 }
1106
1107 fn into_prepared(self, query: &str) -> Result<Prepared<StatementConnection<Self>>, Error> {
1108 let stmt = self.prepare(query)?;
1109 let stmt_ptr = stmt.into_handle().into_sys();
1110 // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
1111 let stmt = unsafe { StatementConnection::new(stmt_ptr, self) };
1112 // `stmt` is valid and in prepared state.
1113 let prepared = Prepared::new(stmt);
1114 Ok(prepared)
1115 }
1116
1117 fn into_preallocated(self) -> Result<Preallocated<StatementConnection<Self>>, Error> {
1118 let stmt = self.preallocate()?;
1119 let stmt_ptr = stmt.into_handle().into_sys();
1120 // Safe: The connection is the parent of the statement referenced by `stmt_ptr`.
1121 let stmt = unsafe { StatementConnection::new(stmt_ptr, self) };
1122 // Safe: `stmt` is valid and its state is allocated.
1123 let preallocated = unsafe { Preallocated::new(stmt) };
1124 Ok(preallocated)
1125 }
1126}