This is an Oracle database driver for Rust based on ODPI-C.
See ChangeLog.md.
- Rust 1.19 or later
- C compiler. See
Compile-time Requirements
in this document.
- Oracle client 11.2 or later. See ODPI-C installation document.
Put this in your Cargo.toml
:
[dependencies]
oracle = "0.1.0"
When you need to fetch or bind chrono
data types, enable chrono
feature:
[dependencies]
oracle = { version = "0.1.0", features = ["chrono"] }
Then put this in your crate root:
extern crate oracle;
Executes select statements and get rows:
use oracle::{Connection, Error};
// Connect to a database.
let conn = Connection::connect("scott", "tiger", "//localhost/XE", &[])?;
let sql = "select ename, sal, comm from emp where deptno = :1";
// Select a table with a bind variable.
println!("---------------|---------------|---------------|");
let rows = conn.query(sql, &[&30])?;
for row_result in &rows {
let row = row_result?;
// get a column value by position (0-based)
let ename: String = row.get(0)?;
// get a column by name (case-insensitive)
let sal: i32 = row.get("sal")?;
// Use `Option<...>` to get a nullable column.
// Otherwise, `Err(Error::NullValue)` is returned
// for null values.
let comm: Option<i32> = row.get(2)?;
println!(" {:14}| {:>10} | {:>10} |",
ename,
sal,
comm.map_or("".to_string(), |v| v.to_string()));
}
// Another way to fetch rows.
// The rows iterator returns Result<(String, i32, Option<i32>)>.
println!("---------------|---------------|---------------|");
let rows = conn.query_as::<(String, i32, Option<i32>)>(sql, &[&10])?;
for row_result in &rows {
let (ename, sal, comm) = row_result?;
println!(" {:14}| {:>10} | {:>10} |",
ename,
sal,
comm.map_or("".to_string(), |v| v.to_string()));
}
Executes select statements and get the first rows:
use oracle::Connection;
// Connect to a database.
let conn = Connection::connect("scott", "tiger", "//localhost/XE", &[])?;
let sql = "select ename, sal, comm from emp where empno = :1";
// Print the first row.
let row = conn.query_row(sql, &[&7369])?;
let ename: String = row.get("empno")?;
let sal: i32 = row.get("sal")?;
let comm: Option<i32> = row.get("comm")?;
println!("---------------|---------------|---------------|");
println!(" {:14}| {:>10} | {:>10} |",
ename,
sal,
comm.map_or("".to_string(), |v| v.to_string()));
// When no rows are found, conn.query_row() returns `Err(Error::NoDataFound)`.
// Get the first row as a tupple
let row = conn.query_row_as::<(String, i32, Option<i32>)>(sql, &[&7566])?;
println!("---------------|---------------|---------------|");
println!(" {:14}| {:>10} | {:>10} |",
row.0,
row.1,
row.2.map_or("".to_string(), |v| v.to_string()));
Executes non-select statements:
use oracle::Connection;
// Connect to a database.
let conn = Connection::connect("scott", "tiger", "//localhost/XE", &[])?;
conn.execute("create table person (id number(38), name varchar2(40))", &[])?;
// Execute a statement with positional parameters.
conn.execute("insert into person values (:1, :2)",
&[&1, // first parameter
&"John" // second parameter
])?;
// Execute a statement with named parameters.
conn.execute_named("insert into person values (:id, :name)",
&[("id", &2), // 'id' parameter
("name", &"Smith"), // 'name' parameter
])?;
// Commit the transaction.
conn.commit()?;
// Delete rows
conn.execute("delete from person", &[])?;
// Rollback the transaction.
conn.rollback()?;
Prints column information:
use oracle::Connection;
// Connect to a database.
let conn = Connection::connect("scott", "tiger", "//localhost/XE", &[])?;
let sql = "select ename, sal, comm from emp where 1 = 2";
let rows = conn.query(sql, &[])?;
// Print column names
for info in rows.column_info() {
print!(" {:14}|", info.name());
}
println!("");
// Print column types
for info in rows.column_info() {
print!(" {:14}|", info.oracle_type().to_string());
}
println!("");
Prepared statement:
use oracle::Connection;
let conn = Connection::connect("scott", "tiger", "//localhost/XE", &[])?;
// Create a prepared statement
let mut stmt = conn.prepare("insert into person values (:1, :2)", &[])?;
// Insert one row
stmt.execute(&[&1, &"John"])?;
// Insert another row
stmt.execute(&[&2, &"Smith"])?;
This is more efficient than two conn.execute()
.
An SQL statement is executed in the DBMS as follows:
- step 1. Parse the SQL statement and create an execution plan.
- step 2. Execute the plan with bind parameters.
When a prepared statement is used, step 1 is called only once.
NLS_LANG consists of three components: language, territory and charset. However the charset component is ignored and UTF-8(AL32UTF8) is used as charset because rust characters are UTF-8.
The territory component specifies numeric format, date format and so on. However it affects only conversion in Oracle. See the following example:
use oracle::Connection;
// The territory is France.
std::env::set_var("NLS_LANG", "french_france.AL32UTF8");
let conn = Connection::connect("scott", "tiger", "", &[])?;
// 10.1 is converted to a string in Oracle and fetched as a string.
let result = conn.query_row_as::<String>("select to_char(10.1) from dual", &[])?;
assert_eq!(result, "10,1"); // The decimal mark depends on the territory.
// 10.1 is fetched as a number and converted to a string in rust-oracle
let result = conn.query_row_as::<String>("select 10.1 from dual", &[])?;
assert_eq!(result, "10.1"); // The decimal mark is always period(.).
Note that NLS_LANG must be set before first rust-oracle function execution if required.
- Connection pooling
- Read and write LOB as stream
- REF CURSOR, BOOLEAN
- Scrollable cursors
- Batch DML
- Better Oracle object type support
Rust-oracle itself is under 2-clause BSD-style license.
ODPI-C bundled in rust-oracle is under the terms of: