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    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}