[−][src]Module odbc_api::parameter
Passing parameters to statement
In a nutshell
()
-> No parametera
-> Single parameter(a,b,c)
-> Fixed number of parameters&[a]
-> Arbitrary number of parameters- a.into_parameter() -> Convert idiomatic Rust type into something bindable by ODBC.
Passing a single parameter
ODBC allows you to bind parameters to positional placeholders. In the simples case it looks like this:
use odbc_api::Environment; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; let year = 1980; if let Some(cursor) = conn.execute("SELECT year, name FROM Birthdays WHERE year > ?;", year)? { // Use cursor to process query results. }
All types implementing the Parameter
trait can be used.
Annotating a parameter with an explicit SQL DataType
In the last example we used a bit of domain knowledge about the query and provided it with an
i32
. Each Parameter
type comes with a default SQL Type as which it is bound. In the last
example this spared us from specifing that we bind year
as an SQL INTEGER
(because INTEGER
is default for i32
). If we want to, we can specify the SQL type independent from the Rust type
we are binding, by wrapping it in WithDataType
.
use odbc_api::{Environment, parameter::WithDataType, DataType}; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; let year = WithDataType{ value: 1980, data_type: DataType::Varchar {length: 4} }; if let Some(cursor) = conn.execute("SELECT year, name FROM Birthdays WHERE year > ?;", year)? { // Use cursor to process query results. }
In that case it is likely that the driver manager converts our anotated year into a string which is most likely being converted back into an integer by the driver. All this converting can be confusing, but it is helpful if we do not know what types the parameters actually have (i.e. the query could have been entered by the user on the command line.). There is also an option to query the parameter types beforhand, but my advice is not trust the information blindly if you cannot test this with your driver beforehand.
Passing a fixed number of parameters
To pass multiple but a fixed number of parameters to a query you can use tuples.
use odbc_api::Environment; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; let too_old = 1980; let too_young = 2000; if let Some(cursor) = conn.execute("SELECT year, name FROM Birthdays WHERE ? < year < ?;", (too_old, too_young))? { // Use cursor to congratulate only persons in the right age group... }
Passing an abitrary number of parameters
Not always do we know the number of required parameters at compile time. This might be the case if the query itself is generated from user input. Luckily slices of parameters are supported, too.
use odbc_api::Environment; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; let params = [1980, 2000]; if let Some(cursor) = conn.execute( "SELECT year, name FROM Birthdays WHERE ? < year < ?;", ¶ms[..])? { // Use cursor to process query results. }
Passing the type you absolutly think should work, but does not.
Sadly not every type can be safely bound as something the ODBC C-API understands. Most prominent
among those is a Rust string slice (&str
).
use odbc_api::Environment; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; // conn.execute("SELECT year FROM Birthdays WHERE name=?;", "Bernd")?; // <- compiler error.
Passing the type you absolutly think should work, but does not.
Sadly not every type can be safely bound as something the ODBC C-API understands. Most prominent
among those is a Rust string slice (&str
).
use odbc_api::Environment; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; // conn.execute("SELECT year FROM Birthdays WHERE name=?;", "Bernd")?; // <- compiler error.
Alas, not all is lost. We can still make use of the IntoParameter
trait to convert it into
something that works.
use odbc_api::{Environment, IntoParameter}; let env = unsafe { Environment::new()? }; let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; if let Some(cursor) = conn.execute( "SELECT year FROM Birthdays WHERE name=?;", "Bernd".into_parameter())? { // Use cursor to process query results. };
Conversion for &str
is not too expensive either. Just an integer more on the stack. Wait, the
type you wanted to use, but that I have conviniently not chosen in this example still does not
work? Well, in that case please open an issue or a pull request. IntoParameter
can usually be
implemented entirely in safe code, and is a suitable spot to enable support for your custom
types.
Structs
VarChar | Binds a byte array as a VarChar input parameter. |
WithDataType | Annotates an instance of an inner type with an SQL Data type in order to indicate how it should be bound as a parameter to an SQL Statement. |
Traits
Parameter | Extend the input trait with the guarantee, that the bound parameter buffer contains at least one element. |