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}