Skip to content

Latest commit

 

History

History
172 lines (146 loc) · 5.92 KB

README.md

File metadata and controls

172 lines (146 loc) · 5.92 KB

oci_rs

oci_rs provides a Rust wrapper to the Oracle Call Interface (OCI) library. The Oracle site describes OCI as a "...comprehensive, high performance, native C language interface to Oracle Database for custom or packaged applications...".

Documentation is available here.

Setup

This crate is developed against version 11.1 of the OCI library. It is expected to work with any newer version. The OCI client library needs to be installed on your machine and can be downloaded here.

If you are on Linux then you are likely to need to tell the linker where to find the files. Adding this to my .bashrc file worked for me, however the details may vary according to your distro, for openSuse and Redhat/Oracle Enterprise Linux 7.

export LIBRARY_PATH=$LIBRARY_PATH:/usr/lib/oracle/[oci_version]/client64/lib/

This crate has not been tested against Windows and so the setup will be different.

Testing has been done against a local installation of Oracle 11g and 12g. In order to run the crate tests then a local database needs to be available on localhost:1521/xe with a user oci_rs and password test.

In order to use oci_rs add this to your Cargo.toml:

[dependencies]
oci_rs = "0.5.0"

and this to your crate root:

extern crate oci_rs;

Examples

In the following example we will create a connection to a database and then create a table, insert a couple of rows using bind variables and then execute a query to fetch them back again. There is a lot of error handling needed. Every OCI function call can fail and so Result and Option are used extensively. The below code takes the usual documentation shortcut of calling unwrap() a lot but doing so in real client code will prove ill-fated. Any remote database connection is inherently unreliable.

use oci_rs::connection::Connection;

let conn = Connection::new("localhost:1521/xe", "oci_rs", "test").unwrap();

// Create a table
let sql_create = "CREATE TABLE Toys (ToyId int,
                                     Name varchar(20),
                                     Price float)";
let mut create = conn.create_prepared_statement(sql_create).unwrap();

// Execute the create statement
create.execute().unwrap();

// Commit in case we lose connection (an abnormal disconnection would result
// in an automatic roll-back.)
create.commit().unwrap();

// Insert some values using bind variables
let sql_insert = "INSERT INTO Toys (ToyId, Name, Price)
                  VALUES (:id, :name, :price)";
let mut insert = conn.create_prepared_statement(sql_insert).unwrap();

let values = [(1, "Barbie", 23.45),
              (2, "Dinosaurs", -5.21)];

// Run through the list of values, bind them and execute the statement
for value in values.iter() {
    insert.bind(&[&value.0, &value.1, &value.2]).unwrap();
    insert.execute().unwrap()
}

insert.commit().unwrap();

{
    // create a select query
    let sql = "SELECT * FROM AM_USER_ROLE";
    let mut select = match conn.create_prepared_statement(sql)
    {
        Ok(select) => select,
        Err(err) => panic!("failed to create select statement {}", err),
    };
    // get optional result, which is result set for select query
    let optional_rs = select.execute().unwrap();
    if let Some(mut rs) = optional_rs {
        {
            let columns = rs.columns().unwrap();
            for column in columns {
                print!("{},", column.name());
            }
            println!();
        }
        while let Some(row) = rs.next() {
            for cell in row.unwrap().cells()
            {
                match String::from_sql_value(&cell)
                {
                    Some(vstr) => print!("{},", vstr),
                    None => print!("unsupported_value_type"),
                }
            }
            println!();
        }
    }
}

{
    // create a same query but get the result set in another way
    let sql = "SELECT * FROM AM_USER_ROLE";
    let mut select = match conn.create_prepared_statement(sql)
    {
        Ok(select) => select,
        Err(err) => panic!("failed to create select statement {}", err),
    };
    // get the result set
    let optional_rs = select.execute().unwrap();
    if let Some(mut rs) = optional_rs {
        {
            let columns = rs.columns().unwrap();
            for column in columns {
                print!("{},", column.name());
            }
            println!();
        }
        // get all the result rows at once
        let rows_result: Result<Vec<_>, OciError> = rs.collect();
        match rows_result {
            Ok(rows) => for row in rows {
                for cell in row.cells()
                {
                    match String::from_sql_value(&cell)
                    {
                        Some(vstr) => print!("{},", vstr),
                        None => print!("unsupported_value_type"),
                    }
                }
                /*
                 if you know the type of column value,
                 you also can use something like:
                 let id: i64 = first_row[0].value().unwrap();
                 let name: String = first_row[1].value().unwrap();
                 let price: f64 = first_row[2].value().unwrap();
                 */
                println!();
            },
            Err(err) => panic!("failed to get rows: {}", err),
        }
    }
}