rust-postgres/src/lib.rs

218 lines
6.0 KiB
Rust
Raw Normal View History

2013-08-22 05:52:15 +00:00
extern mod extra;
2013-08-04 02:17:32 +00:00
2013-08-22 05:52:15 +00:00
use std::cell::Cell;
use std::hashmap::HashMap;
use std::rt::io::net::ip::SocketAddr;
use std::rt::io::net::tcp::TcpStream;
use extra::url::Url;
2013-08-04 02:17:32 +00:00
2013-08-22 05:52:15 +00:00
use message::*;
2013-07-25 07:10:18 +00:00
2013-08-22 05:52:15 +00:00
mod message;
2013-07-25 07:10:18 +00:00
2013-08-18 03:30:31 +00:00
pub struct PostgresConnection {
2013-08-22 05:52:15 +00:00
priv stream: Cell<TcpStream>,
priv next_stmt_id: Cell<int>
2013-08-04 02:17:32 +00:00
}
2013-08-18 03:30:31 +00:00
impl Drop for PostgresConnection {
2013-08-04 02:17:32 +00:00
fn drop(&self) {
2013-08-22 07:12:35 +00:00
self.write_message(&Terminate);
2013-08-18 03:42:40 +00:00
}
2013-08-04 02:17:32 +00:00
}
2013-07-25 07:10:18 +00:00
2013-08-18 03:30:31 +00:00
impl PostgresConnection {
2013-08-23 02:47:06 +00:00
pub fn connect(url: &str) -> PostgresConnection {
2013-08-22 05:52:15 +00:00
let parsed_url: Url = FromStr::from_str(url).unwrap();
let socket_url = fmt!("%s:%s", parsed_url.host,
2013-08-23 02:47:06 +00:00
parsed_url.port.get_ref().as_slice());
2013-08-22 05:52:15 +00:00
let addr: SocketAddr = FromStr::from_str(socket_url).unwrap();
let conn = PostgresConnection {
stream: Cell::new(TcpStream::connect(addr).unwrap()),
2013-08-18 03:42:40 +00:00
next_stmt_id: Cell::new(0)
};
2013-08-22 06:41:26 +00:00
let mut args = HashMap::new();
2013-08-23 02:47:06 +00:00
args.insert(&"user", parsed_url.user.get_ref().user.as_slice());
2013-08-22 06:41:26 +00:00
conn.write_message(&StartupMessage(args));
2013-08-04 05:21:16 +00:00
2013-08-22 06:41:26 +00:00
match conn.read_message() {
2013-08-22 05:52:15 +00:00
AuthenticationOk => (),
2013-08-23 05:24:14 +00:00
resp => fail!("Bad response: %?", resp.to_str())
2013-08-04 02:17:32 +00:00
}
2013-08-22 05:52:15 +00:00
loop {
2013-08-23 05:24:14 +00:00
match conn.read_message() {
2013-08-22 05:52:15 +00:00
ParameterStatus(param, value) =>
printfln!("Param %s = %s", param, value),
2013-08-22 06:41:26 +00:00
BackendKeyData(*) => (),
ReadyForQuery(*) => break,
2013-08-23 05:24:14 +00:00
resp => fail!("Bad response: %?", resp.to_str())
}
2013-08-05 00:48:48 +00:00
}
2013-08-23 05:24:14 +00:00
conn
2013-08-05 00:48:48 +00:00
}
2013-08-22 06:41:26 +00:00
fn write_message(&self, message: &FrontendMessage) {
2013-08-22 05:52:15 +00:00
do self.stream.with_mut_ref |s| {
2013-08-22 06:41:26 +00:00
s.write_message(message);
2013-07-25 07:10:18 +00:00
}
2013-08-22 06:41:26 +00:00
}
2013-08-04 02:17:32 +00:00
2013-08-22 06:41:26 +00:00
fn read_message(&self) -> BackendMessage {
2013-08-22 05:52:15 +00:00
do self.stream.with_mut_ref |s| {
2013-08-22 06:41:26 +00:00
s.read_message()
2013-08-04 05:21:16 +00:00
}
2013-08-22 06:41:26 +00:00
}
pub fn prepare<'a>(&'a self, query: &str) -> PostgresStatement<'a> {
let id = self.next_stmt_id.take();
let stmt_name = ifmt!("statement_{}", id);
self.next_stmt_id.put_back(id + 1);
2013-08-05 00:48:48 +00:00
2013-08-22 06:41:26 +00:00
let types = [];
self.write_message(&Parse(stmt_name, query, types));
self.write_message(&Sync);
match self.read_message() {
2013-08-22 05:52:15 +00:00
ParseComplete => (),
2013-08-23 05:24:14 +00:00
resp @ ErrorResponse(*) => fail!("Error: %?", resp.to_str()),
resp => fail!("Bad response: %?", resp.to_str())
2013-08-05 00:48:48 +00:00
}
2013-08-22 06:41:26 +00:00
self.wait_for_ready();
self.write_message(&Describe('S' as u8, stmt_name));
self.write_message(&Sync);
let num_params = match self.read_message() {
ParameterDescription(ref types) => types.len(),
2013-08-23 05:24:14 +00:00
resp => fail!("Bad response: %?", resp.to_str())
2013-08-22 06:41:26 +00:00
};
match self.read_message() {
RowDescription(*) | NoData => (),
2013-08-23 05:24:14 +00:00
resp => fail!("Bad response: %?", resp.to_str())
2013-08-05 00:48:48 +00:00
}
2013-08-17 22:09:26 +00:00
2013-08-22 06:41:26 +00:00
self.wait_for_ready();
PostgresStatement {
conn: self,
name: stmt_name,
2013-08-22 07:12:35 +00:00
num_params: num_params,
next_portal_id: Cell::new(0)
2013-08-22 06:41:26 +00:00
}
}
2013-08-23 05:24:14 +00:00
fn query(&self, query: &str) {
self.write_message(&Query(query));
loop {
match self.read_message() {
ReadyForQuery(*) => break,
resp @ ErrorResponse(*) => fail!("Error: %?", resp.to_str()),
_ => ()
}
}
}
pub fn in_transaction<T, E: ToStr>(&self, blk: &fn(&PostgresConnection)
-> Result<T, E>)
-> Result<T, E> {
self.query("BEGIN");
// If this fails, Postgres will rollback when the connection closes
let ret = blk(self);
if ret.is_ok() {
self.query("COMMIT");
} else {
self.query("ROLLBACK");
}
ret
}
2013-08-22 06:41:26 +00:00
fn wait_for_ready(&self) {
2013-08-23 05:24:14 +00:00
loop {
match self.read_message() {
ReadyForQuery(*) => break,
resp => fail!("Bad response: %?", resp.to_str())
}
2013-08-22 06:41:26 +00:00
}
2013-08-17 22:09:26 +00:00
}
}
2013-08-22 05:52:15 +00:00
pub struct PostgresStatement<'self> {
priv conn: &'self PostgresConnection,
2013-08-22 06:41:26 +00:00
priv name: ~str,
2013-08-22 07:12:35 +00:00
priv num_params: uint,
priv next_portal_id: Cell<uint>
}
#[unsafe_destructor]
impl<'self> Drop for PostgresStatement<'self> {
fn drop(&self) {
self.conn.write_message(&Close('S' as u8, self.name.as_slice()));
self.conn.write_message(&Sync);
2013-08-23 05:24:14 +00:00
loop {
match self.conn.read_message() {
ReadyForQuery(*) => break,
_ => ()
}
}
2013-08-22 07:12:35 +00:00
}
}
2013-08-22 05:52:15 +00:00
impl<'self> PostgresStatement<'self> {
2013-08-22 07:12:35 +00:00
pub fn num_params(&self) -> uint {
self.num_params
}
2013-08-23 05:24:14 +00:00
fn execute(&self, portal_name: &str) {
2013-08-22 07:12:35 +00:00
let formats = [];
let values = [];
let result_formats = [];
self.conn.write_message(&Bind(portal_name, self.name.as_slice(),
formats, values, result_formats));
2013-08-23 05:24:14 +00:00
self.conn.write_message(&Execute(portal_name.as_slice(), 0));
2013-08-22 07:12:35 +00:00
self.conn.write_message(&Sync);
match self.conn.read_message() {
BindComplete => (),
2013-08-23 05:24:14 +00:00
resp @ ErrorResponse(*) => fail!("Error: %?", resp.to_str()),
resp => fail!("Bad response: %?", resp.to_str())
2013-08-22 07:12:35 +00:00
}
2013-08-23 05:24:14 +00:00
}
pub fn update(&self) -> uint {
self.execute("");
2013-08-22 07:12:35 +00:00
2013-08-23 05:24:14 +00:00
let mut num = 0;
loop {
match self.conn.read_message() {
CommandComplete(ret) => {
let s = ret.split_iter(' ').last().unwrap();
match FromStr::from_str(s) {
None => (),
Some(n) => num = n
}
break;
}
DataRow(*) => (),
EmptyQueryResponse => break,
NoticeResponse(*) => (),
resp @ ErrorResponse(*) => fail!("Error: %?", resp.to_str()),
resp => fail!("Bad response: %?", resp.to_str())
}
}
2013-08-22 07:12:35 +00:00
self.conn.wait_for_ready();
2013-08-23 05:24:14 +00:00
num
2013-08-22 07:12:35 +00:00
}
}