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}