odbc_api/prepared.rs
1use std::num::NonZeroUsize;
2
3use crate::{
4 ColumnarBulkInserter, CursorImpl, DataType, Error, InputParameterMapping,
5 ParameterCollectionRef, ResultSetMetadata,
6 buffers::{AnyBuffer, BufferDesc, ColumnBuffer, TextColumn},
7 columnar_bulk_inserter::InOrder,
8 execute::execute_with_parameters,
9 handles::{
10 ASSUMED_MAX_LENGTH_OF_VARCHAR, ASSUMED_MAX_LENGTH_OF_W_VARCHAR, AsStatementRef, ColumnType,
11 HasDataType, Statement, StatementRef,
12 },
13 parameter::WithDataType,
14};
15
16/// A prepared query. Prepared queries are useful if the similar queries should executed more than
17/// once. See [`crate::Connection::prepare`].
18pub struct Prepared<S> {
19 statement: S,
20}
21
22impl<S> Prepared<S> {
23 pub(crate) fn new(statement: S) -> Self {
24 Self { statement }
25 }
26
27 /// Transfer ownership to the underlying statement handle.
28 ///
29 /// The resulting type is one level of indirection away from the raw pointer of the ODBC API. It
30 /// no longer has any guarantees about bound buffers, but is still guaranteed to be a valid
31 /// allocated statement handle. This serves together with
32 /// [`crate::handles::StatementImpl::into_sys`] or [`crate::handles::Statement::as_sys`] this
33 /// serves as an escape hatch to access the functionality provided by `crate::sys` not yet
34 /// accessible through safe abstractions.
35 pub fn into_handle(self) -> S {
36 self.statement
37 }
38}
39
40impl<S> Prepared<S>
41where
42 S: AsStatementRef,
43{
44 /// Execute the prepared statement.
45 ///
46 /// * `params`: Used to bind these parameters before executing the statement. You can use `()`
47 /// to represent no parameters. In regards to binding arrays of parameters: Should `params`
48 /// specify a parameter set size of `0`, nothing is executed, and `Ok(None)` is returned. See
49 /// the [`crate::parameter`] module level documentation for more information on how to pass
50 /// parameters.
51 pub fn execute(
52 &mut self,
53 params: impl ParameterCollectionRef,
54 ) -> Result<Option<CursorImpl<StatementRef<'_>>>, Error> {
55 let stmt = self.statement.as_stmt_ref();
56 execute_with_parameters(stmt, None, params)
57 }
58
59 /// Describes parameter marker associated with a prepared SQL statement.
60 ///
61 /// # Parameters
62 ///
63 /// * `parameter_number`: Parameter marker number ordered sequentially in increasing parameter
64 /// order, starting at 1.
65 pub fn describe_param(&mut self, parameter_number: u16) -> Result<ColumnType, Error> {
66 let mut stmt = self.as_stmt_ref();
67
68 stmt.describe_param(parameter_number).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 pub fn num_params(&mut self) -> Result<u16, Error> {
75 let mut stmt = self.as_stmt_ref();
76 stmt.num_params().into_result(&stmt)
77 }
78
79 /// Number of placeholders which must be provided with [`Self::execute`] in order to execute
80 /// this statement. This is equivalent to the number of placeholders used in the SQL string
81 /// used to prepare the statement.
82 ///
83 /// ```
84 /// use odbc_api::{Connection, Error, handles::ColumnType};
85 ///
86 /// fn collect_parameter_descriptions(
87 /// connection: Connection<'_>
88 /// ) -> Result<Vec<ColumnType>, Error>{
89 /// // Note the two `?` used as placeholders for the parameters.
90 /// let sql = "INSERT INTO NationalDrink (country, drink) VALUES (?, ?)";
91 /// let mut prepared = connection.prepare(sql)?;
92 ///
93 /// let params: Vec<_> = prepared.parameter_descriptions()?.collect::<Result<_,_>>()?;
94 ///
95 /// Ok(params)
96 /// }
97 /// ```
98 pub fn parameter_descriptions(
99 &mut self,
100 ) -> Result<
101 impl DoubleEndedIterator<Item = Result<ColumnType, Error>>
102 + ExactSizeIterator<Item = Result<ColumnType, Error>>
103 + '_,
104 Error,
105 > {
106 Ok((1..=self.num_params()?).map(|index| self.describe_param(index)))
107 }
108
109 /// Unless you want to roll your own column buffer implementation users are encouraged to use
110 /// [`Self::into_text_inserter`] or [`Self::into_column_inserter`] instead.
111 ///
112 /// # Safety
113 ///
114 /// * Parameters must all be valid for insertion. An example for an invalid parameter would be a
115 /// text buffer with a cell those indiactor value exceeds the maximum element length. This can
116 /// happen after when truncation occurs then writing into a buffer.
117 pub unsafe fn unchecked_bind_columnar_array_parameters<C>(
118 self,
119 parameter_buffers: Vec<C>,
120 index_mapping: impl InputParameterMapping,
121 ) -> Result<ColumnarBulkInserter<S, C>, Error>
122 where
123 C: ColumnBuffer + HasDataType + Send,
124 {
125 // We know that statement is a prepared statement.
126 unsafe { ColumnarBulkInserter::new(self.into_handle(), parameter_buffers, index_mapping) }
127 }
128
129 /// Use this to insert rows of string input into the database.
130 ///
131 /// ```
132 /// use odbc_api::{Connection, Error};
133 ///
134 /// fn insert_text<'e>(connection: Connection<'e>) -> Result<(), Error>{
135 /// // Insert six rows of text with two columns each into the database in batches of 3. In a
136 /// // real use case you are likely to achieve a better results with a higher batch size.
137 ///
138 /// // Note the two `?` used as placeholders for the parameters.
139 /// let prepared = connection.prepare("INSERT INTO NationalDrink (country, drink) VALUES (?, ?)")?;
140 /// // We assume both parameter inputs never exceed 50 bytes.
141 /// let mut prebound = prepared.into_text_inserter(3, [50, 50])?;
142 ///
143 /// // A cell is an option to byte. We could use `None` to represent NULL but we have no
144 /// // need to do that in this example.
145 /// let as_cell = |s: &'static str| { Some(s.as_bytes()) } ;
146 ///
147 /// // First batch of values
148 /// prebound.append(["England", "Tea"].into_iter().map(as_cell))?;
149 /// prebound.append(["Germany", "Beer"].into_iter().map(as_cell))?;
150 /// prebound.append(["Russia", "Vodka"].into_iter().map(as_cell))?;
151 ///
152 /// // Execute statement using values bound in buffer.
153 /// prebound.execute()?;
154 /// // Clear buffer contents, otherwise the previous values would stay in the buffer.
155 /// prebound.clear();
156 ///
157 /// // Second batch of values
158 /// prebound.append(["India", "Tea"].into_iter().map(as_cell))?;
159 /// prebound.append(["France", "Wine"].into_iter().map(as_cell))?;
160 /// prebound.append(["USA", "Cola"].into_iter().map(as_cell))?;
161 ///
162 /// // Send second batch to the database
163 /// prebound.execute()?;
164 ///
165 /// Ok(())
166 /// }
167 /// ```
168 pub fn into_text_inserter(
169 self,
170 capacity: usize,
171 max_str_len: impl IntoIterator<Item = usize>,
172 ) -> Result<ColumnarBulkInserter<S, TextColumn<u8>>, Error> {
173 let max_str_len = max_str_len.into_iter();
174 let parameter_buffers: Vec<_> = max_str_len
175 .map(|max_str_len| TextColumn::new(capacity, max_str_len))
176 .collect();
177 let index_mapping = InOrder::new(parameter_buffers.len());
178 // Text Columns are created with NULL as default, which is valid for insertion.
179 unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping) }
180 }
181
182 /// A [`crate::ColumnarBulkInserter`] which takes ownership of both the statement and the bound
183 /// array parameter buffers.
184 ///
185 /// ```no_run
186 /// use odbc_api::{Connection, Error, IntoParameter, BindParamDesc};
187 ///
188 /// fn insert_birth_years(
189 /// conn: &Connection,
190 /// names: &[&str],
191 /// years: &[i16]
192 /// ) -> Result<(), Error> {
193 /// // All columns must have equal length.
194 /// assert_eq!(names.len(), years.len());
195 ///
196 /// let prepared = conn.prepare("INSERT INTO Birthdays (name, year) VALUES (?, ?)")?;
197 ///
198 /// // Create a columnar buffer which fits the input parameters.
199 /// let parameter_descriptions = [
200 /// BindParamDesc::text(255),
201 /// BindParamDesc::i16(false),
202 /// ];
203 /// // The capacity must be able to hold at least the largest batch. We do everything in one
204 /// // go, so we set it to the length of the input parameters.
205 /// let capacity = names.len();
206 /// // Allocate memory for the array column parameters and bind it to the statement.
207 /// let mut prebound = prepared.into_column_inserter(capacity, parameter_descriptions)?;
208 /// // Length of this batch
209 /// prebound.set_num_rows(capacity);
210 ///
211 ///
212 /// // Fill the buffer with values column by column
213 /// let mut col = prebound
214 /// .column_mut(0)
215 /// .as_text_view()
216 /// .expect("We know the name column to hold text.");
217 ///
218 /// for (index, name) in names.iter().enumerate() {
219 /// col.set_cell(index, Some(name.as_bytes()));
220 /// }
221 ///
222 /// let col = prebound
223 /// .column_mut(1)
224 /// .as_slice::<i16>()
225 /// .expect("We know the year column to hold i16.");
226 /// col.copy_from_slice(years);
227 ///
228 /// prebound.execute()?;
229 /// Ok(())
230 /// }
231 /// ```
232 pub fn into_column_inserter(
233 self,
234 capacity: usize,
235 descriptions: impl IntoIterator<Item = BindParamDesc>,
236 ) -> Result<ColumnarBulkInserter<S, WithDataType<AnyBuffer>>, Error> {
237 let parameter_buffers: Vec<_> = descriptions
238 .into_iter()
239 .map(|desc| desc.make_input_buffer(capacity))
240 .collect();
241 let index_mapping = InOrder::new(parameter_buffers.len());
242 // Safe: We know this to be a valid prepared statement. Also we just created the buffers
243 // to be bound and know them to be empty. => Therfore they are valid and do not contain any
244 // indicator values which would could trigger out of bounds in the database drivers.
245 unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping) }
246 }
247
248 /// Similar to [`Self::into_column_inserter`], but allows to specify a custom mapping between
249 /// columns and parameters. This is useful if e.g. the same values a bound to multiple
250 /// parameter placeholders.
251 pub fn into_column_inserter_with_mapping(
252 self,
253 capacity: usize,
254 descriptions: impl IntoIterator<Item = BindParamDesc>,
255 index_mapping: impl InputParameterMapping,
256 ) -> Result<ColumnarBulkInserter<S, WithDataType<AnyBuffer>>, Error> {
257 let parameter_buffers: Vec<_> = descriptions
258 .into_iter()
259 .map(|desc| desc.make_input_buffer(capacity))
260 .collect();
261 // Safe: We know this to be a valid prepared statement. Also we just created the buffers
262 // to be bound and know them to be empty. => Therfore they are valid and do not contain any
263 // indicator values which would could trigger out of bounds in the database drivers.
264 unsafe { self.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping) }
265 }
266
267 /// A [`crate::ColumnarBulkInserter`] which has ownership of the bound array parameter buffers
268 /// and borrows the statement. For most usecases [`Self::into_column_inserter`] is what you
269 /// want to use, yet on some instances you may want to bind new paramater buffers to the same
270 /// prepared statement. E.g. to grow the capacity dynamically during insertions with several
271 /// chunks. In such use cases you may only want to borrow the prepared statemnt, so it can be
272 /// reused with a different set of parameter buffers.
273 pub fn column_inserter(
274 &mut self,
275 capacity: usize,
276 descriptions: impl IntoIterator<Item = BindParamDesc>,
277 ) -> Result<ColumnarBulkInserter<StatementRef<'_>, WithDataType<AnyBuffer>>, Error> {
278 // Remark: We repeat the implementation here. It is hard to reuse the
279 // `column_inserter_with_mapping` function, because we need to know the number of parameters
280 // to create the `InOrder` mapping.
281 let stmt = self.statement.as_stmt_ref();
282
283 let parameter_buffers: Vec<_> = descriptions
284 .into_iter()
285 .map(|desc| desc.make_input_buffer(capacity))
286 .collect();
287
288 let index_mapping = InOrder::new(parameter_buffers.len());
289 // Safe: We know that the statement is a prepared statement, and we just created the buffers
290 // to be bound and know them to be empty. => Therfore they are valid and do not contain any
291 // indicator values which would could trigger out of bounds in the database drivers.
292 unsafe { ColumnarBulkInserter::new(stmt, parameter_buffers, index_mapping) }
293 }
294
295 /// Similar to [`Self::column_inserter`], but allows to specify a custom mapping between columns
296 /// and parameters. This is useful if e.g. the same values a bound to multiple parameter
297 /// placeholders.
298 pub fn column_inserter_with_mapping(
299 &mut self,
300 capacity: usize,
301 descriptions: impl IntoIterator<Item = BindParamDesc>,
302 index_mapping: impl InputParameterMapping,
303 ) -> Result<ColumnarBulkInserter<StatementRef<'_>, WithDataType<AnyBuffer>>, Error> {
304 let stmt = self.statement.as_stmt_ref();
305
306 let parameter_buffers: Vec<_> = descriptions
307 .into_iter()
308 .map(|desc| desc.make_input_buffer(capacity))
309 .collect();
310
311 // Safe: We know that the statement is a prepared statement, and we just created the buffers
312 // to be bound and know them to be empty. => Therfore they are valid and do not contain any
313 // indicator values which would could trigger out of bounds in the database drivers.
314 unsafe { ColumnarBulkInserter::new(stmt, parameter_buffers, index_mapping) }
315 }
316
317 /// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statement. May return
318 /// `None` if row count is not available. Some drivers may also allow to use this to determine
319 /// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
320 /// have been fetched after they have been fetched.
321 ///
322 /// ```
323 /// use odbc_api::{Connection, Error, IntoParameter};
324 ///
325 /// /// Deletes all comments for every user in the slice. Returns the number of deleted
326 /// /// comments.
327 /// pub fn delete_all_comments_from(
328 /// users: &[&str],
329 /// conn: Connection<'_>,
330 /// ) -> Result<usize, Error>
331 /// {
332 /// // Store prepared query for fast repeated execution.
333 /// let mut prepared = conn.prepare("DELETE FROM Comments WHERE user=?")?;
334 /// let mut total_deleted_comments = 0;
335 /// for user in users {
336 /// prepared.execute(&user.into_parameter())?;
337 /// total_deleted_comments += prepared
338 /// .row_count()?
339 /// .expect("Row count must always be available for DELETE statements.");
340 /// }
341 /// Ok(total_deleted_comments)
342 /// }
343 /// ```
344 pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
345 let mut stmt = self.statement.as_stmt_ref();
346 stmt.row_count().into_result(&stmt).map(|count| {
347 // ODBC returns -1 in case a row count is not available
348 if count == -1 {
349 None
350 } else {
351 Some(count.try_into().unwrap())
352 }
353 })
354 }
355
356 /// Use this to limit the time the query is allowed to take, before responding with data to the
357 /// application. The driver may replace the number of seconds you provide with a minimum or
358 /// maximum value. You can specify ``0``, to deactivate the timeout, this is the default. For
359 /// this to work the driver must support this feature. E.g. PostgreSQL, and Microsoft SQL Server
360 /// do, but SQLite or MariaDB do not.
361 ///
362 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
363 ///
364 /// See:
365 /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
366 pub fn set_query_timeout_sec(&mut self, timeout_sec: usize) -> Result<(), Error> {
367 let mut stmt = self.statement.as_stmt_ref();
368 stmt.set_query_timeout_sec(timeout_sec).into_result(&stmt)
369 }
370
371 /// The number of seconds to wait for a SQL statement to execute before returning to the
372 /// application. If `timeout_sec` is equal to 0 (default), there is no timeout.
373 ///
374 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
375 ///
376 /// See:
377 /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
378 pub fn query_timeout_sec(&mut self) -> Result<usize, Error> {
379 let mut stmt = self.statement.as_stmt_ref();
380 stmt.query_timeout_sec().into_result(&stmt)
381 }
382}
383
384/// Description of paramater domain and buffer bound for bulk insertion. Used by
385/// [`Prepared::column_inserter`].
386#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387pub struct BindParamDesc {
388 /// Describes the C-Type used to describe values and wether we need a NULL representation.
389 pub buffer_desc: BufferDesc,
390 /// The relational type or domain of the parameter. While there is a strong correllation between
391 /// the buffer used and the relational type, it is not always a naive mapping. E.g. a text buffer
392 /// can not only carry VARCHAR but also a DECIMAL or a timestamp.
393 pub data_type: DataType,
394}
395
396impl BindParamDesc {
397 /// A description for binding utf-16 data to a parameter.
398 ///
399 /// # Parameters
400 ///
401 /// * `max_str_len`: The maximum length of the string in utf-16 code units. Excluding
402 /// terminating null character.
403 ///
404 /// Uses a wide character buffer and sets the data type to [`DataType::WVarchar`] or
405 /// [`DataType::WLongVarchar`].
406 pub fn wide_text(max_str_len: usize) -> Self {
407 let data_type = if max_str_len <= ASSUMED_MAX_LENGTH_OF_W_VARCHAR {
408 DataType::WVarchar {
409 length: NonZeroUsize::new(max_str_len),
410 }
411 } else {
412 DataType::WLongVarchar {
413 length: NonZeroUsize::new(max_str_len),
414 }
415 };
416 BindParamDesc {
417 buffer_desc: BufferDesc::WText { max_str_len },
418 data_type,
419 }
420 }
421
422 /// A description for binding narrow text (usually utf-8) data to a parameter.
423 ///
424 /// * `max_str_len`: The maximum length of the string in bytes. excluding terminating null
425 /// character.
426 ///
427 /// Uses a narrow character buffer and sets the data type to [`DataType::Varchar`] or
428 /// [`DataType::LongVarchar`].
429 pub fn text(max_str_len: usize) -> Self {
430 let data_type = if max_str_len <= ASSUMED_MAX_LENGTH_OF_VARCHAR {
431 DataType::Varchar {
432 length: NonZeroUsize::new(max_str_len),
433 }
434 } else {
435 DataType::LongVarchar {
436 length: NonZeroUsize::new(max_str_len),
437 }
438 };
439 BindParamDesc {
440 buffer_desc: BufferDesc::Text { max_str_len },
441 data_type,
442 }
443 }
444
445 /// A description for binding timestamps to a parameter.
446 ///
447 /// # Parameters
448 ///
449 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
450 /// if `false` null values can not be represented, but we can save an allocation for an
451 /// indicator buffer.
452 /// * `precision`: The number of digits for the fractional seconds part. E.g. if you know your
453 /// input to be milliseconds choose `3`. Many databases error if you exceed the maximum
454 /// precision of the column. If you are unsure about the maximum precision supported by the
455 /// Database `7` is a good guess.
456 pub fn timestamp(nullable: bool, precision: i16) -> Self {
457 BindParamDesc {
458 buffer_desc: BufferDesc::Timestamp { nullable },
459 data_type: DataType::Timestamp { precision },
460 }
461 }
462
463 /// A description for binding [`crate::Time`] values to a parameter.
464 ///
465 /// # Parameters
466 ///
467 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
468 /// if `false` null values can not be represented, but we can save an allocation for an
469 /// indicator buffer.
470 pub fn time(nullable: bool) -> Self {
471 BindParamDesc {
472 buffer_desc: BufferDesc::Time { nullable },
473 data_type: DataType::Time { precision: 0 },
474 }
475 }
476
477 /// A description for binding wallclock time in a text buffer.
478 ///
479 /// # Parameters
480 ///
481 /// * `precision`: The number of digits for the fractional seconds part. E.g. if you know your
482 /// input to be milliseconds choose `3`. Some databases error if you exceed the maximum
483 /// precision of the column. If you are unsure about the maximum precision supported by the
484 /// Database `7` is a good guess.
485 pub fn time_as_text(precision: i16) -> Self {
486 // Text representation of time has a fixed length of 8 (hh:mm:ss) plus the radix character
487 // and the fractional seconds. E.g. for milliseconds we would have `hh:mm:ss.fff` which has
488 // a length of 12.
489 let max_str_len = 8 + if precision > 0 {
490 // Radix character + fractional seconds digits
491 1 + precision as usize
492 } else {
493 0
494 };
495 BindParamDesc {
496 buffer_desc: BufferDesc::Text { max_str_len },
497 data_type: DataType::Time { precision },
498 }
499 }
500
501 /// A description for binding decimal represented as text to a parameter.
502 ///
503 /// # Parameters
504 ///
505 /// * `precision`: The total number of digits in the decimal number. E.g. for `123.45` this
506 /// would be `5`.
507 /// * `scale`: The number of digits to the right of the decimal point. E.g. for `123.45` this
508 /// would be `2`.
509 ///
510 pub fn decimal_as_text(precision: u8, scale: i8) -> Self {
511 // Length of a text representation of a decimal
512 let max_str_len = match scale {
513 // Precision digits + (- scale zeroes) + sign
514 i8::MIN..=-1 => (precision as i32 + scale as i32 + 1).try_into().unwrap(),
515 // Precision digits + sign
516 0 => precision as usize + 1,
517 // Precision digits + radix character (`.`) + sign
518 1.. => precision as usize + 1 + 1,
519 };
520 BindParamDesc {
521 buffer_desc: BufferDesc::Text { max_str_len },
522 data_type: DataType::Decimal {
523 precision: precision as usize,
524 scale: scale as i16,
525 },
526 }
527 }
528
529 /// A description for binding 16 bit integers to a parameter.
530 ///
531 /// # Parameters
532 ///
533 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
534 /// if `false` null values can not be represented, but we can save an allocation for an
535 /// indicator buffer.
536 pub fn i16(nullable: bool) -> Self {
537 BindParamDesc {
538 buffer_desc: BufferDesc::I16 { nullable },
539 data_type: DataType::SmallInt,
540 }
541 }
542
543 /// A description for binding 32 bit integers to a parameter.
544 ///
545 /// # Parameters
546 ///
547 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
548 /// if `false` null values can not be represented, but we can save an allocation for an
549 /// indicator buffer.
550 pub fn i32(nullable: bool) -> Self {
551 BindParamDesc {
552 buffer_desc: BufferDesc::I32 { nullable },
553 data_type: DataType::Integer,
554 }
555 }
556
557 /// A description for binding 64 bit integers to a parameter.
558 ///
559 /// # Parameters
560 ///
561 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
562 /// if `false` null values can not be represented, but we can save an allocation for an
563 /// indicator buffer.
564 pub fn i64(nullable: bool) -> Self {
565 BindParamDesc {
566 buffer_desc: BufferDesc::I64 { nullable },
567 data_type: DataType::BigInt,
568 }
569 }
570
571 /// A description for binding variadic binary data to a parameter.
572 ///
573 /// # Parameters
574 ///
575 /// * `max_bytes`: The maximum length of the binary data in bytes.
576 pub fn binary(max_bytes: usize) -> Self {
577 BindParamDesc {
578 buffer_desc: BufferDesc::Binary { max_bytes },
579 data_type: DataType::Binary {
580 length: NonZeroUsize::new(max_bytes),
581 },
582 }
583 }
584
585 /// A description for binding `f64` values to a parameter.
586 ///
587 /// # Parameters
588 ///
589 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
590 /// if `false` null values can not be represented, but we can save an allocation for an
591 /// indicator buffer.
592 pub fn f64(nullable: bool) -> Self {
593 BindParamDesc {
594 buffer_desc: BufferDesc::F64 { nullable },
595 data_type: DataType::Double,
596 }
597 }
598
599 /// A description for binding `f32` values to a parameter.
600 ///
601 /// # Parameters
602 ///
603 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
604 /// if `false` null values can not be represented, but we can save an allocation for an
605 /// indicator buffer.
606 pub fn f32(nullable: bool) -> Self {
607 BindParamDesc {
608 buffer_desc: BufferDesc::F32 { nullable },
609 data_type: DataType::Real,
610 }
611 }
612
613 /// A description for binding `u8` values to a parameter.
614 ///
615 /// # Parameters
616 ///
617 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
618 /// if `false` null values can not be represented, but we can save an allocation for an
619 /// indicator buffer.
620 pub fn u8(nullable: bool) -> Self {
621 BindParamDesc {
622 buffer_desc: BufferDesc::U8 { nullable },
623 // Few databases support unsigned types, binding U8 as tiny int might lead to weird
624 // stuff if the database has type is signed. I guess. Let's bind it as SmallInt by
625 // default, just to be on the safe side.
626 data_type: DataType::SmallInt,
627 }
628 }
629
630 /// A description for binding `u8` values to a parameter.
631 ///
632 /// # Parameters
633 ///
634 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
635 /// if `false` null values can not be represented, but we can save an allocation for an
636 /// indicator buffer.
637 pub fn i8(nullable: bool) -> Self {
638 BindParamDesc {
639 buffer_desc: BufferDesc::I8 { nullable },
640 data_type: DataType::TinyInt,
641 }
642 }
643
644 /// A description for binding [`crate::sys::Date`] values to a parameter.
645 ///
646 /// # Parameters
647 ///
648 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
649 /// if `false` null values can not be represented, but we can save an allocation for an
650 /// indicator buffer.
651 pub fn date(nullable: bool) -> Self {
652 BindParamDesc {
653 buffer_desc: BufferDesc::Date { nullable },
654 data_type: DataType::Date,
655 }
656 }
657
658 /// A description for binding [`crate::Bit`] values to a parameter.
659 ///
660 /// # Parameters
661 ///
662 /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
663 /// if `false` null values can not be represented, but we can save an allocation for an
664 /// indicator buffer.
665 pub fn bit(nullable: bool) -> Self {
666 BindParamDesc {
667 buffer_desc: BufferDesc::Bit { nullable },
668 data_type: DataType::Bit,
669 }
670 }
671
672 fn make_input_buffer(&self, max_rows: usize) -> WithDataType<AnyBuffer> {
673 let buffer = AnyBuffer::from_desc(max_rows, self.buffer_desc);
674 WithDataType::new(buffer, self.data_type)
675 }
676}
677
678impl<S> ResultSetMetadata for Prepared<S> where S: AsStatementRef {}
679
680impl<S> AsStatementRef for Prepared<S>
681where
682 S: AsStatementRef,
683{
684 fn as_stmt_ref(&mut self) -> StatementRef<'_> {
685 self.statement.as_stmt_ref()
686 }
687}
688
689#[cfg(test)]
690mod tests {
691 use crate::buffers::BufferDesc;
692
693 use super::BindParamDesc;
694
695 #[test]
696 fn bind_paramater_description_decimal_length_positive_scale() {
697 let BufferDesc::Text { max_str_len } = BindParamDesc::decimal_as_text(5, 2).buffer_desc
698 else {
699 panic!("Expected a text buffer for decimal parameters.")
700 };
701 assert_eq!("-123.45".len(), max_str_len);
702 }
703
704 #[test]
705 fn bind_paramater_description_decimal_length_scale_zero() {
706 let BufferDesc::Text { max_str_len } = BindParamDesc::decimal_as_text(5, 0).buffer_desc
707 else {
708 panic!("Expected a text buffer for decimal parameters.")
709 };
710 assert_eq!("-12345".len(), max_str_len);
711 }
712
713 #[test]
714 fn bind_paramater_description_decimal_length_negative_scale() {
715 let BufferDesc::Text { max_str_len } = BindParamDesc::decimal_as_text(5, -2).buffer_desc
716 else {
717 panic!("Expected a text buffer for decimal parameters.")
718 };
719 assert_eq!("-123".len(), max_str_len);
720 }
721}