Null support and Makefile
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
*.so
|
||||
*.dummy
|
||||
/postgres
|
||||
/test*
|
||||
|
||||
24
Makefile
Normal file
24
Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
RUSTC ?= rustc
|
||||
RUSTFLAGS += -L.
|
||||
|
||||
.PHONY: all
|
||||
all: sql.dummy postgres.dummy test_postgres sqlite3.dummy
|
||||
|
||||
sql.dummy: src/sql/lib.rs
|
||||
$(RUSTC) $(RUSTFLAGS) --lib $< -o $@
|
||||
touch $@
|
||||
|
||||
postgres.dummy: src/postgres/lib.rs sql.dummy
|
||||
$(RUSTC) $(RUSTFLAGS) --lib src/postgres/lib.rs -o $@
|
||||
touch $@
|
||||
|
||||
test_postgres: src/postgres/test.rs postgres.dummy
|
||||
$(RUSTC) $(RUSTFLAGS) --test src/postgres/test.rs -o $@
|
||||
|
||||
sqlite3.dummy: src/sqlite3/lib.rs sql.dummy
|
||||
$(RUSTC) $(RUSTFLAGS) --lib src/sqlite3/lib.rs -o $@
|
||||
touch $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm *.dummy *.so
|
||||
@@ -47,6 +47,8 @@ mod ffi {
|
||||
fn PQnfields(result: *PGresult) -> c_int;
|
||||
fn PQnparams(result: *PGresult) -> c_int;
|
||||
fn PQcmdTuples(result: *PGresult) -> *c_char;
|
||||
fn PQgetisnull(result: *PGresult, row_number: c_int, col_number: c_int)
|
||||
-> c_int;
|
||||
fn PQgetvalue(result: *PGresult, row_number: c_int, col_number: c_int)
|
||||
-> *c_char;
|
||||
}
|
||||
@@ -242,6 +244,18 @@ impl<'self> PostgresStatement<'self> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for 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))
|
||||
}
|
||||
|
||||
pub struct PostgresResult {
|
||||
priv result: *ffi::PGresult
|
||||
}
|
||||
@@ -275,6 +289,21 @@ impl PostgresResult {
|
||||
fn num_params(&self) -> uint {
|
||||
unsafe { ffi::PQnparams(self.result) as uint }
|
||||
}
|
||||
|
||||
fn is_null(&self, row: uint, col: uint) -> bool {
|
||||
unsafe {
|
||||
ffi::PQgetisnull(self.result, row as c_int, col as c_int) == 1
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value(&self, row: uint, col: uint) -> ~str {
|
||||
unsafe {
|
||||
let raw_s = ffi::PQgetvalue(self.result,
|
||||
row as c_int,
|
||||
col as c_int);
|
||||
str::raw::from_c_str(raw_s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Container for PostgresResult {
|
||||
@@ -345,37 +374,84 @@ impl<'self> Container for PostgresRow<'self> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self, T: FromStr> Index<uint, Option<T>> for PostgresRow<'self> {
|
||||
fn index(&self, idx: &uint) -> Option<T> {
|
||||
impl<'self, T: FromSql> Index<uint, T> for PostgresRow<'self> {
|
||||
fn index(&self, idx: &uint) -> T {
|
||||
self.get(*idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> PostgresRow<'self> {
|
||||
pub fn get<T: FromStr>(&self, idx: uint) -> Option<T> {
|
||||
fn is_null(&self, col: uint) -> bool {
|
||||
self.result.is_null(self.row, col)
|
||||
}
|
||||
|
||||
fn get_value(&self, col: uint) -> ~str {
|
||||
self.result.get_value(self.row, col)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> PostgresRow<'self> {
|
||||
pub fn get<T: FromSql>(&self, idx: uint) -> T {
|
||||
if idx >= self.len() {
|
||||
fail!("Out of bounds access");
|
||||
}
|
||||
|
||||
let s = unsafe {
|
||||
let raw_s = ffi::PQgetvalue(self.result.result,
|
||||
self.row as c_int,
|
||||
idx as c_int);
|
||||
str::raw::from_c_str(raw_s)
|
||||
};
|
||||
|
||||
FromStr::from_str(s)
|
||||
FromSql::from_sql(self, idx)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for s in array.iter() {
|
||||
// DANGER, WILL ROBINSON
|
||||
do s.as_c_str |c_s| {
|
||||
c_array.push(c_s);
|
||||
pub trait FromSql {
|
||||
fn from_sql(row: &PostgresRow, idx: uint) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! from_str_impl(
|
||||
($t:ty) => (
|
||||
impl FromSql for $t {
|
||||
fn from_sql(row: &PostgresRow, idx: uint) -> $t {
|
||||
if row.is_null(idx) {
|
||||
fail!("Row is NULL");
|
||||
}
|
||||
FromStr::from_str(row.get_value(idx)).get()
|
||||
}
|
||||
}
|
||||
}
|
||||
c_array.push(ptr::null());
|
||||
blk(vec::raw::to_ptr(c_array))
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
macro_rules! option_impl(
|
||||
($t:ty) => (
|
||||
impl FromSql for Option<$t> {
|
||||
fn from_sql(row: &PostgresRow, idx: uint) -> Option<$t> {
|
||||
match row.is_null(idx) {
|
||||
true => None,
|
||||
false => Some(FromSql::from_sql(row, idx))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
from_str_impl!(int)
|
||||
option_impl!(int)
|
||||
from_str_impl!(i8)
|
||||
option_impl!(i8)
|
||||
from_str_impl!(i16)
|
||||
option_impl!(i16)
|
||||
from_str_impl!(i32)
|
||||
option_impl!(i32)
|
||||
from_str_impl!(i64)
|
||||
option_impl!(i64)
|
||||
from_str_impl!(uint)
|
||||
option_impl!(uint)
|
||||
from_str_impl!(u8)
|
||||
option_impl!(u8)
|
||||
from_str_impl!(u16)
|
||||
option_impl!(u16)
|
||||
from_str_impl!(u32)
|
||||
option_impl!(u32)
|
||||
from_str_impl!(u64)
|
||||
option_impl!(u64)
|
||||
from_str_impl!(float)
|
||||
option_impl!(float)
|
||||
from_str_impl!(f32)
|
||||
option_impl!(f32)
|
||||
from_str_impl!(f64)
|
||||
option_impl!(f64)
|
||||
|
||||
@@ -38,10 +38,26 @@ fn test_params() {
|
||||
chk!(conn.update("INSERT INTO basic (id) VALUES ($1)", [~"101"]));
|
||||
|
||||
let res = chk!(conn.query("SELECT id from basic WHERE id = $1", [~"101"]));
|
||||
assert_eq!(1, res.len());
|
||||
assert_eq!(1, res.get(0).len());
|
||||
assert_eq!(Some(101), res.get(0)[0]);
|
||||
|
||||
Err::<(), ~str>(~"")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null() {
|
||||
let conn = chk!(PostgresConnection::new("postgres://postgres@localhost"));
|
||||
|
||||
do conn.in_transaction |conn| {
|
||||
chk!(conn.update("CREATE TABLE basic (id INT PRIMARY KEY, foo INT)",
|
||||
[]));
|
||||
chk!(conn.update("INSERT INTO basic (id, foo) VALUES ($1, NULL), ($2, $3)",
|
||||
[~"101", ~"102", ~"0"]));
|
||||
|
||||
let res = chk!(conn.query("SELECT foo from basic ORDER BY id", []));
|
||||
assert_eq!(None, res.get(0).get::<Option<int>>(0));
|
||||
assert_eq!(Some(0), res.get(1).get::<Option<int>>(0));
|
||||
|
||||
Err::<(), ~str>(~"")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
|
||||
pub trait SqlConnection<'self, S: SqlStatement> {
|
||||
fn new(uri: &str) -> ~Self;
|
||||
|
||||
fn prepare(&self, query: &str) -> ~S<'self>;
|
||||
pub trait SqlConnection<Row, It, Res, S: SqlStatement<Row, It, Res>> {
|
||||
fn new(uri: &str) -> Result<~Self, ~str>;
|
||||
fn prepare<'a>(&'a self, query: &str) -> Result<~S, ~str>;
|
||||
}
|
||||
|
||||
pub trait SqlStatement {
|
||||
fn query<T>(&self, params: &[@SqlType],
|
||||
blk: &fn(&SqlResult) -> Result<T, ~str>)
|
||||
pub trait SqlStatement<Row, It, R: SqlResult<Row, It>> {
|
||||
fn query<T>(&self, params: &[~str], blk: &fn(&R) -> Result<T, ~str>)
|
||||
-> Result<T, ~str>;
|
||||
|
||||
fn update(&self, params: &[@SqlType]) -> Result<uint, ~str>;
|
||||
fn update(&self, params: &[~str]) -> Result<uint, ~str>;
|
||||
}
|
||||
|
||||
pub trait SqlResult<R: SqlRow, I: Iterator<R>>: Container {
|
||||
fn iter(&self) -> I;
|
||||
}
|
||||
|
||||
pub trait SqlRow {
|
||||
fn get<T: SqlType>(&self) -> Option<T>;
|
||||
pub trait SqlRow: Container {
|
||||
fn get<T: FromSql<Self>>(&self, index: uint) -> Option<T>;
|
||||
}
|
||||
|
||||
pub enum SqlType {
|
||||
Int(int),
|
||||
Uint(uint),
|
||||
pub trait FromSql<R/*: SqlRow*/> {
|
||||
fn from_sql(row: &R, column: uint) -> Option<Self>;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::libc::c_int;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::vec;
|
||||
use std::uint;
|
||||
|
||||
mod ffi {
|
||||
use std::libc::{c_char, c_int, c_void};
|
||||
@@ -165,7 +164,7 @@ impl<'self> PreparedStatement<'self> {
|
||||
}
|
||||
|
||||
fn bind_params(&self, params: &[@SqlType]) -> Result<(), ~str> {
|
||||
for params.iter().enumerate().advance |(idx, param)| {
|
||||
for (idx, param) in params.iter().enumerate() {
|
||||
let ret = match param.to_sql_str() {
|
||||
Some(val) => do val.as_c_str |c_param| {
|
||||
unsafe {
|
||||
@@ -246,7 +245,7 @@ impl<'self> Row<'self> {
|
||||
let count = unsafe { ffi::sqlite3_column_count(stmt.stmt) as uint};
|
||||
let mut row = Row {stmt: stmt, cols: vec::with_capacity(count)};
|
||||
|
||||
for uint::range(0, count) |i| {
|
||||
for i in range(0, count) {
|
||||
let typ = unsafe {
|
||||
ffi::sqlite3_column_type(stmt.stmt, i as c_int)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user