odbc-safe 0.3.2

Write low level, fast ODBC Applications in safe Rust.
Documentation
extern crate odbc_sys;
extern crate odbc_safe;

use odbc_safe::*;

#[test]
fn allocate_environment() {
    Environment::new().unwrap();
}

#[test]
fn allocate_connection() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    DataSource::with_parent(&env).unwrap();
}

#[test]
#[should_panic]
fn wrong_datasource() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    let dbc = DataSource::with_parent(&env).unwrap();
    dbc.connect("DoesntExist", "", "").unwrap();
}

#[test]
fn diagnostics() {

    let expected = if cfg!(target_os = "windows") {
        "[Microsoft][ODBC Driver Manager] Data source name not found and no default driver \
         specified"
    } else {
        "[unixODBC][Driver Manager]Data source name not found, and no default driver specified"
    };

    use std::str;

    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();

    let dbc = DataSource::with_parent(&env).unwrap();
    let dbc = dbc.connect("DoesntExist", "", "");
    if let Error(d) = dbc {
        let mut message = [0; 512];
        match d.diagnostics(1, &mut message) {
            ReturnOption::Success(rec) => {
                let message = str::from_utf8(&mut message[..(rec.text_length as usize)]).unwrap();
                assert_eq!(expected, message);
            }
            _ => panic!("Error retriving diagnostics"),
        }
    }
}

#[test]
fn drivers_with_empty_buffer() {
    use odbc_sys::SQL_FETCH_NEXT;
    let env = Environment::new().unwrap();
    let mut env: Environment<Odbc3> = env.declare_version().unwrap();
    let mut description = [0; 0];
    let mut attributes = [0; 0];
    match env.drivers(SQL_FETCH_NEXT, &mut description, &mut attributes) {
        ReturnOption::Error(()) => panic!("SQLDrivers call returned error"),
        _ => (),
    }
}

#[cfg_attr(not(feature = "travis"), ignore)]
#[test]
fn connect_to_postgres() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    let dbc = DataSource::with_parent(&env).unwrap();
    let dbc = dbc.connect("PostgreSQL", "postgres", "");
    match dbc {
        Success(c) => assert_no_diagnostic(&c.disconnect()),
        Info(c) => assert_no_diagnostic(&c),
        Error(c) => assert_no_diagnostic(&c),
    };
}

#[cfg_attr(not(feature = "travis"), ignore)]
#[test]
fn connect_to_postgres_with_connection_string() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    let dbc = DataSource::with_parent(&env).unwrap();
    let dbc = dbc.connect_with_connection_string("DSN=PostgreSQL;UID=postgres");
    match dbc {
        Success(c) => assert_no_diagnostic(&c.disconnect()),
        Info(c) => assert_no_diagnostic(&c),
        Error(c) => assert_no_diagnostic(&c),
    };
}

#[cfg_attr(not(feature = "travis"), ignore)]
#[test]
fn query_result() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    let dbc = DataSource::with_parent(&env).unwrap();
    let dbc = dbc.connect("PostgreSQL", "postgres", "").unwrap();
    {
        let stmt = Statement::with_parent(&dbc).unwrap();
        let stmt = match stmt.exec_direct("SELECT title FROM Movies WHERE year=1968;") {
            ReturnOption::Success(s) | ReturnOption::Info(s) => {
                assert_no_diagnostic(&s);
                s
            }
            ReturnOption::NoData(_) => panic!("No Data"),
            ReturnOption::Error(s) => panic!("{}", get_last_error(&s)),
        };
        assert_eq!(1, stmt.num_result_cols().unwrap());
        let mut stmt = match stmt.fetch() {
            ReturnOption::Success(s) => s,
            ReturnOption::Info(s) => s,
            ReturnOption::Error(s) => panic!("Error during fetching row: {}", get_last_error(&s)),
            ReturnOption::NoData(_) => panic!("Empty result set returned from SELECT"),
        };
        let mut buffer = [0u8; 256];
        if let ReturnOption::Success(Indicator::Length(i)) =
            stmt.get_data(1, &mut buffer as &mut [u8])
        {
            assert_eq!("2001: A Space Odyssey".as_bytes(), &buffer[..(i as usize)]);
        } else {
            panic!("No field found!");
        }
    }
    dbc.disconnect().unwrap();
}

#[cfg_attr(not(feature = "travis"), ignore)]
#[test]
fn auto_disconnect() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    let dbc = DataSource::with_parent(&env).unwrap();
    dbc.connect("PostgreSQL", "postgres", "").unwrap();
    // No panic on Drop, because of automatic disconnect
}

#[cfg_attr(not(feature = "travis"), ignore)]
#[test]
fn not_read_only() {
    let env = Environment::new().unwrap();
    let env: Environment<Odbc3> = env.declare_version().unwrap();
    let dbc = DataSource::with_parent(&env).unwrap();
    let mut dbc = dbc.connect("PostgreSQL", "postgres", "").unwrap();
    assert!(!dbc.is_read_only().unwrap());
}

/// Checks for a diagnstic record. Should one be present this function panics printing the contents
/// of said record.
fn assert_no_diagnostic(diag: &Diagnostics) {
    use std::str;
    let mut buffer = [0; 512];
    match diag.diagnostics(1, &mut buffer) {
        ReturnOption::Success(dr) | ReturnOption::Info(dr) => panic!(
            "{}",
            str::from_utf8(&buffer[0..(dr.text_length as usize)]).unwrap()
        ),
        ReturnOption::Error(()) => panic!("Error during fetching diagnostic record"),
        ReturnOption::NoData(()) => (),
    }
}

fn get_last_error(diag: &Diagnostics) -> String {
    use std::str;
    let mut buffer = [0; 512];
    match diag.diagnostics(1, &mut buffer) {
        ReturnOption::Success(dr) | ReturnOption::Info(dr) => str::from_utf8(
            &buffer[0..(dr.text_length as usize)],
        ).unwrap()
            .to_owned(),
        ReturnOption::Error(()) => panic!("Error during fetching diagnostic record"),
        ReturnOption::NoData(()) => panic!("No diagnostic available"),
    }
}