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