odbc_api/parameter.rs
1//! # Passing parameters to statement
2//!
3//! ## In a nutshell
4//!
5//! * `()` -> No parameter
6//! * `&a` -> Single input parameter
7//! * `InOut(&mut a)` -> Input Output parameter
8//! * `Out(&mut a)` -> Output parameter
9//! * `(a,b,c)` -> Fixed number of parameters
10//! * `&[a]` -> Arbitrary number of parameters
11//! * `&mut BlobParam` -> Stream long input parameters.
12//! * `Box<dyn InputParameter>` -> Arbitrary input parameter
13//! * `&[Box<dyn InputParameter>]` -> Arbitrary number of arbitrary input parameters
14//! * `a.into_parameter()` -> Convert idiomatic Rust type into something bindable by ODBC.
15//!
16//! ## Passing a single parameter
17//!
18//! ODBC allows you to bind parameters to positional placeholders. In the simples case it looks like
19//! this:
20//!
21//! ```no_run
22//! use odbc_api::{Environment, ConnectionOptions};
23//!
24//! let env = Environment::new()?;
25//!
26//! let mut conn = env.connect(
27//! "YourDatabase", "SA", "My@Test@Password1",
28//! ConnectionOptions::default()
29//! )?;
30//! let query = "SELECT year, name FROM Birthdays WHERE year > ?;";
31//! let year = 1980;
32//! let timeout_sec = None;
33//! if let Some(cursor) = conn.execute(query, &year, timeout_sec)? {
34//! // Use cursor to process query results.
35//! }
36//! # Ok::<(), odbc_api::Error>(())
37//! ```
38//!
39//! Please see the documentation of [`InputParameter`] for a list of types that can be bound to a
40//! placeholder.
41//!
42//! ## Annotating a parameter with an explicit SQL DataType
43//!
44//! In the last example we used a bit of domain knowledge about the query and provided it with an
45//! `i32`. Each `Parameter` type comes with a default SQL Type as which it is bound. In the last
46//! example this spared us from specifing that we bind `year` as an SQL `INTEGER` (because `INTEGER`
47//! is default for `i32`). If we want to, we can specify the SQL type independent from the Rust type
48//! we are binding, by wrapping it in `WithDataType`.
49//!
50//! ```no_run
51//! use odbc_api::{Environment, ConnectionOptions, DataType, parameter::WithDataType};
52//! use std::num::NonZeroUsize;
53//!
54//! let env = Environment::new()?;
55//!
56//! let mut conn = env.connect(
57//! "YourDatabase", "SA", "My@Test@Password1",
58//! ConnectionOptions::default()
59//! )?;
60//! let query = "SELECT year, name FROM Birthdays WHERE year > ?;";
61//! let year = WithDataType{
62//! value: 1980,
63//! data_type: DataType::Varchar {length: NonZeroUsize::new(4) }
64//! };
65//! let timeout_sec = None;
66//! if let Some(cursor) = conn.execute(query, &year, timeout_sec)? {
67//! // Use cursor to process query results.
68//! }
69//! # Ok::<(), odbc_api::Error>(())
70//! ```
71//!
72//! In that case it is likely that the driver manager converts our annotated year into a string
73//! which is most likely being converted back into an integer by the driver. All this converting can
74//! be confusing, but it is helpful if we do not know what types the parameters actually have (i.e.
75//! the query could have been entered by the user on the command line.). There is also an option to
76//! query the parameter types beforehand, but my advice is not trust the information blindly if you
77//! cannot test this with your driver beforehand.
78//!
79//! ## Passing a fixed number of parameters
80//!
81//! To pass multiple but a fixed number of parameters to a query you can use tuples.
82//!
83//! ```no_run
84//! use odbc_api::{Environment, ConnectionOptions};
85//!
86//! let env = Environment::new()?;
87//!
88//! let mut conn = env.connect(
89//! "YourDatabase", "SA", "My@Test@Password1",
90//! ConnectionOptions::default()
91//! )?;
92//! let too_old = 1980;
93//! let too_young = 2000;
94//! let timeout_sec = None;
95//! if let Some(cursor) = conn.execute(
96//! "SELECT year, name FROM Birthdays WHERE ? < year < ?;",
97//! (&too_old, &too_young),
98//! timeout_sec,
99//! )? {
100//! // Use cursor to congratulate only persons in the right age group...
101//! }
102//! # Ok::<(), odbc_api::Error>(())
103//! ```
104//!
105//! ## Passing an arbitrary number of parameters
106//!
107//! Not always do we know the number of required parameters at compile time. This might be the case
108//! if the query itself is generated from user input. Luckily slices of parameters are supported, too.
109//!
110//! ```no_run
111//! use odbc_api::{Environment, ConnectionOptions};
112//!
113//! let env = Environment::new()?;
114//!
115//! let mut conn = env.connect(
116//! "YourDatabase", "SA", "My@Test@Password1",
117//! ConnectionOptions::default()
118//! )?;
119//! let query = "SELECT year, name FROM Birthdays WHERE ? < year < ?;";
120//! let params = [1980, 2000];
121//! let timeout_sec = None;
122//! if let Some(cursor) = conn.execute(query, ¶ms[..], timeout_sec)?
123//! {
124//! // Use cursor to process query results.
125//! }
126//! # Ok::<(), odbc_api::Error>(())
127//! ```
128//!
129//! ## Passing an input parameters parsed from the command line
130//!
131//! In case you want to read parameters from the command line you can also let ODBC do the work of
132//! converting the text input into something more suitable.
133//!
134//! ```
135//! use odbc_api::{Connection, IntoParameter, Error, parameter::VarCharSlice};
136//!
137//! fn execute_arbitrary_command(connection: &Connection, query: &str, parameters: &[&str])
138//! -> Result<(), Error>
139//! {
140//! // Convert all strings to `VarCharSlice` and bind them as `VarChar`. Let ODBC convert them
141//! // into something better matching the types required be the query.
142//! let params: Vec<_> = parameters
143//! .iter()
144//! .map(|param| param.into_parameter())
145//! .collect();
146//!
147//! let timeout_sec = None;
148//! // Execute the query as a one off, and pass the parameters. String parameters are parsed and
149//! // converted into something more suitable by the data source itself.
150//! connection.execute(&query, params.as_slice(), timeout_sec)?;
151//! Ok(())
152//! }
153//! ```
154//!
155//! Should you have more type information the type available, but only at runtime can also bind an
156//! array of `[Box<dyn InputParameter]`.
157//!
158//! ## Output and Input/Output parameters
159//!
160//! Mutable references are treated as input/output parameters. To use a parameter purely as an
161//! output parameter you may wrapt it into out. Consider a Mircosoft SQL Server with the following
162//! stored procedure:
163//!
164//! ```mssql
165//! CREATE PROCEDURE TestParam
166//! @OutParm int OUTPUT
167//! AS
168//! SELECT @OutParm = @OutParm + 5
169//! RETURN 99
170//! GO
171//! ```
172//!
173//! We bind the return value as the first output parameter. The second parameter is an input/output
174//! bound as a mutable reference.
175//!
176//! ```no_run
177//! use odbc_api::{Environment, ConnectionOptions, Out, InOut, Nullable};
178//!
179//! let env = Environment::new()?;
180//!
181//! let mut conn = env.connect(
182//! "YourDatabase", "SA", "My@Test@Password1",
183//! ConnectionOptions::default()
184//! )?;
185//!
186//! let mut ret = Nullable::<i32>::null();
187//! let mut param = Nullable::<i32>::new(7);
188//!
189//! conn.execute(
190//! "{? = call TestParam(?)}",
191//! (Out(&mut ret), InOut(&mut param)),
192//! None)?;
193//!
194//! assert_eq!(Some(99), ret.into_opt());
195//! assert_eq!(Some(7 + 5), param.into_opt());
196//!
197//! # Ok::<(), odbc_api::Error>(())
198//! ```
199//!
200//! ## Sending long data
201//!
202//! Many ODBC drivers have size limits of how big parameters can be. Apart from that you may not
203//! want to allocate really large buffers in your application in order to keep a small memory
204//! footprint. Luckily ODBC also supports streaming data to the database batch by batch at statement
205//! execution time. To support this, this crate offers the [`BlobParam`], which can be bound as a
206//! mutable reference. An instance of [`BlobParam`] is usually created by calling
207//! [`Blob::as_blob_param`] from a wrapper implenting [`Blob`].
208//!
209//! ### Inserting long binary data from a file.
210//!
211//! [`BlobRead::from_path`] is the most convinient way to turn a file path into a [`Blob`]
212//! parameter. The following example also demonstrates that the streamed blob parameter can be
213//! combined with regular input parmeters like `id`.
214//!
215//! ```
216//! use std::{error::Error, path::Path};
217//! use odbc_api::{Connection, parameter::{Blob, BlobRead}, IntoParameter};
218//!
219//! fn insert_image_to_db(
220//! conn: &Connection<'_>,
221//! id: &str,
222//! image_path: &Path) -> Result<(), Box<dyn Error>>
223//! {
224//! let mut blob = BlobRead::from_path(&image_path)?;
225//!
226//! let sql = "INSERT INTO Images (id, image_data) VALUES (?, ?)";
227//! let parameters = (&id.into_parameter(), &mut blob.as_blob_param());
228//! conn.execute(sql, parameters, None)?;
229//! Ok(())
230//! }
231//! ```
232//!
233//! ### Inserting long binary data from any `io::BufRead`.
234//!
235//! This is more flexible than inserting just from files. Note however that files provide metadata
236//! about the length of the data, which `io::BufRead` does not. This is not an issue for most
237//! drivers, but some can perform optimization if they know the size in advance. In the tests
238//! SQLite has shown a bug to only insert empty data if no size hint has been provided.
239//!
240//! ```
241//! use std::io::BufRead;
242//! use odbc_api::{Connection, parameter::{Blob, BlobRead}, IntoParameter, Error};
243//!
244//! fn insert_image_to_db(
245//! conn: &Connection<'_>,
246//! id: &str,
247//! image_data: impl BufRead) -> Result<(), Error>
248//! {
249//! const MAX_IMAGE_SIZE: usize = 4 * 1024 * 1024;
250//! let mut blob = BlobRead::with_upper_bound(image_data, MAX_IMAGE_SIZE);
251//!
252//! let sql = "INSERT INTO Images (id, image_data) VALUES (?, ?)";
253//! let parameters = (&id.into_parameter(), &mut blob.as_blob_param());
254//! conn.execute(sql, parameters, None)?;
255//! Ok(())
256//! }
257//! ```
258//!
259//! ### Inserting long strings
260//!
261//! This example insert `title` as a normal input parameter but streams the potentially much longer
262//! `String` in `text` to the database as a large text blob. This allows to circumvent the size
263//! restrictions for `String` arguments of many drivers (usually around 4 or 8 KiB).
264//!
265//! ```
266//! use odbc_api::{Connection, parameter::{Blob, BlobSlice}, IntoParameter, Error};
267//!
268//! fn insert_book(
269//! conn: &Connection<'_>,
270//! title: &str,
271//! text: &str
272//! ) -> Result<(), Error>
273//! {
274//! let mut blob = BlobSlice::from_text(text);
275//!
276//! let insert = "INSERT INTO Books (title, text) VALUES (?,?)";
277//! let parameters = (&title.into_parameter(), &mut blob.as_blob_param());
278//! conn.execute(&insert, parameters, None)?;
279//! Ok(())
280//! }
281//! ```
282//!
283//! ### Inserting long binary data from `&[u8]`.
284//!
285//! ```
286//! use odbc_api::{Connection, parameter::{Blob, BlobSlice}, IntoParameter, Error};
287//!
288//! fn insert_image(
289//! conn: &Connection<'_>,
290//! id: &str,
291//! image_data: &[u8]
292//! ) -> Result<(), Error>
293//! {
294//! let mut blob = BlobSlice::from_byte_slice(image_data);
295//!
296//! let insert = "INSERT INTO Images (id, image_data) VALUES (?,?)";
297//! let parameters = (&id.into_parameter(), &mut blob.as_blob_param());
298//! conn.execute(&insert, parameters, None)?;
299//! Ok(())
300//! }
301//! ```
302//!
303//! ## Passing the type you absolutely think should work, but does not.
304//!
305//! Sadly not every type can be safely bound as something the ODBC C-API understands. Most prominent
306//! among those is a Rust string slice (`&str`).
307//!
308//! ```no_run
309//! use odbc_api::{Environment, ConnectionOptions};
310//!
311//! let env = Environment::new()?;
312//!
313//! let mut conn = env.connect(
314//! "YourDatabase", "SA", "My@Test@Password1",
315//! ConnectionOptions::default()
316//! )?;
317//! // conn.execute("SELECT year FROM Birthdays WHERE name=?;", "Bernd")?; // <- compiler error.
318//! # Ok::<(), odbc_api::Error>(())
319//! ```
320//!
321//! Alas, not all is lost. We can still make use of the [`crate::IntoParameter`] trait to convert it
322//! into something that works.
323//!
324//! ```no_run
325//! use odbc_api::{Environment, IntoParameter, ConnectionOptions};
326//!
327//! let env = Environment::new()?;
328//!
329//! let mut conn = env.connect(
330//! "YourDatabase", "SA", "My@Test@Password1",
331//! ConnectionOptions::default()
332//! )?;
333//! if let Some(cursor) = conn.execute(
334//! "SELECT year FROM Birthdays WHERE name=?;",
335//! &"Bernd".into_parameter(),
336//! None)?
337//! {
338//! // Use cursor to process query results.
339//! };
340//! # Ok::<(), odbc_api::Error>(())
341//! ```
342//!
343//! Conversion for `&str` is not too expensive either. Just an integer more on the stack. Wait, the
344//! type you wanted to use, but that I have conveniently not chosen in this example still does not
345//! work? Well, in that case please open an issue or a pull request. [`crate::IntoParameter`] can usually be
346//! implemented entirely in safe code, and is a suitable spot to enable support for your custom
347//! types.
348mod blob;
349mod c_string;
350mod varcell;
351
352pub use self::{
353 blob::{Blob, BlobParam, BlobRead, BlobSlice},
354 varcell::{
355 Binary, Text, VarBinary, VarBinaryArray, VarBinaryBox, VarBinarySlice, VarBinarySliceMut,
356 VarCell, VarChar, VarCharArray, VarCharBox, VarCharSlice, VarCharSliceMut, VarKind,
357 VarWCharArray, VarWCharBox, VarWCharSlice, VarWCharSliceMut, WideText,
358 },
359};
360
361use std::ffi::c_void;
362
363use odbc_sys::CDataType;
364
365use crate::{
366 DataType,
367 fixed_sized::Pod,
368 handles::{CData, CDataMut, HasDataType},
369};
370
371/// A CData representing a single value rather than an entire buffer of a range of values.
372///
373/// # Safety
374///
375/// Considerations for implementers
376///
377/// Callers must be able to rely on all pointers being valid, i.e. the "range" is not empty.
378///
379/// Since the indicator provided by implementation is used to indicate the length of the value in
380/// the buffer, care must be taken to prevent out of bounds access in case the implementation also
381/// is used as an output parameter, and contains truncated values (i.e. the indicator is longer than
382/// the buffer and the value within).
383pub unsafe trait CElement: CData {
384 /// Must panic if the parameter is not complete. I.e. the indicator of a variable length
385 /// parameter indicates a value larger than what is present in the value buffer.
386 ///
387 /// This is used to prevent using truncacted values as input buffers, which could cause
388 /// inserting invalid memory with drivers which just copy values for the length of the indicator
389 /// buffer without checking the length of the target buffer first. The ODBC standard is
390 /// inconclusive wether the driver has to check for this or not. So we need to check this. We
391 /// can not manifest this as an invariant expressed by a type for all cases, due to the
392 /// existence of input/output parameters.
393 fn assert_completness(&self);
394}
395
396/// Can be used to fill in a field value indicated by a placeholder (`?`) then executing an SQL
397/// statement.
398///
399/// # Supported input parameters
400///
401/// To bind a parameter its memory layout must be known to the ODBC driver. For some C-Types this is
402/// defined in the ODBC standard. Their rust equivalents are:
403///
404/// - f32
405/// - f64
406/// - i8
407/// - i16
408/// - i32
409/// - i64
410/// - u8
411/// - u16
412/// - u32
413/// - u64
414/// - Box<dyn InputParameter>
415/// - CString
416/// - CStr
417/// - [`Date`](crate::sys::Date)
418/// - [`VarCharSlice`]
419/// - [`VarCharSliceMut`]
420/// - [`VarCharArray`]
421/// - [`VarCharBox`]
422/// - [`VarWCharSlice`]
423/// - [`VarWCharSliceMut`]
424/// - [`VarWCharBox`]
425/// - [`VarBinarySlice`]
426/// - [`VarBinarySliceMut`]
427/// - [`VarBinaryArray`]
428/// - [`VarBinaryBox`]
429///
430/// Some types have a memory layout known to the ODBC driver, but additional type information is
431/// required to bind them as parameters. You can use these together with [`WithDataType`]:
432///
433/// - [`Timestamp`](crate::sys::Timestamp)
434/// - [`Time`](crate::sys::Time)
435/// - [`Numeric`](crate::sys::Numeric)*
436///
437/// * Note that the you may need to set the desired precision in the ADR record of the statement
438/// using a descriptor handle in order to bind a `Numeric` value which does not have scale `0`.
439/// This is currently not supported in safe code. Consider binding large numeric values as Text.
440///
441/// # Support for idiomatic Rust types
442///
443/// Idomatic Rust types are supported through the [`IntoParameter`](crate::IntoParameter) trait.
444/// This trait can also be implemented for custom types in safe code.
445pub trait InputParameter: HasDataType + CElement {}
446
447impl<T> InputParameter for T where T: CElement + HasDataType + ?Sized {}
448
449/// # Safety
450///
451/// Guarantees that there is space in the output buffer for at least one element.
452pub unsafe trait OutputParameter: CDataMut + HasDataType {}
453
454/// Wraps a mutable reference. Use this wrapper in order to indicate that a mutable reference should
455/// be bound as an input / output parameter.
456///
457/// # Example
458///
459/// ```no_run
460/// use odbc_api::{Environment, Out, InOut, Nullable, ConnectionOptions};
461///
462/// let env = Environment::new()?;
463///
464/// let mut conn = env.connect(
465/// "YourDatabase", "SA", "My@Test@Password1",
466/// ConnectionOptions::default()
467/// )?;
468///
469/// let mut ret = Nullable::<i32>::null();
470/// let mut param = Nullable::new(7);
471///
472/// conn.execute(
473/// "{? = call TestParam(?)}",
474/// (Out(&mut ret), InOut(&mut param)),
475/// None,
476/// )?;
477///
478/// # Ok::<(), odbc_api::Error>(())
479/// ```
480pub struct InOut<'a, T>(pub &'a mut T);
481
482/// Use this to warp a mutable reference to an [`OutputParameter`]. This will cause the argument to
483/// be considered an output parameter only. Without this wrapper it would be considered an input
484/// parameter. You can use [`InOut`] if you want to indicate that the argument is an input and an
485/// output parameter.
486///
487/// # Example
488///
489/// ```no_run
490/// use odbc_api::{Environment, Out, InOut, Nullable, ConnectionOptions};
491///
492/// let env = Environment::new()?;
493///
494/// let mut conn = env.connect(
495/// "YourDatabase", "SA", "My@Test@Password1",
496/// ConnectionOptions::default(),
497/// )?;
498///
499/// let mut ret = Nullable::<i32>::null();
500/// let mut param = Nullable::new(7);
501///
502/// conn.execute(
503/// "{? = call TestParam(?)}",
504/// (Out(&mut ret), InOut(&mut param)),
505/// None)?;
506///
507/// # Ok::<(), odbc_api::Error>(())
508/// ```
509pub struct Out<'a, T>(pub &'a mut T);
510
511/// Annotates an instance of an inner type with an SQL Data type in order to indicate how it should
512/// be bound as a parameter to an SQL Statement.
513///
514/// # Example
515///
516/// ```no_run
517/// use odbc_api::{Environment, ConnectionOptions, DataType, parameter::WithDataType};
518/// use std::num::NonZeroUsize;
519///
520/// let env = Environment::new()?;
521///
522/// let mut conn = env.connect(
523/// "YourDatabase", "SA", "My@Test@Password1",
524/// ConnectionOptions::default()
525/// )?;
526/// // Bind year as VARCHAR(4) rather than integer.
527/// let year = WithDataType{
528/// value: 1980,
529/// data_type: DataType::Varchar {length: NonZeroUsize::new(4)}
530/// };
531/// let maybe_cursor = conn.execute(
532/// "SELECT year, name FROM Birthdays WHERE year > ?;",
533/// &year,
534/// None
535/// )?;
536/// if let Some(cursor) = maybe_cursor {
537/// // Use cursor to process query results.
538/// }
539/// # Ok::<(), odbc_api::Error>(())
540/// ```
541///
542/// Can also be used to wrap [`crate::sys::Timestamp`] so they implement [`OutputParameter`].
543///
544/// ```no_run
545/// # use odbc_api::{
546/// # Connection, Cursor, DataType, parameter::WithDataType, IntoParameter, sys::Timestamp
547/// # };
548/// # fn given(cursor: &mut impl Cursor, connection: Connection<'_>) {
549/// let mut ts = WithDataType {
550/// value: Timestamp::default(),
551/// data_type: DataType::Timestamp { precision: 0 },
552/// };
553/// connection.execute(
554/// "INSERT INTO Posts (text, timestamps) VALUES (?,?)",
555/// (&"Hello".into_parameter(), &ts.into_parameter()),
556/// None,
557/// );
558/// # }
559/// ```
560#[derive(Debug)]
561pub struct WithDataType<T> {
562 /// Value to wrap with a Data Type. Should implement [`crate::handles::CData`], to be useful.
563 pub value: T,
564 /// The SQL type this value is supposed to map onto. What exactly happens with this information
565 /// is up to the ODBC driver in use.
566 pub data_type: DataType,
567}
568
569unsafe impl<T> CData for WithDataType<T>
570where
571 T: CData,
572{
573 fn cdata_type(&self) -> CDataType {
574 self.value.cdata_type()
575 }
576
577 fn indicator_ptr(&self) -> *const isize {
578 self.value.indicator_ptr()
579 }
580
581 fn value_ptr(&self) -> *const c_void {
582 self.value.value_ptr()
583 }
584
585 fn buffer_length(&self) -> isize {
586 self.value.buffer_length()
587 }
588}
589
590unsafe impl<T> CDataMut for WithDataType<T>
591where
592 T: CDataMut,
593{
594 fn mut_indicator_ptr(&mut self) -> *mut isize {
595 self.value.mut_indicator_ptr()
596 }
597
598 fn mut_value_ptr(&mut self) -> *mut c_void {
599 self.value.mut_value_ptr()
600 }
601}
602
603impl<T> HasDataType for WithDataType<T> {
604 fn data_type(&self) -> DataType {
605 self.data_type
606 }
607}
608
609unsafe impl<T> CElement for WithDataType<T>
610where
611 T: CElement,
612{
613 fn assert_completness(&self) {
614 self.value.assert_completness()
615 }
616}
617unsafe impl<T> OutputParameter for WithDataType<T> where T: Pod {}
618
619// Allow for input parameters whose type is only known at runtime.
620unsafe impl CData for Box<dyn InputParameter> {
621 fn cdata_type(&self) -> CDataType {
622 self.as_ref().cdata_type()
623 }
624
625 fn indicator_ptr(&self) -> *const isize {
626 self.as_ref().indicator_ptr()
627 }
628
629 fn value_ptr(&self) -> *const c_void {
630 self.as_ref().value_ptr()
631 }
632
633 fn buffer_length(&self) -> isize {
634 self.as_ref().buffer_length()
635 }
636}
637
638impl HasDataType for Box<dyn InputParameter> {
639 fn data_type(&self) -> DataType {
640 self.as_ref().data_type()
641 }
642}
643unsafe impl CElement for Box<dyn InputParameter> {
644 fn assert_completness(&self) {
645 self.as_ref().assert_completness()
646 }
647}