#![warn(missing_docs)]
extern crate byteorder;
extern crate chrono;
extern crate libc;
pub mod connection;
pub mod oci_error;
pub mod types;
pub mod row;
mod common;
mod oci_bindings;
pub mod statement;
#[cfg(test)]
mod tests {
use chrono::{Date, DateTime, FixedOffset, TimeZone, Timelike, Utc};
use connection::Connection;
use oci_error::OciError;
const CONNECTION: &str = "localhost:1521/xe";
const BAD_CONNECTION: &str = "localhost:1521/xp";
const USER: &str = "oci_rs";
const PASSWORD: &str = "test";
const BAD_PASSWORD: &str = "toast";
#[test]
#[allow(unused_variables)]
fn create_connection() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
}
#[test]
#[allow(unused_variables)]
#[should_panic]
fn create_connection_with_bad_password() {
let conn = match Connection::new(CONNECTION, USER, BAD_PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
}
#[test]
#[allow(unused_variables)]
fn create_connection_with_bad_connection_string() {
let error = match Connection::new(BAD_CONNECTION, USER, PASSWORD) {
Ok(conn) => panic!("Should not have been able to create a connection, test is wrong."),
Err(err) => err,
};
let code = match error {
OciError::Oracle(ref error_record) => &error_record.error_records()[0].0,
OciError::Conversion(_) => {
panic!("Should not have found a conversion error, test is wrong.")
}
};
let tns_listener_error: i32 = 12514;
assert_eq!(&tns_listener_error, code)
}
#[test]
fn create_prepared_statement() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Books";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(s) => s,
Err(err) => panic!("Failed to prepare drop Books: {}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Books(BookID int, Name varchar(200))";
let mut stmt = match conn.create_prepared_statement(sql_create) {
Ok(s) => s,
Err(err) => panic!("Failed to create a statement: {}", err),
};
if let Err(err) = stmt.execute() {
panic!("Failed to execute: {}", err)
}
if let Err(err) = stmt.commit() {
panic!("Failed to commit: {}", err)
}
}
#[test]
fn bind() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Fruit";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(s) => s,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Fruit(FruitId integer, Name varchar(20))";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Fruit(FruitId, Name) VALUES(:id, :name)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
let name = String::from("Apple");
let id: i64 = 22;
if let Err(err) = insert.bind(&[&id, &name]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
let name = String::from("Pear");
let id: i64 = 23;
if let Err(err) = insert.bind(&[&id, &name]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
if let Err(err) = insert.bind(&[&24, &"Banana".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
if let Err(err) = insert.commit() {
panic!("{}", err)
}
}
#[test]
fn query() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Cars";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(s) => s,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Cars(CarId integer, Name varchar(20))";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Cars(CarId, Name) VALUES('12', 'BMW')";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = insert.execute() {
panic!("{}", err)
}
let sql_query = "SELECT * FROM Cars";
let mut select = match conn.create_prepared_statement(sql_query) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = select.execute() {
panic!("{}", err)
}
let result_set = match select.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let row = &result_set[0];
let car_id: i64 = row[0].value().expect("Not an i64");
assert_eq!(car_id, 12);
let car_name: String = row[1].value().expect("Not a string");
assert_eq!(car_name, "BMW")
}
#[test]
fn multi_row_query() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Flowers";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Flowers(FlowerId integer, Name varchar(20))";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Flowers(FlowerId, Name) VALUES(:id, :name)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = insert.bind(&[&1, &"Rose".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
if let Err(err) = insert.bind(&[&2, &"Tulip".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
let sql_query = "SELECT * FROM Flowers";
let mut select = match conn.create_prepared_statement(sql_query) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = select.execute() {
panic!("{}", err)
}
let result_set = match select.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let pairs = [(1, "Rose"), (2, "Tulip")];
for (index, pair) in pairs.iter().enumerate() {
let row = &result_set[index];
let flower_id: i64 = row[0].value().expect("Not an i64");
let flower_name: String = row[1].value().expect("Not a string");
assert_eq!(flower_id, pair.0);
assert_eq!(flower_name, pair.1);
}
}
#[test]
fn multi_row_query_with_prefetch() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Words";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Words(Word varchar(100))";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Words(Word) VALUES(:word)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
let words = ["Podgy", "Rumpy", "Wiggle", "Ponder", "Spice", "Constable"];
for word in words.iter() {
if let Err(err) = insert.bind(&[word]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
}
let sql_query = "SELECT * FROM Words";
let mut select = match conn.create_prepared_statement(sql_query) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = select.execute() {
panic!("{}", err)
}
if let Err(err) = select.set_prefetch(5) {
panic!("{}", err)
}
let result_set = match select.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
for row in result_set {
let word: String = row[0].value().unwrap();
assert!(words.contains(&word.as_ref()));
}
}
#[test]
fn lazy_multi_row_query() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Birds";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Birds(BirdId integer, Name varchar(20))";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Birds(BirdId, Name) VALUES(:id, :name)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = insert.bind(&[&1, &"Chafinch".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
if let Err(err) = insert.bind(&[&2, &"Eagle".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
let sql_query = "SELECT * FROM Birds";
let mut select = match conn.create_prepared_statement(sql_query) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = select.execute() {
panic!("{}", err)
}
let mut result_set = Vec::new();
for row_result in select.lazy_result_set() {
match row_result {
Ok(row) => result_set.push(row),
Err(err) => panic!("{}", err),
}
}
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let pairs = [(1, "Chafinch"), (2, "Eagle")];
for (index, pair) in pairs.iter().enumerate() {
let row = &result_set[index];
let bird_id: i64 = row[0].value().expect("Not an i64");
let bird_name: String = row[1].value().expect("Not a string");
assert_eq!(bird_id, pair.0);
assert_eq!(bird_name, pair.1);
}
}
#[test]
#[should_panic]
fn lazy_multi_row_query_repeat_call() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Fish";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Fish(FishId integer, Name varchar(20))";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Fish(FishId, Name) VALUES(:id, :name)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = insert.bind(&[&1, &"Tuna".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
if let Err(err) = insert.bind(&[&2, &"Salmon".to_string()]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
let sql_query = "SELECT * FROM Fish";
let mut select = match conn.create_prepared_statement(sql_query) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = select.execute() {
panic!("{}", err)
}
let mut result_set = Vec::new();
for row_result in select.lazy_result_set() {
match row_result {
Ok(row) => result_set.push(row),
Err(err) => panic!("{}", err),
}
}
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let pairs = [(1, "Tuna"), (2, "Salmon")];
for (index, pair) in pairs.iter().enumerate() {
let row = &result_set[index];
let bird_id: i64 = row[0].value().expect("Not an i64");
let bird_name: String = row[1].value().expect("Not a string");
assert_eq!(bird_id, pair.0);
assert_eq!(bird_name, pair.1);
}
let mut repeat_result_set = Vec::new();
for row_result in select.lazy_result_set() {
match row_result {
Ok(row) => repeat_result_set.push(row),
Err(err) => panic!("{}", err),
}
}
}
#[test]
fn number_conversion() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Sweets";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Sweets(SweetId integer, Name varchar(20), Price float)";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("{}", err)
}
let sql_insert = "INSERT INTO Sweets(SweetId, Name, Price) VALUES(:id, :name, :price)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
let values = [
(1, "Toffee", 22.5),
(2, "Haribo", -4.0),
(3, "Gobstoppers", 34.5657),
];
for value in values.iter() {
if let Err(err) = insert.bind(&[&value.0, &value.1.to_string(), &value.2]) {
panic!("{}", err)
}
if let Err(err) = insert.execute() {
panic!("{}", err)
}
}
let sql_query = "SELECT * FROM Sweets";
let mut query = match conn.create_prepared_statement(sql_query) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = query.execute() {
panic!("{}", err)
}
let result_set = match query.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
for (index, value) in values.iter().enumerate() {
let row = &result_set[index];
let sweet_id: i64 = row[0].value().expect("Not an i64");
let sweet_name: String = row[1].value().expect("Not a string");
let sweet_price: f64 = match row[2].value() {
Some(p) => p,
None => panic!("{:?}", row[2]),
};
assert_eq!(sweet_id, value.0);
assert_eq!(sweet_name, value.1);
assert_eq!(sweet_price, value.2);
}
}
#[test]
fn date_in_oracle_binary_format() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Birthdays";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Birthdays(id INTEGER,
Name VARCHAR2(200),
Birthday DATE)";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("Couldn't execute create Birthdays: {}", err)
}
let sql_insert = "INSERT INTO Birthdays(Id, Name, Birthday)
VALUES(:id, :name, :birthday)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("Cannot create insert for Birthdays: {}", err),
};
let id = 1;
let name = "Roger";
let birthday = Utc.ymd(2006, 10, 1);
if let Err(err) = insert.bind(&[&id, &name, &birthday]) {
panic!("Cannot bind for insert to Birthdays: {}", err)
}
if let Err(err) = insert.execute() {
panic!("Couldn't execute insert into Birthdays: {}", err)
}
let sql_select = "SELECT * FROM Birthdays";
let mut select = match conn.create_prepared_statement(sql_select) {
Ok(stmt) => stmt,
Err(err) => panic!("Couldn't create select for Birthdays: {}", err),
};
if let Err(err) = select.execute() {
panic!("Couldn't execute select for Birthdays: {}", err)
}
let result_set = match select.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let first_row = &result_set[0];
let date: Date<Utc> = first_row[2].value().unwrap();
assert_eq!(date, birthday);
}
#[test]
fn timezone_conversions() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Times";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Times(TimeId INTEGER,
WorldTime TIMESTAMP(9) WITH TIME ZONE)";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("Couldn't execute create Times: {}", err)
}
let sql_insert = "INSERT INTO Times(TimeId, WorldTime)
VALUES(:id, :time)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("Cannot create insert for Times: {}", err),
};
let utc = Utc.ymd(2018, 01, 05).and_hms(19, 25, 30);
let utc_tz = utc.with_timezone(&FixedOffset::east(0));
let east_5 = utc.with_timezone(&FixedOffset::east(5 * 3600));
let east_5_30 = utc.with_timezone(&FixedOffset::east((5 * 3600) + (30 * 60)));
let west_8 = utc.with_timezone(&FixedOffset::west(8 * 3600));
let west_8_30 = utc.with_timezone(&FixedOffset::west((8 * 3600) + (30 * 60)));
let times = [
(1, utc_tz),
(2, east_5),
(3, east_5_30),
(4, west_8),
(5, west_8_30),
];
for time in times.iter() {
if let Err(err) = insert.bind(&[&time.0, &time.1]) {
panic!("Cannot bind for insert to Times: {}", err)
}
if let Err(err) = insert.execute() {
panic!(
"Couldn't insert id {} tz {} into Times: {}",
time.0, time.1, err
)
}
}
let sql_select = "SELECT WorldTime FROM Times
WHERE TimeId = :id";
let mut select = match conn.create_prepared_statement(sql_select) {
Ok(stmt) => stmt,
Err(err) => panic!("Couldn't create select for Times: {}", err),
};
for time in times.iter() {
if let Err(err) = select.bind(&[&time.0]) {
panic!("Cannot bind for select for Times: {}", err)
}
if let Err(err) = select.execute() {
panic!("Couldn't execute select for Times: {}", err)
}
let result_set = match select.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let first_row = &result_set[0];
let timestamp_tz: DateTime<FixedOffset> = first_row[0].value().unwrap();
assert_eq!(time.1, timestamp_tz);
}
}
#[test]
fn conversions() {
let conn = match Connection::new(CONNECTION, USER, PASSWORD) {
Ok(conn) => conn,
Err(err) => panic!("Failed to create a connection: {}", err),
};
let sql_drop = "DROP TABLE Films";
let mut drop = match conn.create_prepared_statement(sql_drop) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
drop.execute().ok();
let sql_create = "CREATE TABLE Films(FilmId INTEGER,
Name VARCHAR2(200),
Genre CHAR(10),
Released DATE,
LastUpdate TIMESTAMP(9),
LastViewed TIMESTAMP(9) WITH TIME ZONE)";
let mut create = match conn.create_prepared_statement(sql_create) {
Ok(stmt) => stmt,
Err(err) => panic!("{}", err),
};
if let Err(err) = create.execute() {
panic!("Couldn't execute create Films: {}", err)
}
let sql_insert = "INSERT INTO Films(FilmId, Name, Genre, Released, LastUpdate, LastViewed)
VALUES(:id, :name, :genre, :released, :updated, :viewed)";
let mut insert = match conn.create_prepared_statement(sql_insert) {
Ok(stmt) => stmt,
Err(err) => panic!("Cannot create insert for Films: {}", err),
};
let id = 1;
let name = "Guardians of the Galaxy";
let genre = "Sci-fi ";
let released = Utc.ymd(2014, 7, 21);
let updated = Utc::now();
let viewed = updated.with_timezone(&FixedOffset::east((5 * 3600) + 1800));
println!("viewed {}", viewed.hour());
if let Err(err) = insert.bind(&[&id, &name, &genre, &released, &updated, &viewed]) {
panic!("Cannot bind for insert to Films: {}", err)
}
if let Err(err) = insert.execute() {
panic!("Couldn't execute insert into Films: {}", err)
}
let sql_select = "SELECT * FROM Films";
let mut select = match conn.create_prepared_statement(sql_select) {
Ok(stmt) => stmt,
Err(err) => panic!("Couldn't create select for Films: {}", err),
};
if let Err(err) = select.execute() {
panic!("Couldn't execute select for Films: {}", err)
}
let result_set = match select.result_set() {
Ok(res) => res,
Err(err) => panic!("{}", err),
};
if result_set.is_empty() {
panic!("Should not have an empty result")
}
let first_row = &result_set[0];
let genre_char: String = first_row[2].value().unwrap();
assert_eq!(genre_char, genre);
let date: Date<Utc> = first_row[3].value().unwrap();
assert_eq!(date, released);
let date_as_string: String = first_row[3].value().unwrap();
assert_eq!(date_as_string, released.to_string());
let timestamp: DateTime<Utc> = first_row[4].value().unwrap();
assert_eq!(timestamp, updated);
let timestamp_as_string: String = first_row[4].value().unwrap();
assert_eq!(timestamp_as_string, updated.to_string());
let timestamp_tz: DateTime<FixedOffset> = first_row[5].value().unwrap();
assert_eq!(timestamp_tz, viewed);
let timestamp_tz_as_string: String = first_row[5].value().unwrap();
assert_eq!(timestamp_tz_as_string, viewed.to_string());
}
}