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}