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_handle(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(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 mut 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 mut 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 a
109    ///   text buffer with a cell those indiactor value exceeds the maximum element length. This can
110    ///   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 + Send,
118    {
119        // We know that statement is a prepared statement.
120        unsafe { ColumnarBulkInserter::new(self.into_handle(), parameter_buffers, index_mapping) }
121    }
122
123    /// Use this to insert rows of string input into the database.
124    ///
125    /// ```
126    /// use odbc_api::{Connection, Error};
127    ///
128    /// fn insert_text<'e>(connection: Connection<'e>) -> Result<(), Error>{
129    ///     // Insert six rows of text with two columns each into the database in batches of 3. In a
130    ///     // real use case you are likely to achieve a better results with a higher batch size.
131    ///
132    ///     // Note the two `?` used as placeholders for the parameters.
133    ///     let prepared = connection.prepare("INSERT INTO NationalDrink (country, drink) VALUES (?, ?)")?;
134    ///     // We assume both parameter inputs never exceed 50 bytes.
135    ///     let mut prebound = prepared.into_text_inserter(3, [50, 50])?;
136    ///
137    ///     // A cell is an option to byte. We could use `None` to represent NULL but we have no
138    ///     // need to do that in this example.
139    ///     let as_cell = |s: &'static str| { Some(s.as_bytes()) } ;
140    ///
141    ///     // First batch of values
142    ///     prebound.append(["England", "Tea"].into_iter().map(as_cell))?;
143    ///     prebound.append(["Germany", "Beer"].into_iter().map(as_cell))?;
144    ///     prebound.append(["Russia", "Vodka"].into_iter().map(as_cell))?;
145    ///
146    ///     // Execute statement using values bound in buffer.
147    ///     prebound.execute()?;
148    ///     // Clear buffer contents, otherwise the previous values would stay in the buffer.
149    ///     prebound.clear();
150    ///
151    ///     // Second batch of values
152    ///     prebound.append(["India", "Tea"].into_iter().map(as_cell))?;
153    ///     prebound.append(["France", "Wine"].into_iter().map(as_cell))?;
154    ///     prebound.append(["USA", "Cola"].into_iter().map(as_cell))?;
155    ///
156    ///     // Send second batch to the database
157    ///     prebound.execute()?;
158    ///
159    ///     Ok(())
160    /// }
161    /// ```
162    pub fn into_text_inserter(
163        self,
164        capacity: usize,
165        max_str_len: impl IntoIterator<Item = usize>,
166    ) -> Result<ColumnarBulkInserter<S, TextColumn<u8>>, Error> {
167        let max_str_len = max_str_len.into_iter();
168        let parameter_buffers: Vec<_> = max_str_len
169            .map(|max_str_len| TextColumn::new(capacity, max_str_len))
170            .collect();
171        let index_mapping = InOrder::new(parameter_buffers.len());
172        // Text Columns are created with NULL as default, which is valid for insertion.
173        unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping) }
174    }
175
176    /// A [`crate::ColumnarBulkInserter`] which takes ownership of both the statement and the bound
177    /// array parameter buffers.
178    ///
179    /// ```no_run
180    /// use odbc_api::{Connection, Error, IntoParameter, buffers::BufferDesc};
181    ///
182    /// fn insert_birth_years(
183    ///     conn: &Connection,
184    ///     names: &[&str],
185    ///     years: &[i16]
186    /// ) -> Result<(), Error> {
187    ///     // All columns must have equal length.
188    ///     assert_eq!(names.len(), years.len());
189    ///
190    ///     let prepared = conn.prepare("INSERT INTO Birthdays (name, year) VALUES (?, ?)")?;
191    ///
192    ///     // Create a columnar buffer which fits the input parameters.
193    ///     let buffer_description = [
194    ///         BufferDesc::Text { max_str_len: 255 },
195    ///         BufferDesc::I16 { nullable: false },
196    ///     ];
197    ///     // The capacity must be able to hold at least the largest batch. We do everything in one
198    ///     // go, so we set it to the length of the input parameters.
199    ///     let capacity = names.len();
200    ///     // Allocate memory for the array column parameters and bind it to the statement.
201    ///     let mut prebound = prepared.into_column_inserter(capacity, buffer_description)?;
202    ///     // Length of this batch
203    ///     prebound.set_num_rows(capacity);
204    ///
205    ///
206    ///     // Fill the buffer with values column by column
207    ///     let mut col = prebound
208    ///         .column_mut(0)
209    ///         .as_text_view()
210    ///         .expect("We know the name column to hold text.");
211    ///
212    ///     for (index, name) in names.iter().enumerate() {
213    ///         col.set_cell(index, Some(name.as_bytes()));
214    ///     }
215    ///
216    ///     let col = prebound
217    ///         .column_mut(1)
218    ///         .as_slice::<i16>()
219    ///         .expect("We know the year column to hold i16.");
220    ///     col.copy_from_slice(years);
221    ///
222    ///     prebound.execute()?;
223    ///     Ok(())
224    /// }
225    /// ```
226    pub fn into_column_inserter(
227        self,
228        capacity: usize,
229        descriptions: impl IntoIterator<Item = BufferDesc>,
230    ) -> Result<ColumnarBulkInserter<S, AnyBuffer>, Error> {
231        let parameter_buffers: Vec<_> = descriptions
232            .into_iter()
233            .map(|desc| AnyBuffer::from_desc(capacity, desc))
234            .collect();
235        let index_mapping = InOrder::new(parameter_buffers.len());
236        // Safe: We know this to be a valid prepared statement. Also we just created the buffers
237        // to be bound and know them to be empty. => Therfore they are valid and do not contain any
238        // indicator values which would could trigger out of bounds in the database drivers.
239        unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping) }
240    }
241
242    /// Similar to [`Self::into_column_inserter`], but allows to specify a custom mapping between
243    /// columns and parameters. This is useful if e.g. the same values a bound to multiple
244    /// parameter placeholders.
245    pub fn into_column_inserter_with_mapping(
246        self,
247        capacity: usize,
248        descriptions: impl IntoIterator<Item = BufferDesc>,
249        index_mapping: impl InputParameterMapping,
250    ) -> Result<ColumnarBulkInserter<S, AnyBuffer>, Error> {
251        let parameter_buffers: Vec<_> = descriptions
252            .into_iter()
253            .map(|desc| AnyBuffer::from_desc(capacity, desc))
254            .collect();
255        // Safe: We know this to be a valid prepared statement. Also we just created the buffers
256        // to be bound and know them to be empty. => Therfore they are valid and do not contain any
257        // indicator values which would could trigger out of bounds in the database drivers.
258        unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping) }
259    }
260
261    /// A [`crate::ColumnarBulkInserter`] which has ownership of the bound array parameter buffers
262    /// and borrows the statement. For most usecases [`Self::into_column_inserter`] is what you
263    /// want to use, yet on some instances you may want to bind new paramater buffers to the same
264    /// prepared statement. E.g. to grow the capacity dynamically during insertions with several
265    /// chunks. In such use cases you may only want to borrow the prepared statemnt, so it can be
266    /// reused with a different set of parameter buffers.
267    pub fn column_inserter(
268        &mut self,
269        capacity: usize,
270        descriptions: impl IntoIterator<Item = BufferDesc>,
271    ) -> Result<ColumnarBulkInserter<StatementRef<'_>, AnyBuffer>, Error> {
272        // Remark: We repeat the implementation here. It is hard to reuse the
273        // `column_inserter_with_mapping` function, because we need to know the number of parameters
274        // to create the `InOrder` mapping.
275        let stmt = self.statement.as_stmt_ref();
276
277        let parameter_buffers: Vec<_> = descriptions
278            .into_iter()
279            .map(|desc| AnyBuffer::from_desc(capacity, desc))
280            .collect();
281
282        let index_mapping = InOrder::new(parameter_buffers.len());
283        // Safe: We know that the statement is a prepared statement, and we just created the buffers
284        // to be bound and know them to be empty. => Therfore they are valid and do not contain any
285        // indicator values which would could trigger out of bounds in the database drivers.
286        unsafe { ColumnarBulkInserter::new(stmt, parameter_buffers, index_mapping) }
287    }
288
289    /// Similar to [`Self::column_inserter`], but allows to specify a custom mapping between columns
290    /// and parameters. This is useful if e.g. the same values a bound to multiple parameter
291    /// placeholders.
292    pub fn column_inserter_with_mapping(
293        &mut self,
294        capacity: usize,
295        descriptions: impl IntoIterator<Item = BufferDesc>,
296        index_mapping: impl InputParameterMapping,
297    ) -> Result<ColumnarBulkInserter<StatementRef<'_>, AnyBuffer>, Error> {
298        let stmt = self.statement.as_stmt_ref();
299
300        let parameter_buffers: Vec<_> = descriptions
301            .into_iter()
302            .map(|desc| AnyBuffer::from_desc(capacity, desc))
303            .collect();
304
305        // Safe: We know that the statement is a prepared statement, and we just created the buffers
306        // to be bound and know them to be empty. => Therfore they are valid and do not contain any
307        // indicator values which would could trigger out of bounds in the database drivers.
308        unsafe { ColumnarBulkInserter::new(stmt, parameter_buffers, index_mapping) }
309    }
310
311    /// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statement. May return
312    /// `None` if row count is not available. Some drivers may also allow to use this to determine
313    /// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
314    /// have been fetched after they have been fetched.
315    ///
316    /// ```
317    /// use odbc_api::{Connection, Error, IntoParameter};
318    ///
319    /// /// Deletes all comments for every user in the slice. Returns the number of deleted
320    /// /// comments.
321    /// pub fn delete_all_comments_from(
322    ///     users: &[&str],
323    ///     conn: Connection<'_>,
324    /// ) -> Result<usize, Error>
325    /// {
326    ///     // Store prepared query for fast repeated execution.
327    ///     let mut prepared = conn.prepare("DELETE FROM Comments WHERE user=?")?;
328    ///     let mut total_deleted_comments = 0;
329    ///     for user in users {
330    ///         prepared.execute(&user.into_parameter())?;
331    ///         total_deleted_comments += prepared
332    ///             .row_count()?
333    ///             .expect("Row count must always be available for DELETE statements.");
334    ///     }
335    ///     Ok(total_deleted_comments)
336    /// }
337    /// ```
338    pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
339        let mut stmt = self.statement.as_stmt_ref();
340        stmt.row_count().into_result(&stmt).map(|count| {
341            // ODBC returns -1 in case a row count is not available
342            if count == -1 {
343                None
344            } else {
345                Some(count.try_into().unwrap())
346            }
347        })
348    }
349
350    /// Use this to limit the time the query is allowed to take, before responding with data to the
351    /// application. The driver may replace the number of seconds you provide with a minimum or
352    /// maximum value. You can specify ``0``, to deactivate the timeout, this is the default. For
353    /// this to work the driver must support this feature. E.g. PostgreSQL, and Microsoft SQL Server
354    /// do, but SQLite or MariaDB do not.
355    ///
356    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
357    ///
358    /// See:
359    /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
360    pub fn set_query_timeout_sec(&mut self, timeout_sec: usize) -> Result<(), Error> {
361        let mut stmt = self.statement.as_stmt_ref();
362        stmt.set_query_timeout_sec(timeout_sec).into_result(&stmt)
363    }
364
365    /// The number of seconds to wait for a SQL statement to execute before returning to the
366    /// application. If `timeout_sec` is equal to 0 (default), there is no timeout.
367    ///
368    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
369    ///
370    /// See:
371    /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
372    pub fn query_timeout_sec(&mut self) -> Result<usize, Error> {
373        let mut stmt = self.statement.as_stmt_ref();
374        stmt.query_timeout_sec().into_result(&stmt)
375    }
376}
377
378impl<S> ResultSetMetadata for Prepared<S> where S: AsStatementRef {}
379
380impl<S> AsStatementRef for Prepared<S>
381where
382    S: AsStatementRef,
383{
384    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
385        self.statement.as_stmt_ref()
386    }
387}