diff --git a/src/postgres/lib.rs b/src/postgres/lib.rs new file mode 100644 index 00000000..26805936 --- /dev/null +++ b/src/postgres/lib.rs @@ -0,0 +1,114 @@ +extern mod sql; + +use sql::{ToSqlStr, FromSqlStr}; + +use std::str; +use std::ptr; + +mod ffi { + use std::libc::{c_char, c_int, c_uint, c_void}; + + pub type PGconn = c_void; + pub type PGresult = c_void; + pub type OId = c_uint; + + pub enum ConnStatusType { + CONNECTION_OK, + CONNECTION_BAD, + CONNECTION_STARTED, + CONNECTION_MADE, + CONNECTION_AWAITING_RESPONSE, + CONNECTION_AUTH_OK, + CONNECTION_SETENV, + CONNECTION_SSL_STARTUP, + CONNECTION_NEEDED + } + + pub enum ExecStatusType { + PGRES_EMPTY_QUERY = 0, + PGRES_COMMAND_OK, + PGRES_TUPLES_OK, + PGRES_COPY_OUT, + PGRES_COPY_IN, + PGRES_BAD_RESPONSE, + PGRES_NONFATAL_ERROR, + PGRES_FATAL_ERROR, + PGRES_COPY_BOTH, + PGRES_SINGLE_TUPLE + } + + #[link_args = "-lpq"] + extern "C" { + fn PQconnectdb(conninfo: *c_char) -> *PGconn; + fn PQfinish(conn: *PGconn); + fn PQstatus(conn: *PGconn) -> ConnStatusType; + fn PQerrorMessage(conn: *PGconn) -> *c_char; + fn PQexecParams(conn: *PGconn, command: *c_char, nParams: c_int, + paramTypes: *OId, paramValues: **c_char, + paramLengths: *c_int, paramFormats: *c_int, + resultFormat: c_int) -> *PGresult; + fn PQresultStatus(res: *PGresult) -> ExecStatusType; + fn PQresultErrorMessage(res: *PGresult) -> *c_char; + fn PQclear(res: *PGresult); + fn PQntuples(res: *PGresult) -> c_int; + } +} + +fn open(name: &str) -> Result<~Connection, ~str> { + unsafe { + let conn = ~Connection {conn: do name.as_c_str |c_name| { + ffi::PQconnectdb(c_name) + }}; + + match ffi::PQstatus(conn.conn) { + ffi::CONNECTION_OK => Ok(conn), + _ => Err(str::raw::from_c_str(ffi::PQerrorMessage(conn.conn))) + } + } +} + +pub struct Connection { + priv conn: *ffi::PGconn +} + +impl Drop for Connection { + fn drop(&self) { + unsafe { + ffi::PQfinish(self.conn) + } + } +} + +impl Connection { + fn query(&self, query: &str, params: &[@ToSqlStr]) -> Result<~RowIterator, ~str> { + Err(~"foo") + } +} + +pub struct RowIterator { + priv res: *ffi::PGresult, + priv row: Row +} + +impl Drop for RowIterator { + fn drop(&self) { + unsafe { + ffi::PQclear(self.res) + } + } +} + +impl<'self> Iterator<&'self Row<'self>> for RowIterator { + fn next(&mut self) -> Option<&'self Row> { + unsafe { + if ffi::PQntuples(self.res) == self. { + return None; + } + } + } +} + +pub struct Row<'self> { + priv res: *ffi::PGresult, + priv row: uint +} diff --git a/src/postgres/test.rs b/src/postgres/test.rs new file mode 100644 index 00000000..b60ea06e --- /dev/null +++ b/src/postgres/test.rs @@ -0,0 +1,6 @@ +extern mod postgres; + +#[test] +fn test_conn() { + printfln!("%?", postgres::open("postgres://postgres@localhost")); +} diff --git a/src/sql/lib.rs b/src/sql/lib.rs new file mode 100644 index 00000000..9982f9db --- /dev/null +++ b/src/sql/lib.rs @@ -0,0 +1,44 @@ + +// pub trait Connection<'self, R: Rows<'self>> { +// fn query(&self, query: &str, params: &[@ToSqlStr]) -> Result<~R, ~str>; +// } + +// pub trait Rows<'self, R: Row, I: Iterator<&'self R>>: Container { +// fn iter(&self) -> I; +// } + +// pub trait Row: Container { +// fn get(&self) -> Option; +// } + +pub trait ToSqlStr { + fn to_sql_str(&self) -> ~str; +} + +impl ToSqlStr for int { + fn to_sql_str(&self) -> ~str { + self.to_str() + } +} + +impl ToSqlStr for uint { + fn to_sql_str(&self) -> ~str { + self.to_str() + } +} + +pub trait FromSqlStr { + fn from_sql_str(&str) -> Option; +} + +impl FromSqlStr for int { + fn from_sql_str(s: &str) -> Option { + FromStr::from_str(s) + } +} + +impl FromSqlStr for uint { + fn from_sql_str(s: &str) -> Option { + FromStr::from_str(s) + } +} diff --git a/src/sqlite3/lib.rs b/src/sqlite3/lib.rs index 9df378d2..4910273c 100644 --- a/src/sqlite3/lib.rs +++ b/src/sqlite3/lib.rs @@ -78,6 +78,25 @@ impl Connection { _ => Err(self.get_error()) } } + + pub fn update(&self, query: &str) -> Result { + self.prepare(query).chain(|stmt| stmt.update()) + } + + pub fn query(&self, query: &str, blk: &fn (&mut ResultIterator) -> T) + -> Result { + let stmt = match self.prepare(query) { + Ok(stmt) => stmt, + Err(err) => return Err(err) + }; + + let mut it = match stmt.query() { + Ok(it) => it, + Err(err) => return Err(err) + }; + + Ok(blk(&mut it)) + } } pub struct PreparedStatement<'self> { diff --git a/src/sqlite3/test.rs b/src/sqlite3/test.rs index e2516063..e6b45f33 100644 --- a/src/sqlite3/test.rs +++ b/src/sqlite3/test.rs @@ -3,13 +3,15 @@ extern mod sqlite3; #[test] fn test() { let conn = sqlite3::open("db").unwrap(); - conn.prepare("DROP TABLE IF EXISTS foo").unwrap().update(); - conn.prepare("CREATE TABLE foo ( + conn.update("DROP TABLE IF EXISTS foo"); + conn.update("CREATE TABLE foo ( id BIGINT PRIMARY KEY - )").unwrap().update(); - conn.prepare("INSERT INTO foo (id) VALUES (101), (102)").unwrap().update(); - let stmt = conn.prepare("SELECT id FROM foo").unwrap(); - for stmt.query().unwrap().advance |row| { - printfln!("%u %d", row.len(), row.get(0).get()); - } + )"); + conn.update("INSERT INTO foo (id) VALUES (101), (102)"); + + do conn.query("SELECT id FROM foo") |it| { + for it.advance |row| { + printfln!("%u %d", row.len(), row.get(0).get()); + } + }; }