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}