use oracle::sql_type::{FromSql, OracleType, ToSql};
use oracle::{Connection, Result, Row, RowValue, Version};
use std::env;
use std::thread::sleep;
use std::time;
fn env_var_or(env_name: &str, default: &str) -> String {
match env::var_os(env_name) {
Some(env_var) => env_var.into_string().unwrap(),
None => String::from(default),
}
}
pub fn main_user() -> String {
env_var_or("ODPIC_TEST_MAIN_USER", "odpic")
}
pub fn main_password() -> String {
env_var_or("ODPIC_TEST_MAIN_PASSWORD", "welcome")
}
#[allow(dead_code)]
pub fn proxy_user() -> String {
env_var_or("ODPIC_TEST_PROXY_USER", "odpic_proxy")
}
#[allow(dead_code)]
pub fn proxy_password() -> String {
env_var_or("ODPIC_TEST_PROXY_PASSWORD", "welcome")
}
pub fn connect_string() -> String {
env_var_or("ODPIC_TEST_CONNECT_STRING", "localhost/orclpdb")
}
#[allow(dead_code)]
pub fn dir_name() -> String {
env_var_or("ODPIC_TEST_DIR_NAME", "odpic_dir")
}
#[allow(dead_code)]
pub fn connect() -> Result<Connection> {
Connection::connect(&main_user(), &main_password(), &connect_string())
}
#[allow(dead_code)]
pub fn check_oracle_version(
test_name: &str,
conn: &Connection,
major: i32,
minor: i32,
) -> Result<bool> {
let ver = Version::new(major, minor, 0, 0, 0);
let client_ver = Version::client()?;
let server_ver = conn.server_version()?;
if client_ver >= ver && server_ver.0 >= ver {
Ok(true)
} else {
println!(
"Skip {}, which requires an Oracle {}.{} feature.",
test_name,
ver.major(),
ver.minor()
);
Ok(false)
}
}
#[macro_export]
#[allow(unused_macros)]
macro_rules! test_from_sql {
($conn:expr, $column_literal:expr, $column_type:expr, $expected_result:expr) => {
common::test_from_sql(
$conn,
$column_literal,
$column_type,
$expected_result,
file!(),
line!(),
)?;
};
}
#[allow(dead_code)]
pub fn test_from_sql<T>(
conn: &Connection,
column_literal: &str,
column_type: &OracleType,
expected_result: &T,
file: &str,
line: u32,
) -> Result<()>
where
T: FromSql + ::std::fmt::Debug + ::std::cmp::PartialEq,
{
let mut stmt = conn
.statement(&format!("select {} from dual", column_literal))
.build()?;
let mut rows = stmt
.query_as::<T>(&[])
.unwrap_or_else(|_| panic!("error at {}:{}", file, line));
assert_eq!(
rows.column_info()[0].oracle_type(),
column_type,
"called by {}:{}",
file,
line
);
let result = rows.next().unwrap()?;
assert_eq!(&result, expected_result, "called by {}:{}", file, line);
Ok(())
}
#[macro_export]
#[allow(unused_macros)]
macro_rules! test_to_sql {
($conn:expr, $input_data:expr, $input_literal:expr, $expected_result:expr) => {
common::test_to_sql(
$conn,
$input_data,
$input_literal,
$expected_result,
file!(),
line!(),
)?;
};
}
#[allow(dead_code)]
pub fn test_to_sql<T>(
conn: &Connection,
input_data: &T,
input_literal: &str,
expected_result: &str,
file: &str,
line: u32,
) -> Result<()>
where
T: ToSql,
{
let mut stmt = conn
.statement(&format!("begin :out := {}; end;", input_literal))
.build()?;
stmt.bind(1, &OracleType::Varchar2(4000))?;
stmt.bind(2, input_data)?;
stmt.execute(&[])
.unwrap_or_else(|_| panic!("error at {}:{}", file, line));
let result: String = stmt.bind_value(1)?;
assert_eq!(&result, expected_result, "called by {}:{}", file, line);
Ok(())
}
#[allow(dead_code)]
pub struct TestString {
pub int_col: i32,
pub string_col: String,
pub raw_col: Vec<u8>,
pub fixed_char_col: String,
pub nullable_col: Option<String>,
}
impl RowValue for TestString {
fn get(row: &Row) -> Result<TestString> {
Ok(TestString {
int_col: row.get(0)?,
string_col: row.get(1)?,
raw_col: row.get(2)?,
fixed_char_col: row.get(3)?,
nullable_col: row.get(4)?,
})
}
}
type TestStringsTableRow = (
i32,
&'static str,
&'static [u8],
&'static str,
Option<&'static str>,
);
#[allow(dead_code)]
const VALUES_IN_TEST_STRINGS: [TestStringsTableRow; 11] = [
(0, "", b"", "", None),
(
1,
"String 1",
b"Raw 1",
"Fixed Char 1 ",
Some("Nullable 1"),
),
(
2,
"String 2",
b"Raw 2",
"Fixed Char 2 ",
None,
),
(
3,
"String 3",
b"Raw 3",
"Fixed Char 3 ",
Some("Nullable 3"),
),
(
4,
"String 4",
b"Raw 4",
"Fixed Char 4 ",
None,
),
(
5,
"String 5",
b"Raw 5",
"Fixed Char 5 ",
Some("Nullable 5"),
),
(
6,
"String 6",
b"Raw 6",
"Fixed Char 6 ",
None,
),
(
7,
"String 7",
b"Raw 7",
"Fixed Char 7 ",
Some("Nullable 7"),
),
(
8,
"String 8",
b"Raw 8",
"Fixed Char 8 ",
None,
),
(
9,
"String 9",
b"Raw 9",
"Fixed Char 9 ",
Some("Nullable 9"),
),
(
10,
"String 10",
b"Raw 10",
"Fixed Char 10 ",
None,
),
];
#[allow(dead_code)]
pub type TestStringTuple = (i32, String, Vec<u8>, String, Option<String>);
#[allow(dead_code)]
pub fn assert_test_string_type(idx: usize, row: &TestString) {
let row = (
row.int_col,
row.string_col.clone(),
row.raw_col.clone(),
row.fixed_char_col.clone(),
row.nullable_col.clone(),
);
assert_test_string_tuple(idx, &row);
}
#[allow(dead_code)]
pub fn assert_test_string_row(idx: usize, row: &Row) {
let row = row.get_as::<TestStringTuple>().unwrap();
assert_test_string_tuple(idx, &row);
}
#[allow(dead_code)]
pub fn assert_test_string_tuple(idx: usize, row: &TestStringTuple) {
assert_eq!(row.0, VALUES_IN_TEST_STRINGS[idx].0);
assert_eq!(row.1, VALUES_IN_TEST_STRINGS[idx].1);
assert_eq!(row.2, VALUES_IN_TEST_STRINGS[idx].2);
assert_eq!(row.3, VALUES_IN_TEST_STRINGS[idx].3);
assert_eq!(row.4, VALUES_IN_TEST_STRINGS[idx].4.map(|s| s.to_string()));
}
#[allow(dead_code)]
pub fn truncate_table(conn: &Connection, table_name: &str) -> Result<()> {
let sql = format!("truncate table {}", table_name);
let mut retry_count = 0;
loop {
match conn.execute(&sql, &[]) {
Ok(_) => return Ok(()),
Err(err) if retry_count < 3 && err.oci_code() == Some(54) => {
sleep(time::Duration::from_secs(1));
retry_count += 1;
}
Err(err) => return Err(err),
}
}
}