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