Do not assume sqlite3_column_text is valid UTF-8.

Fix Statement::value_ref
This commit is contained in:
gwenn 2019-07-24 20:08:31 +02:00
parent bd5506899d
commit f0ae7b6e9b
5 changed files with 16 additions and 12 deletions

View File

@ -216,7 +216,7 @@ impl InnerConnection {
pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> { pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> {
let mut c_stmt = MaybeUninit::uninit(); let mut c_stmt = MaybeUninit::uninit();
let (c_sql, len, _) = str_for_sqlite(sql)?; let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?;
let r = unsafe { let r = unsafe {
if cfg!(feature = "unlock_notify") { if cfg!(feature = "unlock_notify") {
let mut rc; let mut rc;

View File

@ -248,9 +248,9 @@ fn str_to_cstring(s: &str) -> Result<CString> {
/// The `sqlite3_destructor_type` item is always `SQLITE_TRANSIENT` unless /// The `sqlite3_destructor_type` item is always `SQLITE_TRANSIENT` unless
/// the string was empty (in which case it's `SQLITE_STATIC`, and the ptr is /// the string was empty (in which case it's `SQLITE_STATIC`, and the ptr is
/// static). /// static).
fn str_for_sqlite(s: &str) -> Result<(*const c_char, c_int, ffi::sqlite3_destructor_type)> { fn str_for_sqlite(s: &[u8]) -> Result<(*const c_char, c_int, ffi::sqlite3_destructor_type)> {
let len = len_as_c_int(s.len())?; let len = len_as_c_int(s.len())?;
if memchr::memchr(0, s.as_bytes()).is_none() { if memchr::memchr(0, s).is_none() {
let (ptr, dtor_info) = if len != 0 { let (ptr, dtor_info) = if len != 0 {
(s.as_ptr() as *const c_char, ffi::SQLITE_TRANSIENT()) (s.as_ptr() as *const c_char, ffi::SQLITE_TRANSIENT())
} else { } else {

View File

@ -86,6 +86,7 @@ impl Sql {
self.push_real(r); self.push_real(r);
} }
ValueRef::Text(s) => { ValueRef::Text(s) => {
let s = std::str::from_utf8(s).expect("invalid UTF-8");
self.push_string_literal(s); self.push_string_literal(s);
} }
_ => { _ => {

View File

@ -622,10 +622,7 @@ impl Statement<'_> {
CStr::from_ptr(text as *const c_char) 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_bytes();
let s = s
.to_str()
.expect("sqlite3_column_text returned invalid UTF-8");
ValueRef::Text(s) ValueRef::Text(s)
} }
ffi::SQLITE_BLOB => { ffi::SQLITE_BLOB => {

View File

@ -14,7 +14,7 @@ pub enum ValueRef<'a> {
/// The value is a floating point number. /// The value is a floating point number.
Real(f64), Real(f64),
/// The value is a text string. /// The value is a text string.
Text(&'a str), Text(&'a [u8]),
/// The value is a blob of data /// The value is a blob of data
Blob(&'a [u8]), Blob(&'a [u8]),
} }
@ -54,7 +54,10 @@ impl<'a> ValueRef<'a> {
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_str(&self) -> FromSqlResult<&'a str> { pub fn as_str(&self) -> FromSqlResult<&'a str> {
match *self { match *self {
ValueRef::Text(t) => Ok(t), ValueRef::Text(t) => match std::str::from_utf8(t) {
Err(e) => Err(FromSqlError::Other(Box::new(e))),
Ok(r) => Ok(r),
},
_ => Err(FromSqlError::InvalidType), _ => Err(FromSqlError::InvalidType),
} }
} }
@ -75,7 +78,10 @@ impl From<ValueRef<'_>> for Value {
ValueRef::Null => Value::Null, ValueRef::Null => Value::Null,
ValueRef::Integer(i) => Value::Integer(i), ValueRef::Integer(i) => Value::Integer(i),
ValueRef::Real(r) => Value::Real(r), ValueRef::Real(r) => Value::Real(r),
ValueRef::Text(s) => Value::Text(s.to_string()), ValueRef::Text(s) => {
let s = std::str::from_utf8(s).expect("invalid UTF-8");
Value::Text(s.to_string())
}
ValueRef::Blob(b) => Value::Blob(b.to_vec()), ValueRef::Blob(b) => Value::Blob(b.to_vec()),
} }
} }
@ -83,7 +89,7 @@ impl From<ValueRef<'_>> for Value {
impl<'a> From<&'a str> for ValueRef<'a> { impl<'a> From<&'a str> for ValueRef<'a> {
fn from(s: &str) -> ValueRef<'_> { fn from(s: &str) -> ValueRef<'_> {
ValueRef::Text(s) ValueRef::Text(s.as_bytes())
} }
} }
@ -99,7 +105,7 @@ impl<'a> From<&'a Value> for ValueRef<'a> {
Value::Null => ValueRef::Null, Value::Null => ValueRef::Null,
Value::Integer(i) => ValueRef::Integer(i), Value::Integer(i) => ValueRef::Integer(i),
Value::Real(r) => ValueRef::Real(r), Value::Real(r) => ValueRef::Real(r),
Value::Text(ref s) => ValueRef::Text(s), Value::Text(ref s) => ValueRef::Text(s.as_bytes()),
Value::Blob(ref b) => ValueRef::Blob(b), Value::Blob(ref b) => ValueRef::Blob(b),
} }
} }