odbc_api/
prepared.rs

1use crate::{
2    buffers::{AnyBuffer, BufferDesc, ColumnBuffer, TextColumn},
3    execute::execute_with_parameters,
4    handles::{AsStatementRef, HasDataType, ParameterDescription, Statement, StatementRef},
5    ColumnarBulkInserter, CursorImpl, Error, ParameterCollectionRef, ResultSetMetadata,
6};
7
8/// A prepared query. Prepared queries are useful if the similar queries should executed more than
9/// once. See [`crate::Connection::prepare`].
10pub struct Prepared<S> {
11    statement: S,
12}
13
14impl<S> Prepared<S> {
15    pub(crate) fn new(statement: S) -> Self {
16        Self { statement }
17    }
18
19    /// Transfer ownership to the underlying statement handle.
20    ///
21    /// The resulting type is one level of indirection away from the raw pointer of the ODBC API. It
22    /// no longer has any guarantees about bound buffers, but is still guaranteed to be a valid
23    /// allocated statement handle. This serves together with
24    /// [`crate::handles::StatementImpl::into_sys`] or [`crate::handles::Statement::as_sys`] this
25    /// serves as an escape hatch to access the functionality provided by `crate::sys` not yet
26    /// accessible through safe abstractions.
27    pub fn into_statement(self) -> S {
28        self.statement
29    }
30}
31
32impl<S> Prepared<S>
33where
34    S: AsStatementRef,
35{
36    /// Execute the prepared statement.
37    ///
38    /// * `params`: Used to bind these parameters before executing the statement. You can use `()`
39    ///   to represent no parameters. In regards to binding arrays of parameters: Should `params`
40    ///   specify a parameter set size of `0`, nothing is executed, and `Ok(None)` is returned. See
41    ///   the [`crate::parameter`] module level documentation for more information on how to pass
42    ///   parameters.
43    pub fn execute(
44        &mut self,
45        params: impl ParameterCollectionRef,
46    ) -> Result<Option<CursorImpl<StatementRef<'_>>>, Error> {
47        let stmt = self.statement.as_stmt_ref();
48        execute_with_parameters(move || Ok(stmt), None, params)
49    }
50
51    /// Describes parameter marker associated with a prepared SQL statement.
52    ///
53    /// # Parameters
54    ///
55    /// * `parameter_number`: Parameter marker number ordered sequentially in increasing parameter
56    ///   order, starting at 1.
57    pub fn describe_param(&mut self, parameter_number: u16) -> Result<ParameterDescription, Error> {
58        let stmt = self.as_stmt_ref();
59
60        stmt.describe_param(parameter_number).into_result(&stmt)
61    }
62
63    /// Number of placeholders which must be provided with [`Self::execute`] in order to execute
64    /// this statement. This is equivalent to the number of placeholders used in the SQL string
65    /// used to prepare the statement.
66    pub fn num_params(&mut self) -> Result<u16, Error> {
67        let stmt = self.as_stmt_ref();
68        stmt.num_params().into_result(&stmt)
69    }
70
71    /// Number of placeholders which must be provided with [`Self::execute`] in order to execute
72    /// this statement. This is equivalent to the number of placeholders used in the SQL string
73    /// used to prepare the statement.
74    ///
75    /// ```
76    /// use odbc_api::{Connection, Error, handles::ParameterDescription};
77    ///
78    /// fn collect_parameter_descriptions(
79    ///     connection: Connection<'_>
80    /// ) -> Result<Vec<ParameterDescription>, Error>{
81    ///     // Note the two `?` used as placeholders for the parameters.
82    ///     let sql = "INSERT INTO NationalDrink (country, drink) VALUES (?, ?)";
83    ///     let mut prepared = connection.prepare(sql)?;
84    ///
85    ///     let params: Vec<_> = prepared.parameter_descriptions()?.collect::<Result<_,_>>()?;
86    ///
87    ///     Ok(params)
88    /// }
89    /// ```
90    pub fn parameter_descriptions(
91        &mut self,
92    ) -> Result<
93        impl DoubleEndedIterator<Item = Result<ParameterDescription, Error>>
94            + ExactSizeIterator<Item = Result<ParameterDescription, Error>>
95            + '_,
96        Error,
97    > {
98        Ok((1..=self.num_params()?).map(|index| self.describe_param(index)))
99    }
100
101    /// Unless you want to roll your own column buffer implementation users are encouraged to use
102    /// [`Self::into_text_inserter`] instead.
103    ///
104    /// # Safety
105    ///
106    /// * Parameters must all be valid for insertion. An example for an invalid parameter would be
107    ///   a text buffer with a cell those indiactor value exceeds the maximum element length. This
108    ///   can happen after when truncation occurs then writing into a buffer.
109    pub unsafe fn unchecked_bind_columnar_array_parameters<C>(
110        self,
111        parameter_buffers: Vec<C>,
112    ) -> Result<ColumnarBulkInserter<S, C>, Error>
113    where
114        C: ColumnBuffer + HasDataType,
115    {
116        // We know that statement is a prepared statement.
117        ColumnarBulkInserter::new(self.into_statement(), parameter_buffers)
118    }
119
120    /// Use this to insert rows of string input into the database.
121    ///
122    /// ```
123    /// use odbc_api::{Connection, Error};
124    ///
125    /// fn insert_text<'e>(connection: Connection<'e>) -> Result<(), Error>{
126    ///     // Insert six rows of text with two columns each into the database in batches of 3. In a
127    ///     // real use case you are likely to achieve a better results with a higher batch size.
128    ///
129    ///     // Note the two `?` used as placeholders for the parameters.
130    ///     let prepared = connection.prepare("INSERT INTO NationalDrink (country, drink) VALUES (?, ?)")?;
131    ///     // We assume both parameter inputs never exceed 50 bytes.
132    ///     let mut prebound = prepared.into_text_inserter(3, [50, 50])?;
133    ///     
134    ///     // A cell is an option to byte. We could use `None` to represent NULL but we have no
135    ///     // need to do that in this example.
136    ///     let as_cell = |s: &'static str| { Some(s.as_bytes()) } ;
137    ///
138    ///     // First batch of values
139    ///     prebound.append(["England", "Tea"].into_iter().map(as_cell))?;
140    ///     prebound.append(["Germany", "Beer"].into_iter().map(as_cell))?;
141    ///     prebound.append(["Russia", "Vodka"].into_iter().map(as_cell))?;
142    ///
143    ///     // Execute statement using values bound in buffer.
144    ///     prebound.execute()?;
145    ///     // Clear buffer contents, otherwise the previous values would stay in the buffer.
146    ///     prebound.clear();
147    ///
148    ///     // Second batch of values
149    ///     prebound.append(["India", "Tea"].into_iter().map(as_cell))?;
150    ///     prebound.append(["France", "Wine"].into_iter().map(as_cell))?;
151    ///     prebound.append(["USA", "Cola"].into_iter().map(as_cell))?;
152    ///
153    ///     // Send second batch to the database
154    ///     prebound.execute()?;
155    ///
156    ///     Ok(())
157    /// }
158    /// ```
159    pub fn into_text_inserter(
160        self,
161        capacity: usize,
162        max_str_len: impl IntoIterator<Item = usize>,
163    ) -> Result<ColumnarBulkInserter<S, TextColumn<u8>>, Error> {
164        let max_str_len = max_str_len.into_iter();
165        let parameter_buffers = max_str_len
166            .map(|max_str_len| TextColumn::new(capacity, max_str_len))
167            .collect();
168        // Text Columns are created with NULL as default, which is valid for insertion.
169        unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers) }
170    }
171
172    /// A [`crate::ColumnarBulkInserter`] which takes ownership of both the statement and the bound
173    /// array parameter buffers.
174    ///
175    /// ```no_run
176    /// use odbc_api::{Connection, Error, IntoParameter, buffers::BufferDesc};
177    ///
178    /// fn insert_birth_years(
179    ///     conn: &Connection,
180    ///     names: &[&str],
181    ///     years: &[i16]
182    /// ) -> Result<(), Error> {
183    ///     // All columns must have equal length.
184    ///     assert_eq!(names.len(), years.len());
185    ///
186    ///     let prepared = conn.prepare("INSERT INTO Birthdays (name, year) VALUES (?, ?)")?;
187    ///
188    ///     // Create a columnar buffer which fits the input parameters.
189    ///     let buffer_description = [
190    ///         BufferDesc::Text { max_str_len: 255 },
191    ///         BufferDesc::I16 { nullable: false },
192    ///     ];
193    ///     // The capacity must be able to hold at least the largest batch. We do everything in one
194    ///     // go, so we set it to the length of the input parameters.
195    ///     let capacity = names.len();
196    ///     // Allocate memory for the array column parameters and bind it to the statement.
197    ///     let mut prebound = prepared.into_column_inserter(capacity, buffer_description)?;
198    ///     // Length of this batch
199    ///     prebound.set_num_rows(capacity);
200    ///
201    ///
202    ///     // Fill the buffer with values column by column
203    ///     let mut col = prebound
204    ///         .column_mut(0)
205    ///         .as_text_view()
206    ///         .expect("We know the name column to hold text.");
207    ///
208    ///     for (index, name) in names.iter().enumerate() {
209    ///         col.set_cell(index, Some(name.as_bytes()));
210    ///     }
211    ///
212    ///     let col = prebound
213    ///         .column_mut(1)
214    ///         .as_slice::<i16>()
215    ///         .expect("We know the year column to hold i16.");
216    ///     col.copy_from_slice(years);
217    ///
218    ///     prebound.execute()?;
219    ///     Ok(())
220    /// }
221    /// ```
222    pub fn into_column_inserter(
223        self,
224        capacity: usize,
225        descriptions: impl IntoIterator<Item = BufferDesc>,
226    ) -> Result<ColumnarBulkInserter<S, AnyBuffer>, Error> {
227        let parameter_buffers = descriptions
228            .into_iter()
229            .map(|desc| AnyBuffer::from_desc(capacity, desc))
230            .collect();
231        unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers) }
232    }
233
234    /// A [`crate::ColumnarBulkInserter`] which has ownership of the bound array parameter buffers
235    /// and borrows the statement. For most usecases [`Self::into_column_inserter`] is what you
236    /// want to use, yet on some instances you may want to bind new paramater buffers to the same
237    /// prepared statement. E.g. to grow the capacity dynamically during insertions with several
238    /// chunks. In such use cases you may only want to borrow the prepared statemnt, so it can be
239    /// reused with a different set of parameter buffers.
240    pub fn column_inserter(
241        &mut self,
242        capacity: usize,
243        descriptions: impl IntoIterator<Item = BufferDesc>,
244    ) -> Result<ColumnarBulkInserter<StatementRef<'_>, AnyBuffer>, Error> {
245        let stmt = self.statement.as_stmt_ref();
246
247        let parameter_buffers = descriptions
248            .into_iter()
249            .map(|desc| AnyBuffer::from_desc(capacity, desc))
250            .collect();
251        unsafe { ColumnarBulkInserter::new(stmt, parameter_buffers) }
252    }
253
254    /// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statement. May return
255    /// `None` if row count is not available. Some drivers may also allow to use this to determine
256    /// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
257    /// have been fetched after they have been fetched.
258    ///
259    /// ```
260    /// use odbc_api::{Connection, Error, IntoParameter};
261    ///
262    /// /// Deletes all comments for every user in the slice. Returns the number of deleted
263    /// /// comments.
264    /// pub fn delete_all_comments_from(
265    ///     users: &[&str],
266    ///     conn: Connection<'_>,
267    /// ) -> Result<usize, Error>
268    /// {
269    ///     // Store prepared query for fast repeated execution.
270    ///     let mut prepared = conn.prepare("DELETE FROM Comments WHERE user=?")?;
271    ///     let mut total_deleted_comments = 0;
272    ///     for user in users {
273    ///         prepared.execute(&user.into_parameter())?;
274    ///         total_deleted_comments += prepared
275    ///             .row_count()?
276    ///             .expect("Row count must always be available for DELETE statements.");
277    ///     }
278    ///     Ok(total_deleted_comments)
279    /// }
280    /// ```
281    pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
282        let mut stmt = self.statement.as_stmt_ref();
283        stmt.row_count().into_result(&stmt).map(|count| {
284            // ODBC returns -1 in case a row count is not available
285            if count == -1 {
286                None
287            } else {
288                Some(count.try_into().unwrap())
289            }
290        })
291    }
292
293    /// Use this to limit the time the query is allowed to take, before responding with data to the
294    /// application. The driver may replace the number of seconds you provide with a minimum or
295    /// maximum value. You can specify ``0``, to deactivate the timeout, this is the default. For
296    /// this to work the driver must support this feature. E.g. PostgreSQL, and Microsoft SQL Server
297    /// do, but SQLite or MariaDB do not.
298    ///
299    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
300    ///
301    /// See:
302    /// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function
303    pub fn set_query_timeout_sec(&mut self, timeout_sec: usize) -> Result<(), Error> {
304        let mut stmt = self.statement.as_stmt_ref();
305        stmt.set_query_timeout_sec(timeout_sec).into_result(&stmt)
306    }
307
308    /// The number of seconds to wait for a SQL statement to execute before returning to the
309    /// application. If `timeout_sec` is equal to 0 (default), there is no timeout.
310    ///
311    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
312    ///
313    /// See:
314    /// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function
315    pub fn query_timeout_sec(&mut self) -> Result<usize, Error> {
316        let mut stmt = self.statement.as_stmt_ref();
317        stmt.query_timeout_sec().into_result(&stmt)
318    }
319}
320
321impl<S> ResultSetMetadata for Prepared<S> where S: AsStatementRef {}
322
323impl<S> AsStatementRef for Prepared<S>
324where
325    S: AsStatementRef,
326{
327    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
328        self.statement.as_stmt_ref()
329    }
330}