From 9db82e74db20fc51b309120a154accc9c7b898a8 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 2 Jan 2016 10:28:00 +0100 Subject: [PATCH] Make possible to execute dynamic queries. Queries with dynamic column count/type. --- src/lib.rs | 24 +++++++++++++++++++++ src/types.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0b77690..a407c96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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(); + } } } diff --git a/src/types.rs b/src/types.rs index c33afa8..c4f39b8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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 { @@ -293,6 +293,39 @@ impl FromSql for Option { } } +/// 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), +} + +impl FromSql for Value { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { + 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::>(4).err().unwrap())); assert!(is_invalid_column_type(row.get_checked::(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::(0).unwrap()); + assert_eq!(Value::Text(String::from("text")), + row.get_checked::(1).unwrap()); + assert_eq!(Value::Integer(1), row.get_checked::(2).unwrap()); + assert_eq!(Value::Real(1.5), row.get_checked::(3).unwrap()); + assert_eq!(Value::Null, row.get_checked::(4).unwrap()); + } }