mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 19:41:37 +08:00
Merge pull request #659 from gwenn/nul_byte
Handle text with internal nuls
This commit is contained in:
commit
25bd5fc914
@ -237,13 +237,12 @@ fn str_to_cstring(s: &str) -> Result<CString> {
|
|||||||
|
|
||||||
/// Returns `Ok((string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT))`
|
/// Returns `Ok((string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT))`
|
||||||
/// normally.
|
/// normally.
|
||||||
/// Returns errors if the string has embedded nuls or is too large for sqlite.
|
/// Returns error if the string is too large for sqlite.
|
||||||
/// 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: &[u8]) -> 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).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 {
|
||||||
@ -251,11 +250,6 @@ fn str_for_sqlite(s: &[u8]) -> Result<(*const c_char, c_int, ffi::sqlite3_destru
|
|||||||
("".as_ptr() as *const c_char, ffi::SQLITE_STATIC())
|
("".as_ptr() as *const c_char, ffi::SQLITE_STATIC())
|
||||||
};
|
};
|
||||||
Ok((ptr, len, dtor_info))
|
Ok((ptr, len, dtor_info))
|
||||||
} else {
|
|
||||||
// There's an embedded nul, so we fabricate a NulError.
|
|
||||||
let e = CString::new(s);
|
|
||||||
Err(Error::NulError(e.unwrap_err()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to cast to c_int safely, returning the correct error type if the cast
|
// Helper to cast to c_int safely, returning the correct error type if the cast
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use std::ffi::CStr;
|
|
||||||
use std::iter::IntoIterator;
|
use std::iter::IntoIterator;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
#[cfg(feature = "array")]
|
#[cfg(feature = "array")]
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
@ -659,15 +658,18 @@ impl Statement<'_> {
|
|||||||
}
|
}
|
||||||
ffi::SQLITE_TEXT => {
|
ffi::SQLITE_TEXT => {
|
||||||
let s = unsafe {
|
let s = unsafe {
|
||||||
|
// Quoting from "Using SQLite" book:
|
||||||
|
// To avoid problems, an application should first extract the desired type using a sqlite3_column_xxx() function,
|
||||||
|
// and then call the appropriate sqlite3_column_bytes() function.
|
||||||
let text = ffi::sqlite3_column_text(raw, col as c_int);
|
let text = ffi::sqlite3_column_text(raw, col as c_int);
|
||||||
|
let len = ffi::sqlite3_column_bytes(raw, col as c_int);
|
||||||
assert!(
|
assert!(
|
||||||
!text.is_null(),
|
!text.is_null(),
|
||||||
"unexpected SQLITE_TEXT column type with NULL data"
|
"unexpected SQLITE_TEXT column type with NULL data"
|
||||||
);
|
);
|
||||||
CStr::from_ptr(text as *const c_char)
|
from_raw_parts(text as *const u8, len as usize)
|
||||||
};
|
};
|
||||||
|
|
||||||
let s = s.to_bytes();
|
|
||||||
ValueRef::Text(s)
|
ValueRef::Text(s)
|
||||||
}
|
}
|
||||||
ffi::SQLITE_BLOB => {
|
ffi::SQLITE_BLOB => {
|
||||||
@ -1089,4 +1091,32 @@ mod test {
|
|||||||
let stmt = conn.prepare(";").unwrap();
|
let stmt = conn.prepare(";").unwrap();
|
||||||
assert_eq!(0, stmt.column_count());
|
assert_eq!(0, stmt.column_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_utf16_conversion() {
|
||||||
|
let db = Connection::open_in_memory().unwrap();
|
||||||
|
db.pragma_update(None, "encoding", &"UTF-16le").unwrap();
|
||||||
|
let encoding: String = db
|
||||||
|
.pragma_query_value(None, "encoding", |row| row.get(0))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!("UTF-16le", encoding);
|
||||||
|
db.execute_batch("CREATE TABLE foo(x TEXT)").unwrap();
|
||||||
|
let expected = "テスト";
|
||||||
|
db.execute("INSERT INTO foo(x) VALUES (?)", &[&expected])
|
||||||
|
.unwrap();
|
||||||
|
let actual: String = db
|
||||||
|
.query_row("SELECT x FROM foo", NO_PARAMS, |row| row.get(0))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nul_byte() {
|
||||||
|
let db = Connection::open_in_memory().unwrap();
|
||||||
|
let expected = "a\x00b";
|
||||||
|
let actual: String = db
|
||||||
|
.query_row("SELECT ?", &[&expected], |row| row.get(0))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user