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}