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