rust-postgres/src/postgres/lib.rs

382 lines
11 KiB
Rust
Raw Normal View History

2013-08-04 02:17:32 +00:00
use std::cell::Cell;
2013-07-25 07:10:18 +00:00
use std::str;
use std::ptr;
2013-08-04 02:17:32 +00:00
use std::libc::{c_void, c_char, c_int};
use std::cast;
use std::iterator::RandomAccessIterator;
2013-08-04 05:21:16 +00:00
use std::vec;
2013-07-25 07:10:18 +00:00
mod ffi {
2013-08-04 02:17:32 +00:00
use std::libc::{c_char, c_int, c_uchar, c_uint, c_void};
2013-07-25 07:10:18 +00:00
pub type PGconn = c_void;
pub type PGresult = c_void;
2013-08-04 02:17:32 +00:00
pub type Oid = c_uint;
pub static CONNECTION_OK: c_int = 0;
pub static PGRES_EMPTY_QUERY: c_int = 0;
pub static PGRES_COMMAND_OK: c_int = 1;
pub static PGRES_TUPLES_OK: c_int = 2;
pub static TEXT_FORMAT: c_int = 0;
// FIXME when FFI gets fixed
type PQnoticeProcessor = *c_uchar /*extern "C" fn(*c_void, *c_char)*/;
2013-07-25 07:10:18 +00:00
#[link_args = "-lpq"]
extern "C" {
fn PQconnectdb(conninfo: *c_char) -> *PGconn;
2013-08-04 02:17:32 +00:00
fn PQsetNoticeProcessor(conn: *PGconn, proc: PQnoticeProcessor,
arg: *c_void) -> PQnoticeProcessor;
2013-07-25 07:10:18 +00:00
fn PQfinish(conn: *PGconn);
2013-08-04 02:17:32 +00:00
fn PQstatus(conn: *PGconn) -> c_int;
2013-07-25 07:10:18 +00:00
fn PQerrorMessage(conn: *PGconn) -> *c_char;
2013-08-04 02:17:32 +00:00
fn PQexec(conn: *PGconn, query: *c_char) -> *PGresult;
fn PQresultStatus(result: *PGresult) -> c_int;
fn PQresultErrorMessage(result: *PGresult) -> *c_char;
fn PQclear(result: *PGresult);
fn PQprepare(conn: *PGconn, stmtName: *c_char, query: *c_char,
nParams: c_int, paramTypes: *Oid) -> *PGresult;
2013-08-04 05:21:16 +00:00
fn PQdescribePrepared(conn: *PGconn, stmtName: *c_char) -> *PGresult;
2013-08-04 02:17:32 +00:00
fn PQexecPrepared(conn: *PGconn, stmtName: *c_char, nParams: c_int,
paramValues: **c_char, paramLengths: *c_int,
paramFormats: *c_int, resultFormat: c_int)
-> *PGresult;
fn PQntuples(result: *PGresult) -> c_int;
fn PQnfields(result: *PGresult) -> c_int;
2013-08-04 05:21:16 +00:00
fn PQnparams(result: *PGresult) -> c_int;
2013-08-04 02:17:32 +00:00
fn PQcmdTuples(result: *PGresult) -> *c_char;
fn PQgetvalue(result: *PGresult, row_number: c_int, col_number: c_int)
-> *c_char;
2013-07-25 07:10:18 +00:00
}
}
2013-08-04 02:17:32 +00:00
pub struct PostgresConnection<'self> {
priv conn: *ffi::PGconn,
priv next_stmt_id: Cell<uint>,
priv notice_handler: &'self fn(~str)
}
pub fn log_notice_handler(notice: ~str) {
if notice.starts_with("DEBUG") {
debug!("%s", notice);
} else if notice.starts_with("NOTICE") ||
notice.starts_with("INFO") ||
notice.starts_with("LOG") {
info!("%s", notice);
} else if notice.starts_with("WARNING") {
warn!("%s", notice);
} else {
error!("%s", notice);
}
}
extern "C" fn notice_handler(arg: *c_void, message: *c_char) {
2013-07-25 07:10:18 +00:00
unsafe {
2013-08-04 02:17:32 +00:00
let conn: *PostgresConnection = cast::transmute(arg);
((*conn).notice_handler)(str::raw::from_c_str(message));
}
}
#[unsafe_destructor]
impl<'self> Drop for PostgresConnection<'self> {
fn drop(&self) {
unsafe { ffi::PQfinish(self.conn) }
}
}
impl<'self> PostgresConnection<'self> {
fn get_error(&self) -> ~str {
unsafe { str::raw::from_c_str(ffi::PQerrorMessage(self.conn)) }
}
}
2013-07-25 07:10:18 +00:00
2013-08-04 02:17:32 +00:00
impl<'self> PostgresConnection<'self> {
pub fn new(uri: &str) -> Result<~PostgresConnection, ~str> {
unsafe {
let conn = ~PostgresConnection {conn: do uri.as_c_str |c_uri| {
ffi::PQconnectdb(c_uri)
}, next_stmt_id: Cell::new(0), notice_handler: log_notice_handler};
let arg: *PostgresConnection = &*conn;
ffi::PQsetNoticeProcessor(conn.conn, notice_handler,
arg as *c_void);
match ffi::PQstatus(conn.conn) {
ffi::CONNECTION_OK => Ok(conn),
_ => Err(conn.get_error())
}
}
}
pub fn set_notice_handler(&mut self, handler: &'self fn(~str)) {
self.notice_handler = handler;
}
pub fn prepare<'a>(&'a self, query: &str)
-> Result<~PostgresStatement<'a>, ~str> {
let id = self.next_stmt_id.take();
let name = fmt!("__libpostgres_stmt_%u", id);
self.next_stmt_id.put_back(id + 1);
2013-08-04 05:21:16 +00:00
let mut res = unsafe {
2013-08-04 02:17:32 +00:00
let raw_res = do query.as_c_str |c_query| {
do name.as_c_str |c_name| {
ffi::PQprepare(self.conn, c_name, c_query,
0, ptr::null())
}
};
PostgresResult {result: raw_res}
};
2013-08-04 05:21:16 +00:00
if res.status() != ffi::PGRES_COMMAND_OK {
return Err(res.error());
2013-08-04 02:17:32 +00:00
}
2013-08-04 05:21:16 +00:00
res = unsafe {
let raw_res = do name.as_c_str |c_name| {
ffi::PQdescribePrepared(self.conn, c_name)
};
PostgresResult {result: raw_res}
};
if res.status() != ffi::PGRES_COMMAND_OK {
return Err(res.error());
}
Ok(~PostgresStatement {conn: self, name: name,
num_params: res.num_params()})
2013-08-04 02:17:32 +00:00
}
2013-08-04 05:21:16 +00:00
pub fn update(&self, query: &str, params: &[~str]) -> Result<uint, ~str> {
2013-08-04 02:17:32 +00:00
do self.prepare(query).chain |stmt| {
2013-08-04 05:21:16 +00:00
stmt.update(params)
2013-08-04 02:17:32 +00:00
}
}
2013-08-04 05:21:16 +00:00
pub fn query(&self, query: &str, params: &[~str])
-> Result<~PostgresResult, ~str> {
2013-08-04 02:17:32 +00:00
do self.prepare(query).chain |stmt| {
2013-08-04 05:21:16 +00:00
stmt.query(params)
2013-07-25 07:10:18 +00:00
}
}
2013-08-04 05:21:16 +00:00
pub fn in_transaction<T>(&self,
blk: &fn(&PostgresConnection) -> Result<T, ~str>)
-> Result<T, ~str> {
match self.update("BEGIN", []) {
Ok(_) => (),
Err(err) => return Err(err)
};
// If the task fails in blk, the transaction will roll back when the
// connection closes
let ret = blk(self);
// TODO What to do about errors here?
if ret.is_ok() {
self.update("COMMIT", []);
} else {
self.update("ABORT", []);
}
ret
}
2013-07-25 07:10:18 +00:00
}
2013-08-04 02:17:32 +00:00
pub struct PostgresStatement<'self> {
priv conn: &'self PostgresConnection<'self>,
2013-08-04 05:21:16 +00:00
priv name: ~str,
priv num_params: uint
2013-07-25 07:10:18 +00:00
}
2013-08-04 02:17:32 +00:00
#[unsafe_destructor]
impl<'self> Drop for PostgresStatement<'self> {
2013-07-25 07:10:18 +00:00
fn drop(&self) {
2013-08-04 02:17:32 +00:00
// We can't do self.conn.update(...) since that will create a statement
let query = fmt!("DEALLOCATE %s", self.name);
2013-07-25 07:10:18 +00:00
unsafe {
2013-08-04 02:17:32 +00:00
do query.as_c_str |c_query| {
ffi::PQclear(ffi::PQexec(self.conn.conn, c_query));
}
2013-07-25 07:10:18 +00:00
}
}
}
2013-08-04 02:17:32 +00:00
impl<'self> PostgresStatement<'self> {
2013-08-04 05:21:16 +00:00
fn exec(&self, params: &[~str]) -> Result<~PostgresResult, ~str> {
if params.len() != self.num_params {
return Err(~"Incorrect number of parameters");
}
2013-08-04 02:17:32 +00:00
let res = unsafe {
let raw_res = do self.name.as_c_str |c_name| {
2013-08-04 05:21:16 +00:00
do as_c_str_array(params) |c_params| {
ffi::PQexecPrepared(self.conn.conn, c_name,
self.num_params as c_int,
c_params, ptr::null(), ptr::null(),
ffi::TEXT_FORMAT)
}
2013-08-04 02:17:32 +00:00
};
~PostgresResult{result: raw_res}
};
match res.status() {
ffi::PGRES_EMPTY_QUERY |
ffi::PGRES_COMMAND_OK |
ffi::PGRES_TUPLES_OK => Ok(res),
_ => Err(res.error())
}
}
2013-08-04 05:21:16 +00:00
pub fn update(&self, params: &[~str]) -> Result<uint, ~str> {
do self.exec(params).chain |res| {
2013-08-04 02:17:32 +00:00
Ok(res.affected_rows())
}
}
2013-08-04 05:21:16 +00:00
pub fn query(&self, params: &[~str]) -> Result<~PostgresResult, ~str> {
do self.exec(params).chain |res| {
2013-08-04 02:17:32 +00:00
Ok(res)
}
2013-07-25 07:10:18 +00:00
}
}
2013-08-04 02:17:32 +00:00
pub struct PostgresResult {
priv result: *ffi::PGresult
2013-07-25 07:10:18 +00:00
}
2013-08-04 02:17:32 +00:00
impl Drop for PostgresResult {
2013-07-25 07:10:18 +00:00
fn drop(&self) {
2013-08-04 02:17:32 +00:00
unsafe { ffi::PQclear(self.result) }
}
}
impl PostgresResult {
fn status(&self) -> c_int {
unsafe { ffi::PQresultStatus(self.result) }
}
fn error(&self) -> ~str {
unsafe { str::raw::from_c_str(ffi::PQresultErrorMessage(self.result)) }
}
fn affected_rows(&self) -> uint {
let s = unsafe {
str::raw::from_c_str(ffi::PQcmdTuples(self.result))
};
match FromStr::from_str(s) {
Some(updates) => updates,
None => 0
2013-07-25 07:10:18 +00:00
}
}
2013-08-04 05:21:16 +00:00
fn num_params(&self) -> uint {
unsafe { ffi::PQnparams(self.result) as uint }
}
2013-07-25 07:10:18 +00:00
}
2013-08-04 02:17:32 +00:00
impl Container for PostgresResult {
fn len(&self) -> uint {
unsafe { ffi::PQntuples(self.result) as uint }
}
}
impl PostgresResult {
pub fn iter<'a>(&'a self) -> PostgresResultIterator<'a> {
PostgresResultIterator {result: self, next_row: 0}
}
2013-08-04 05:21:16 +00:00
pub fn get<'a>(&'a self, idx: uint) -> PostgresRow<'a> {
if idx >= self.len() {
fail!("Out of bounds access");
}
self.iter().idx(idx).get()
}
2013-08-04 02:17:32 +00:00
}
pub struct PostgresResultIterator<'self> {
priv result: &'self PostgresResult,
priv next_row: uint
}
impl<'self> Iterator<PostgresRow<'self>> for PostgresResultIterator<'self> {
fn next(&mut self) -> Option<PostgresRow<'self>> {
if self.result.len() == self.next_row {
return None;
}
let row = self.next_row;
self.next_row += 1;
Some(PostgresRow {result: self.result, row: row})
}
fn size_hint(&self) -> (uint, Option<uint>) {
let rem = self.result.len() - self.next_row;
(rem, Some(rem))
}
}
impl<'self> RandomAccessIterator<PostgresRow<'self>> for
PostgresResultIterator<'self> {
fn indexable(&self) -> uint {
self.result.len()
}
fn idx(&self, idx: uint) -> Option<PostgresRow<'self>> {
if idx < self.indexable() {
Some(PostgresRow {result: self.result, row: idx})
} else {
None
2013-07-25 07:10:18 +00:00
}
}
}
2013-08-04 02:17:32 +00:00
pub struct PostgresRow<'self> {
priv result: &'self PostgresResult,
2013-07-25 07:10:18 +00:00
priv row: uint
}
2013-08-04 02:17:32 +00:00
impl<'self> Container for PostgresRow<'self> {
fn len(&self) -> uint {
unsafe { ffi::PQnfields(self.result.result) as uint }
}
}
impl<'self, T: FromStr> Index<uint, Option<T>> for PostgresRow<'self> {
2013-08-04 05:21:16 +00:00
fn index(&self, idx: &uint) -> Option<T> {
self.get(*idx)
}
}
impl<'self> PostgresRow<'self> {
pub fn get<T: FromStr>(&self, idx: uint) -> Option<T> {
if idx >= self.len() {
2013-08-04 02:17:32 +00:00
fail!("Out of bounds access");
}
let s = unsafe {
let raw_s = ffi::PQgetvalue(self.result.result,
self.row as c_int,
2013-08-04 05:21:16 +00:00
idx as c_int);
2013-08-04 02:17:32 +00:00
str::raw::from_c_str(raw_s)
};
FromStr::from_str(s)
}
}
2013-08-04 05:21:16 +00:00
fn as_c_str_array<T>(array: &[~str], blk: &fn(**c_char) -> T) -> T {
let mut c_array: ~[*c_char] = vec::with_capacity(array.len() + 1);
foreach s in array.iter() {
// DANGER, WILL ROBINSON
do s.as_c_str |c_s| {
c_array.push(c_s);
}
}
c_array.push(ptr::null());
blk(vec::raw::to_ptr(c_array))
}