[][src]Module odbc_api::parameter

Passing parameters to statement

In a nutshell

  • () -> No parameter
  • a -> 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 < ?;",
    &params[..])?
{
    // 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.