odbc_api/
prepared.rs

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