Merge branch 'dynamic' of https://github.com/gwenn/rusqlite into gwenn-dynamic

This commit is contained in:
John Gallagher 2016-01-07 11:12:05 -05:00
commit f290c15d0d
2 changed files with 82 additions and 3 deletions

View File

@ -728,6 +728,11 @@ impl<'conn> Statement<'conn> {
cols cols
} }
/// Return the number of columns in the result set returned by the prepared statement.
pub fn column_count(&self) -> i32 {
self.column_count
}
/// Returns the column index in the result set for a given column name. /// Returns the column index in the result set for a given column name.
/// If there is no AS clause then the name of the column is unspecified and may change from one release of SQLite to the next. /// If there is no AS clause then the name of the column is unspecified and may change from one release of SQLite to the next.
/// ///
@ -1131,6 +1136,11 @@ impl<'stmt> Row<'stmt> {
} }
} }
} }
/// Return the number of columns in the current row.
pub fn column_count(&self) -> i32 {
self.stmt.column_count()
}
} }
/// A trait implemented by types that can index into columns of a row. /// A trait implemented by types that can index into columns of a row.
@ -1267,9 +1277,11 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
let stmt = db.prepare("SELECT * FROM foo").unwrap(); let stmt = db.prepare("SELECT * FROM foo").unwrap();
assert_eq!(stmt.column_count(), 1);
assert_eq!(stmt.column_names(), vec!["x"]); assert_eq!(stmt.column_names(), vec!["x"]);
let stmt = db.prepare("SELECT x AS a, x AS b FROM foo").unwrap(); let stmt = db.prepare("SELECT x AS a, x AS b FROM foo").unwrap();
assert_eq!(stmt.column_count(), 2);
assert_eq!(stmt.column_names(), vec!["a", "b"]); assert_eq!(stmt.column_names(), vec!["a", "b"]);
} }
@ -1668,5 +1680,17 @@ mod test {
} }
} }
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_dynamic() {
let db = checked_memory_handle();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y TEXT);
INSERT INTO foo VALUES(4, \"hello\");
END;";
db.execute_batch(sql).unwrap();
db.query_row("SELECT * FROM foo", &[], |r| assert_eq!(2, r.column_count())).unwrap();
}
} }
} }

View File

@ -96,7 +96,7 @@ macro_rules! raw_to_impl(
) )
); );
raw_to_impl!(c_int, sqlite3_bind_int); raw_to_impl!(c_int, sqlite3_bind_int); // i32
raw_to_impl!(i64, sqlite3_bind_int64); raw_to_impl!(i64, sqlite3_bind_int64);
raw_to_impl!(c_double, sqlite3_bind_double); raw_to_impl!(c_double, sqlite3_bind_double);
@ -208,9 +208,9 @@ macro_rules! raw_from_impl(
) )
); );
raw_from_impl!(c_int, sqlite3_column_int, ffi::SQLITE_INTEGER); raw_from_impl!(c_int, sqlite3_column_int, ffi::SQLITE_INTEGER); // i32
raw_from_impl!(i64, sqlite3_column_int64, ffi::SQLITE_INTEGER); raw_from_impl!(i64, sqlite3_column_int64, ffi::SQLITE_INTEGER);
raw_from_impl!(c_double, sqlite3_column_double, ffi::SQLITE_FLOAT); raw_from_impl!(c_double, sqlite3_column_double, ffi::SQLITE_FLOAT); // f64
impl FromSql for bool { impl FromSql for bool {
unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<bool> { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<bool> {
@ -293,6 +293,39 @@ impl<T: FromSql> FromSql for Option<T> {
} }
} }
/// Dynamic type value (http://sqlite.org/datatype3.html)
/// Value's type is dictated by SQLite (not by the caller).
#[derive(Clone,Debug,PartialEq)]
pub enum Value {
/// The value is a `NULL` value.
Null,
/// The value is a signed integer.
Integer(i64),
/// The value is a floating point number.
Real(f64),
/// The value is a text string.
Text(String),
/// The value is a blob of data
Blob(Vec<u8>),
}
impl FromSql for Value {
unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<Value> {
match sqlite3_column_type(stmt, col) {
ffi::SQLITE_TEXT => FromSql::column_result(stmt, col).map(|t| Value::Text(t)),
ffi::SQLITE_INTEGER => Ok(Value::Integer(ffi::sqlite3_column_int64(stmt, col))),
ffi::SQLITE_FLOAT => Ok(Value::Real(ffi::sqlite3_column_double(stmt, col))),
ffi::SQLITE_NULL => Ok(Value::Null),
ffi::SQLITE_BLOB => FromSql::column_result(stmt, col).map(|t| Value::Blob(t)),
_ => Err(Error::InvalidColumnType),
}
}
unsafe fn column_has_valid_sqlite_type(_: *mut sqlite3_stmt, _: c_int) -> bool {
true
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use Connection; use Connection;
@ -438,4 +471,26 @@ mod test {
assert!(is_invalid_column_type(row.get_checked::<i32,Vec<u8>>(4).err().unwrap())); assert!(is_invalid_column_type(row.get_checked::<i32,Vec<u8>>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<i32,time::Timespec>(4).err().unwrap())); assert!(is_invalid_column_type(row.get_checked::<i32,time::Timespec>(4).err().unwrap()));
} }
#[test]
fn test_dynamic_type() {
use super::Value;
let db = checked_memory_handle();
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
&[])
.unwrap();
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
let mut rows = stmt.query(&[]).unwrap();
let row = rows.next().unwrap().unwrap();
assert_eq!(Value::Blob(vec![1, 2]),
row.get_checked::<i32,Value>(0).unwrap());
assert_eq!(Value::Text(String::from("text")),
row.get_checked::<i32,Value>(1).unwrap());
assert_eq!(Value::Integer(1), row.get_checked::<i32,Value>(2).unwrap());
assert_eq!(Value::Real(1.5), row.get_checked::<i32,Value>(3).unwrap());
assert_eq!(Value::Null, row.get_checked::<i32,Value>(4).unwrap());
}
} }