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