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