Redo FromSql to make implementing it not unsafe.

Pass implementers a BorrowedValue instead of relying on them to use
the FFI interface. We take the responsibility of converting the raw
statement and column index into a BorrowedValue.
This commit is contained in:
John Gallagher
2016-05-23 21:49:54 -04:00
parent c90cd37c00
commit 5b0cdbaa56
5 changed files with 133 additions and 187 deletions

View File

@@ -75,7 +75,7 @@ use std::result;
use std::str;
use libc::{c_int, c_char};
use types::{ToSql, FromSql};
use types::{ToSql, FromSql, BorrowedValue};
use error::{error_from_sqlite_code, error_from_handle};
use raw_statement::RawStatement;
use cache::StatementCache;
@@ -1064,15 +1064,9 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column name
/// for this row.
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
unsafe {
let idx = try!(idx.idx(self.stmt));
if T::column_has_valid_sqlite_type(self.stmt.stmt.ptr(), idx) {
FromSql::column_result(self.stmt.stmt.ptr(), idx)
} else {
Err(Error::InvalidColumnType)
}
}
let idx = try!(idx.idx(self.stmt));
let value = unsafe { BorrowedValue::new(&self.stmt.stmt, idx) };
FromSql::column_result(value)
}
/// Return the number of columns in the current row.
@@ -1106,6 +1100,39 @@ impl<'a> RowIndex for &'a str {
}
}
impl<'a> BorrowedValue<'a> {
unsafe fn new(stmt: &RawStatement, col: c_int) -> BorrowedValue {
use std::slice::from_raw_parts;
let raw = stmt.ptr();
match stmt.column_type(col) {
ffi::SQLITE_NULL => BorrowedValue::Null,
ffi::SQLITE_INTEGER => BorrowedValue::Integer(ffi::sqlite3_column_int64(raw, col)),
ffi::SQLITE_FLOAT => BorrowedValue::Real(ffi::sqlite3_column_double(raw, col)),
ffi::SQLITE_TEXT => {
let text = ffi::sqlite3_column_text(raw, col);
assert!(!text.is_null(), "unexpected SQLITE_TEXT column type with NULL data");
let s = CStr::from_ptr(text as *const c_char);
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8");
BorrowedValue::Text(s)
}
ffi::SQLITE_BLOB => {
let blob = ffi::sqlite3_column_blob(raw, col);
assert!(!blob.is_null(), "unexpected SQLITE_BLOB column type with NULL data");
let len = ffi::sqlite3_column_bytes(raw, col);
assert!(len >= 0, "unexpected negative return from sqlite3_column_bytes");
BorrowedValue::Blob(from_raw_parts(blob as *const u8, len as usize))
}
_ => unreachable!("sqlite3_column_type returned invalid value")
}
}
}
#[cfg(test)]
mod test {
extern crate tempdir;