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}