(This text was generated with LLM assistance and modified by me. I feel it's accurate and a good bug report.)
From<ValueRef> for Value panics on invalid UTF-8 text instead of returning an error.
Problem
From<ValueRef<'_>> for Value panics via expect() when a ValueRef::Text contains invalid UTF-8 bytes. This makes it impossible to use row.get::<_, Value>()on databases that may contain non-UTF-8 text data due to an immediate runtime panic.
The FromSql for String path handles this well via as_str() → from_utf8().map_err() but the Value conversion path does not do that and panics unconditionally.
Reproduction
Minimal repo: https://github.com/dimitarvp/rusqlite-utf8-panic
Just cargo run it.
use rusqlite::{Connection, types::Value};
fn main() {
let conn = Connection::open_in_memory().unwrap();
conn.execute_batch("CREATE TABLE t (val TEXT);").unwrap();
conn.execute(
"INSERT INTO t (val) VALUES (CAST(X'FFFE8041' AS TEXT))",
[],
).unwrap();
// Panics in From<ValueRef> for Value
let mut stmt = conn.prepare("SELECT UPPER(val) FROM t").unwrap();
let result = stmt.query_row([], |row| row.get::<_, Value>(0));
println!("result: {:?}", result);
}
Output:
thread 'main' panicked at rusqlite-0.37.0/src/types/from_sql.rs:293:18:
invalid UTF-8: Utf8Error { valid_up_to: 0, error_len: Some(1) }
Tested on both 0.37.0 and 0.38.0. Both panic.
Root cause
value_ref.rs:
impl From<ValueRef<'_>> for Value {
fn from(borrowed: ValueRef<'_>) -> Self {
match borrowed {
// ...
ValueRef::Text(s) => {
let s = std::str::from_utf8(s).expect("invalid UTF-8"); // panics
Self::Text(s.to_string())
}
// ...
}
}
}
This uses expect() instead of returning an error. ValueRef::as_str() uses from_utf8(t).map_err(FromSqlError::other) and does not panic.
Context
SQLite does not enforce UTF-8 validity for TEXT columns. Databases created by non-Rust programs, or TEXT columns containing data processed by SQLite functions like UPPER() / LOWER() on non-UTF-8 input, can legitimately contain invalid byte sequences.
This was partially addressed in #548 (making ValueRef::Text hold &[u8]), but the From<ValueRef> for Value conversion was not updated to handle the error case.
Potential fixes
Replace expect() with code that returns Result or store the bytes as a blob fallback. For example, TryFrom<ValueRef<'_>> for Value could be introduced alongside the existing From, or the From impl could fall back to Value::Blob for invalid UTF-8 text.
(This text was generated with LLM assistance and modified by me. I feel it's accurate and a good bug report.)
From<ValueRef> for Valuepanics on invalid UTF-8 text instead of returning an error.Problem
From<ValueRef<'_>> for Valuepanics viaexpect()when aValueRef::Textcontains invalid UTF-8 bytes. This makes it impossible to userow.get::<_, Value>()on databases that may contain non-UTF-8 text data due to an immediate runtime panic.The
FromSql for Stringpath handles this well viaas_str()→from_utf8().map_err()but theValueconversion path does not do that and panics unconditionally.Reproduction
Minimal repo: https://github.com/dimitarvp/rusqlite-utf8-panic
Just
cargo runit.Output:
Tested on both 0.37.0 and 0.38.0. Both panic.
Root cause
value_ref.rs:This uses
expect()instead of returning an error.ValueRef::as_str()usesfrom_utf8(t).map_err(FromSqlError::other)and does not panic.Context
SQLite does not enforce UTF-8 validity for TEXT columns. Databases created by non-Rust programs, or TEXT columns containing data processed by SQLite functions like
UPPER()/LOWER()on non-UTF-8 input, can legitimately contain invalid byte sequences.This was partially addressed in #548 (making
ValueRef::Texthold&[u8]), but theFrom<ValueRef> for Valueconversion was not updated to handle the error case.Potential fixes
Replace
expect()with code that returnsResultor store the bytes as a blob fallback. For example,TryFrom<ValueRef<'_>> for Valuecould be introduced alongside the existingFrom, or theFromimpl could fall back toValue::Blobfor invalid UTF-8 text.