Make possible to execute dynamic queries.

Queries with dynamic column count/type.
This commit is contained in:
gwenn 2016-01-02 10:28:00 +01:00
parent 38cf8d597b
commit 9db82e74db
2 changed files with 82 additions and 3 deletions

View File

@ -728,6 +728,11 @@ impl<'conn> Statement<'conn> {
cols
}
/// Return the number of columns in the result set returned by the prepared statement.
pub fn column_count(&self) -> i32 {
self.column_count
}
/// Execute the prepared statement.
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
@ -1109,6 +1114,11 @@ impl<'stmt> Row<'stmt> {
}
}
}
/// Return the number of columns in the current row.
pub fn column_count(&self) -> i32 {
self.stmt.column_count()
}
}
#[cfg(test)]
@ -1220,9 +1230,11 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
let stmt = db.prepare("SELECT * FROM foo").unwrap();
assert_eq!(stmt.column_count(), 1);
assert_eq!(stmt.column_names(), vec!["x"]);
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"]);
}
@ -1621,5 +1633,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!(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!(c_double, sqlite3_column_double, ffi::SQLITE_FLOAT);
raw_from_impl!(c_double, sqlite3_column_double, ffi::SQLITE_FLOAT); // f64
impl FromSql for 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)]
mod test {
use Connection;
@ -438,4 +471,26 @@ mod test {
assert!(is_invalid_column_type(row.get_checked::<Vec<u8>>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<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::<Value>(0).unwrap());
assert_eq!(Value::Text(String::from("text")),
row.get_checked::<Value>(1).unwrap());
assert_eq!(Value::Integer(1), row.get_checked::<Value>(2).unwrap());
assert_eq!(Value::Real(1.5), row.get_checked::<Value>(3).unwrap());
assert_eq!(Value::Null, row.get_checked::<Value>(4).unwrap());
}
}