odbc_api/
preallocated.rs

1use crate::{
2    execute::{
3        execute_columns, execute_foreign_keys, execute_tables, execute_with_parameters,
4        execute_with_parameters_polling,
5    },
6    handles::{AsStatementRef, SqlText, Statement, StatementRef},
7    CursorImpl, CursorPolling, Error, ParameterCollectionRef, Sleep,
8};
9
10/// A preallocated SQL statement handle intended for sequential execution of different queries. See
11/// [`crate::Connection::preallocate`].
12///
13/// # Example
14///
15/// ```
16/// use odbc_api::{Connection, Error};
17/// use std::io::{self, stdin, Read};
18///
19/// fn interactive(conn: &Connection<'_>) -> io::Result<()>{
20///     let mut statement = conn.preallocate().unwrap();
21///     let mut query = String::new();
22///     stdin().read_line(&mut query)?;
23///     while !query.is_empty() {
24///         match statement.execute(&query, ()) {
25///             Err(e) => println!("{}", e),
26///             Ok(None) => println!("No results set generated."),
27///             Ok(Some(cursor)) => {
28///                 // ...print cursor contents...
29///             },
30///         }
31///         stdin().read_line(&mut query)?;
32///     }
33///     Ok(())
34/// }
35/// ```
36pub struct Preallocated<S> {
37    /// A valid statement handle.
38    statement: S,
39}
40
41impl<S> Preallocated<S>
42where
43    S: AsStatementRef,
44{
45    /// Users which intend to write their application in safe Rust should prefer using
46    /// [`crate::Connection::preallocate`] as opposed to this constructor.
47    ///
48    /// # Safety
49    ///
50    /// `statement` must be an allocated handled with no pointers bound for either results or
51    /// arguments. The statement must not be prepared, but in the state of a "freshly" allocated
52    /// handle.
53    pub unsafe fn new(statement: S) -> Self {
54        Self { statement }
55    }
56
57    /// Executes a statement. This is the fastest way to sequentially execute different SQL
58    /// Statements.
59    ///
60    /// # Parameters
61    ///
62    /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
63    /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
64    ///   represent no parameters. Check the [`crate::parameter`] module level documentation for
65    ///   more information on how to pass parameters.
66    ///
67    /// # Return
68    ///
69    /// Returns `Some` if a cursor is created. If `None` is returned no cursor has been created (
70    /// e.g. the query came back empty). Note that an empty query may also create a cursor with zero
71    /// rows. Since we want to reuse the statement handle a returned cursor will not take ownership
72    /// of it and instead borrow it.
73    ///
74    /// # Example
75    ///
76    /// ```
77    /// use odbc_api::{Connection, Error};
78    /// use std::io::{self, stdin, Read};
79    ///
80    /// fn interactive(conn: &Connection) -> io::Result<()>{
81    ///     let mut statement = conn.preallocate().unwrap();
82    ///     let mut query = String::new();
83    ///     stdin().read_line(&mut query)?;
84    ///     while !query.is_empty() {
85    ///         match statement.execute(&query, ()) {
86    ///             Err(e) => println!("{}", e),
87    ///             Ok(None) => println!("No results set generated."),
88    ///             Ok(Some(cursor)) => {
89    ///                 // ...print cursor contents...
90    ///             },
91    ///         }
92    ///         stdin().read_line(&mut query)?;
93    ///     }
94    ///     Ok(())
95    /// }
96    /// ```
97    pub fn execute(
98        &mut self,
99        query: &str,
100        params: impl ParameterCollectionRef,
101    ) -> Result<Option<CursorImpl<StatementRef<'_>>>, Error> {
102        let stmt = self.statement.as_stmt_ref();
103        let query = SqlText::new(query);
104        execute_with_parameters(move || Ok(stmt), Some(&query), params)
105    }
106
107    /// Transfer ownership to the underlying statement handle.
108    ///
109    /// The resulting type is one level of indirection away from the raw pointer of the ODBC API. It
110    /// no longer has any guarantees about bound buffers, but is still guaranteed to be a valid
111    /// allocated statement handle. This serves together with
112    /// [`crate::handles::StatementImpl::into_sys`] or [`crate::handles::Statement::as_sys`] this
113    /// serves as an escape hatch to access the functionality provided by `crate::sys` not yet
114    /// accessible through safe abstractions.
115    pub fn into_statement(self) -> S {
116        self.statement
117    }
118
119    /// List tables, schemas, views and catalogs of a datasource.
120    ///
121    /// # Parameters
122    ///
123    /// * `catalog_name`: Filter result by catalog name. Accept search patterns. Use `%` to match
124    ///   any number of characters. Use `_` to match exactly on character. Use `\` to escape
125    ///   characeters.
126    /// * `schema_name`: Filter result by schema. Accepts patterns in the same way as
127    ///   `catalog_name`.
128    /// * `table_name`: Filter result by table. Accepts patterns in the same way as `catalog_name`.
129    /// * `table_type`: Filters results by table type. E.g: 'TABLE', 'VIEW'. This argument accepts a
130    ///   comma separeted list of table types. Omit it to not filter the result by table type at
131    ///   all.
132    pub fn tables(
133        &mut self,
134        catalog_name: &str,
135        schema_name: &str,
136        table_name: &str,
137        table_type: &str,
138    ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
139        let stmt = self.statement.as_stmt_ref();
140        execute_tables(
141            stmt,
142            &SqlText::new(catalog_name),
143            &SqlText::new(schema_name),
144            &SqlText::new(table_name),
145            &SqlText::new(table_type),
146        )
147    }
148
149    /// A cursor describing columns of all tables matching the patterns. Patterns support as
150    /// placeholder `%` for multiple characters or `_` for a single character. Use `\` to escape.The
151    /// returned cursor has the columns:
152    /// `TABLE_CAT`, `TABLE_SCHEM`, `TABLE_NAME`, `COLUMN_NAME`, `DATA_TYPE`, `TYPE_NAME`,
153    /// `COLUMN_SIZE`, `BUFFER_LENGTH`, `DECIMAL_DIGITS`, `NUM_PREC_RADIX`, `NULLABLE`,
154    /// `REMARKS`, `COLUMN_DEF`, `SQL_DATA_TYPE`, `SQL_DATETIME_SUB`, `CHAR_OCTET_LENGTH`,
155    /// `ORDINAL_POSITION`, `IS_NULLABLE`.
156    ///
157    /// In addition to that there may be a number of columns specific to the data source.
158    pub fn columns(
159        &mut self,
160        catalog_name: &str,
161        schema_name: &str,
162        table_name: &str,
163        column_name: &str,
164    ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
165        let stmt = self.statement.as_stmt_ref();
166        execute_columns(
167            stmt,
168            &SqlText::new(catalog_name),
169            &SqlText::new(schema_name),
170            &SqlText::new(table_name),
171            &SqlText::new(column_name),
172        )
173    }
174
175    /// This can be used to retrieve either a list of foreign keys in the specified table or a list
176    /// of foreign keys in other table that refer to the primary key of the specified table.
177    ///
178    /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function>
179    pub fn foreign_keys(
180        &mut self,
181        pk_catalog_name: &str,
182        pk_schema_name: &str,
183        pk_table_name: &str,
184        fk_catalog_name: &str,
185        fk_schema_name: &str,
186        fk_table_name: &str,
187    ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
188        let stmt = self.statement.as_stmt_ref();
189        execute_foreign_keys(
190            stmt,
191            &SqlText::new(pk_catalog_name),
192            &SqlText::new(pk_schema_name),
193            &SqlText::new(pk_table_name),
194            &SqlText::new(fk_catalog_name),
195            &SqlText::new(fk_schema_name),
196            &SqlText::new(fk_table_name),
197        )
198    }
199
200    /// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statment. May return
201    /// `None` if row count is not available. Some drivers may also allow to use this to determine
202    /// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
203    /// have been fetched after they have been fetched.
204    ///
205    /// ```
206    /// use odbc_api::{Connection, Error};
207    ///
208    /// /// Make everyone rich and return how many colleagues are happy now.
209    /// fn raise_minimum_salary(
210    ///     conn: &Connection<'_>,
211    ///     new_min_salary: i32
212    /// ) -> Result<usize, Error> {
213    ///     // We won't use conn.execute directly, because we need a handle to ask about the number
214    ///     // of changed rows. So let's allocate the statement explicitly.
215    ///     let mut stmt = conn.preallocate()?;
216    ///     stmt.execute(
217    ///         "UPDATE Employees SET salary = ? WHERE salary < ?",
218    ///         (&new_min_salary, &new_min_salary),
219    ///     )?;
220    ///     let number_of_updated_rows = stmt
221    ///         .row_count()?
222    ///         .expect("For UPDATE statements row count must always be available.");
223    ///     Ok(number_of_updated_rows)
224    /// }
225    /// ```
226    pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
227        let mut stmt = self.statement.as_stmt_ref();
228        stmt.row_count().into_result(&stmt).map(|count| {
229            // ODBC returns -1 in case a row count is not available
230            if count == -1 {
231                None
232            } else {
233                Some(count.try_into().unwrap())
234            }
235        })
236    }
237
238    /// Use this to limit the time the query is allowed to take, before responding with data to the
239    /// application. The driver may replace the number of seconds you provide with a minimum or
240    /// maximum value. You can specify ``0``, to deactivate the timeout, this is the default. For
241    /// this to work the driver must support this feature. E.g. PostgreSQL, and Microsoft SQL Server
242    /// do, but SQLite or MariaDB do not.
243    ///
244    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
245    ///
246    /// See:
247    /// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function
248    pub fn set_query_timeout_sec(&mut self, timeout_sec: usize) -> Result<(), Error> {
249        let mut stmt = self.statement.as_stmt_ref();
250        stmt.as_stmt_ref()
251            .set_query_timeout_sec(timeout_sec)
252            .into_result(&stmt)
253    }
254
255    /// The number of seconds to wait for a SQL statement to execute before returning to the
256    /// application. If `timeout_sec` is equal to 0 (default), there is no timeout.
257    ///
258    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
259    ///
260    /// See:
261    /// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function
262    pub fn query_timeout_sec(&mut self) -> Result<usize, Error> {
263        let mut stmt = self.statement.as_stmt_ref();
264        stmt.query_timeout_sec().into_result(&stmt)
265    }
266
267    /// Call this method to enable asynchronous polling mode on the statement
268    pub fn into_polling(mut self) -> Result<PreallocatedPolling<S>, Error> {
269        let mut stmt = self.statement.as_stmt_ref();
270        stmt.set_async_enable(true).into_result(&stmt)?;
271        Ok(PreallocatedPolling::new(self.statement))
272    }
273}
274
275impl<S> AsStatementRef for Preallocated<S>
276where
277    S: AsStatementRef,
278{
279    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
280        self.statement.as_stmt_ref()
281    }
282}
283
284/// Asynchronous sibling of [`Preallocated`] using polling mode for execution. Can be obtained using
285/// [`Preallocated::into_polling`].
286pub struct PreallocatedPolling<S> {
287    /// A valid statement handle in polling mode
288    statement: S,
289}
290
291impl<S> PreallocatedPolling<S> {
292    fn new(statement: S) -> Self {
293        Self { statement }
294    }
295}
296
297impl<S> PreallocatedPolling<S>
298where
299    S: AsStatementRef,
300{
301    /// Executes a statement. This is the fastest way to sequentially execute different SQL
302    /// Statements asynchronously.
303    ///
304    /// # Parameters
305    ///
306    /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
307    /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
308    ///   represent no parameters. Check the [`crate::parameter`] module level documentation for
309    ///   more information on how to pass parameters.
310    /// * `sleep`: Governs the polling intervals
311    ///
312    /// # Return
313    ///
314    /// Returns `Some` if a cursor is created. If `None` is returned no cursor has been created (
315    /// e.g. the query came back empty). Note that an empty query may also create a cursor with zero
316    /// rows. Since we want to reuse the statement handle a returned cursor will not take ownership
317    /// of it and instead burrow it.
318    ///
319    /// # Example
320    ///
321    /// ```
322    /// use odbc_api::{Connection, Error};
323    /// use std::{io::{self, stdin, Read}, time::Duration};
324    ///
325    /// /// Execute many different queries sequentially.
326    /// async fn execute_all(conn: &Connection<'_>, queries: &[&str]) -> Result<(), Error>{
327    ///     let mut statement = conn.preallocate()?.into_polling()?;
328    ///     let sleep = || tokio::time::sleep(Duration::from_millis(20));
329    ///     for query in queries {
330    ///         println!("Executing {query}");
331    ///         match statement.execute(&query, (), sleep).await {
332    ///             Err(e) => println!("{}", e),
333    ///             Ok(None) => println!("No results set generated."),
334    ///             Ok(Some(cursor)) => {
335    ///                 // ...print cursor contents...
336    ///             },
337    ///         }
338    ///     }
339    ///     Ok(())
340    /// }
341    /// ```
342    pub async fn execute(
343        &mut self,
344        query: &str,
345        params: impl ParameterCollectionRef,
346        sleep: impl Sleep,
347    ) -> Result<Option<CursorPolling<StatementRef<'_>>>, Error> {
348        let query = SqlText::new(query);
349        let stmt = self.statement.as_stmt_ref();
350        execute_with_parameters_polling(move || Ok(stmt), Some(&query), params, sleep).await
351    }
352}
353
354impl<S> AsStatementRef for PreallocatedPolling<S>
355where
356    S: AsStatementRef,
357{
358    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
359        self.statement.as_stmt_ref()
360    }
361}