Skip to main content

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    /// * `precision`: The number of digits for the fractional seconds part. E.g. if you know your
471    ///   input to be milliseconds choose `3`. Many databases error if you exceed the maximum
472    ///   precision of the column. If you are unsure about the maximum precision supported by the
473    ///   Database `7` is a good guess.
474    pub fn time(nullable: bool, precision: i16) -> Self {
475        BindParamDesc {
476            buffer_desc: BufferDesc::Time { nullable },
477            data_type: DataType::Time { precision },
478        }
479    }
480
481    /// A description for binding decimal represented as text to a parameter.
482    ///
483    /// # Parameters
484    ///
485    /// * `precision`: The total number of digits in the decimal number. E.g. for `123.45` this
486    ///   would be `5`.
487    /// * `scale`: The number of digits to the right of the decimal point. E.g. for `123.45` this
488    ///   would be `2`.
489    ///
490    pub fn decimal_as_text(precision: u8, scale: i8) -> Self {
491        // Length of a text representation of a decimal
492        let max_str_len = match scale {
493            // Precision digits + (- scale zeroes) + sign
494            i8::MIN..=-1 => (precision as i32 + scale as i32 + 1).try_into().unwrap(),
495            // Precision digits + sign
496            0 => precision as usize + 1,
497            // Precision digits + radix character (`.`) + sign
498            1.. => precision as usize + 1 + 1,
499        };
500        BindParamDesc {
501            buffer_desc: BufferDesc::Text { max_str_len },
502            data_type: DataType::Decimal {
503                precision: precision as usize,
504                scale: scale as i16,
505            },
506        }
507    }
508
509    /// A description for binding 16 bit integers to a parameter.
510    ///
511    /// # Parameters
512    ///
513    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
514    ///   if `false` null values can not be represented, but we can save an allocation for an
515    ///   indicator buffer.
516    pub fn i16(nullable: bool) -> Self {
517        BindParamDesc {
518            buffer_desc: BufferDesc::I16 { nullable },
519            data_type: DataType::SmallInt,
520        }
521    }
522
523    /// A description for binding 32 bit integers to a parameter.
524    ///
525    /// # Parameters
526    ///
527    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
528    ///   if `false` null values can not be represented, but we can save an allocation for an
529    ///   indicator buffer.
530    pub fn i32(nullable: bool) -> Self {
531        BindParamDesc {
532            buffer_desc: BufferDesc::I32 { nullable },
533            data_type: DataType::Integer,
534        }
535    }
536
537    /// A description for binding 64 bit integers to a parameter.
538    ///
539    /// # Parameters
540    ///
541    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
542    ///   if `false` null values can not be represented, but we can save an allocation for an
543    ///   indicator buffer.
544    pub fn i64(nullable: bool) -> Self {
545        BindParamDesc {
546            buffer_desc: BufferDesc::I64 { nullable },
547            data_type: DataType::BigInt,
548        }
549    }
550
551    /// A description for binding variadic binary data to a parameter.
552    ///
553    /// # Parameters
554    ///
555    /// * `max_bytes`: The maximum length of the binary data in bytes.
556    pub fn binary(max_bytes: usize) -> Self {
557        BindParamDesc {
558            buffer_desc: BufferDesc::Binary { max_bytes },
559            data_type: DataType::Binary {
560                length: NonZeroUsize::new(max_bytes),
561            },
562        }
563    }
564
565    /// A description for binding `f64` values to a parameter.
566    ///
567    /// # Parameters
568    ///
569    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
570    ///   if `false` null values can not be represented, but we can save an allocation for an
571    ///   indicator buffer.
572    pub fn f64(nullable: bool) -> Self {
573        BindParamDesc {
574            buffer_desc: BufferDesc::F64 { nullable },
575            data_type: DataType::Double,
576        }
577    }
578
579    /// A description for binding `f32` values to a parameter.
580    ///
581    /// # Parameters
582    ///
583    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
584    ///   if `false` null values can not be represented, but we can save an allocation for an
585    ///   indicator buffer.
586    pub fn f32(nullable: bool) -> Self {
587        BindParamDesc {
588            buffer_desc: BufferDesc::F32 { nullable },
589            data_type: DataType::Real,
590        }
591    }
592
593    /// A description for binding `u8` values to a parameter.
594    ///
595    /// # Parameters
596    ///
597    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
598    ///   if `false` null values can not be represented, but we can save an allocation for an
599    ///   indicator buffer.
600    pub fn u8(nullable: bool) -> Self {
601        BindParamDesc {
602            buffer_desc: BufferDesc::U8 { nullable },
603            // Few databases support unsigned types, binding U8 as tiny int might lead to weird
604            // stuff if the database has type is signed. I guess. Let's bind it as SmallInt by
605            // default, just to be on the safe side.
606            data_type: DataType::SmallInt,
607        }
608    }
609
610    /// A description for binding `u8` values to a parameter.
611    ///
612    /// # Parameters
613    ///
614    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
615    ///   if `false` null values can not be represented, but we can save an allocation for an
616    ///   indicator buffer.
617    pub fn i8(nullable: bool) -> Self {
618        BindParamDesc {
619            buffer_desc: BufferDesc::I8 { nullable },
620            data_type: DataType::TinyInt,
621        }
622    }
623
624    /// A description for binding [`crate::sys::Date`] values to a parameter.
625    ///
626    /// # Parameters
627    ///
628    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
629    ///   if `false` null values can not be represented, but we can save an allocation for an
630    ///   indicator buffer.
631    pub fn date(nullable: bool) -> Self {
632        BindParamDesc {
633            buffer_desc: BufferDesc::Date { nullable },
634            data_type: DataType::Date,
635        }
636    }
637
638    /// A description for binding [`crate::Bit`] values to a parameter.
639    ///
640    /// # Parameters
641    ///
642    /// * `nullable`: Whether the parameter can be NULL. If `true` null values can be represented,
643    ///   if `false` null values can not be represented, but we can save an allocation for an
644    ///   indicator buffer.
645    pub fn bit(nullable: bool) -> Self {
646        BindParamDesc {
647            buffer_desc: BufferDesc::Bit { nullable },
648            data_type: DataType::Bit,
649        }
650    }
651
652    fn make_input_buffer(&self, max_rows: usize) -> WithDataType<AnyBuffer> {
653        let buffer = AnyBuffer::from_desc(max_rows, self.buffer_desc);
654        WithDataType::new(buffer, self.data_type)
655    }
656}
657
658impl<S> ResultSetMetadata for Prepared<S> where S: AsStatementRef {}
659
660impl<S> AsStatementRef for Prepared<S>
661where
662    S: AsStatementRef,
663{
664    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
665        self.statement.as_stmt_ref()
666    }
667}
668
669#[cfg(test)]
670mod tests {
671    use crate::buffers::BufferDesc;
672
673    use super::BindParamDesc;
674
675    #[test]
676    fn bind_paramater_description_decimal_length_positive_scale() {
677        let BufferDesc::Text { max_str_len } = BindParamDesc::decimal_as_text(5, 2).buffer_desc
678        else {
679            panic!("Expected a text buffer for decimal parameters.")
680        };
681        assert_eq!("-123.45".len(), max_str_len);
682    }
683
684    #[test]
685    fn bind_paramater_description_decimal_length_scale_zero() {
686        let BufferDesc::Text { max_str_len } = BindParamDesc::decimal_as_text(5, 0).buffer_desc
687        else {
688            panic!("Expected a text buffer for decimal parameters.")
689        };
690        assert_eq!("-12345".len(), max_str_len);
691    }
692
693    #[test]
694    fn bind_paramater_description_decimal_length_negative_scale() {
695        let BufferDesc::Text { max_str_len } = BindParamDesc::decimal_as_text(5, -2).buffer_desc
696        else {
697            panic!("Expected a text buffer for decimal parameters.")
698        };
699        assert_eq!("-123".len(), max_str_len);
700    }
701}