Merge pull request #584 from gwenn/empty_query

Segmentation fault on `prepare_cached` with an empty query
This commit is contained in:
gwenn 2019-11-01 09:51:10 +01:00 committed by GitHub
commit 4923d8f8da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 8 deletions

View File

@ -135,9 +135,12 @@ impl StatementCache {
// Return a statement to the cache. // Return a statement to the cache.
fn cache_stmt(&self, stmt: RawStatement) { fn cache_stmt(&self, stmt: RawStatement) {
if stmt.is_null() {
return;
}
let mut cache = self.0.borrow_mut(); let mut cache = self.0.borrow_mut();
stmt.clear_bindings(); stmt.clear_bindings();
let sql = String::from_utf8_lossy(stmt.sql().to_bytes()) let sql = String::from_utf8_lossy(stmt.sql().unwrap().to_bytes())
.trim() .trim()
.to_string(); .to_string();
cache.insert(sql, stmt); cache.insert(sql, stmt);
@ -339,4 +342,10 @@ mod test {
} }
assert_eq!(1, cache.len()); assert_eq!(1, cache.len());
} }
#[test]
fn test_empty_stmt() {
let conn = Connection::open_in_memory().unwrap();
conn.prepare_cached("").unwrap();
}
} }

View File

@ -90,9 +90,17 @@ impl InnerConnection {
} else { } else {
let mut e = error_from_handle(db, r); let mut e = error_from_handle(db, r);
if let Error::SqliteFailure( if let Error::SqliteFailure(
ffi::Error{code: ffi::ErrorCode::CannotOpen, extended_code: _}, Some(msg)) = e { ffi::Error {
code: ffi::ErrorCode::CannotOpen,
extended_code: _,
},
Some(msg),
) = e
{
e = Error::SqliteFailure( e = Error::SqliteFailure(
ffi::Error::new(r), Some(format!("{}: {}", msg, c_path.to_string_lossy()))); ffi::Error::new(r),
Some(format!("{}: {}", msg, c_path.to_string_lossy())),
);
} }
ffi::sqlite3_close(db); ffi::sqlite3_close(db);
e e
@ -254,8 +262,10 @@ impl InnerConnection {
) )
} }
}; };
// If there is an error, *ppStmt is set to NULL.
self.decode_result(r)?; self.decode_result(r)?;
// If the input text contains no SQL (if the input is an empty string or a
// comment) then *ppStmt is set to NULL.
let c_stmt: *mut ffi::sqlite3_stmt = unsafe { c_stmt.assume_init() }; let c_stmt: *mut ffi::sqlite3_stmt = unsafe { c_stmt.assume_init() };
let c_tail: *const c_char = unsafe { c_tail.assume_init() }; let c_tail: *const c_char = unsafe { c_tail.assume_init() };
// TODO ignore spaces, comments, ... at the end // TODO ignore spaces, comments, ... at the end

View File

@ -966,7 +966,12 @@ mod test {
if let Error::SqliteFailure(e, Some(msg)) = err { if let Error::SqliteFailure(e, Some(msg)) = err {
assert_eq!(ErrorCode::CannotOpen, e.code); assert_eq!(ErrorCode::CannotOpen, e.code);
assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code); assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code);
assert!(msg.contains(filename), "error message '{}' does not contain '{}'", msg, filename); assert!(
msg.contains(filename),
"error message '{}' does not contain '{}'",
msg,
filename
);
} else { } else {
panic!("SqliteFailure expected"); panic!("SqliteFailure expected");
} }

View File

@ -14,6 +14,10 @@ impl RawStatement {
RawStatement(stmt, tail) RawStatement(stmt, tail)
} }
pub fn is_null(&self) -> bool {
self.0.is_null()
}
pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt { pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
self.0 self.0
} }
@ -95,8 +99,12 @@ impl RawStatement {
unsafe { ffi::sqlite3_clear_bindings(self.0) } unsafe { ffi::sqlite3_clear_bindings(self.0) }
} }
pub fn sql(&self) -> &CStr { pub fn sql(&self) -> Option<&CStr> {
unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) } if self.0.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) })
}
} }
pub fn finalize(mut self) -> c_int { pub fn finalize(mut self) -> c_int {

View File

@ -625,7 +625,11 @@ impl Into<RawStatement> for Statement<'_> {
impl fmt::Debug for Statement<'_> { impl fmt::Debug for Statement<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sql = str::from_utf8(self.stmt.sql().to_bytes()); let sql = if self.stmt.is_null() {
Ok("")
} else {
str::from_utf8(self.stmt.sql().unwrap().to_bytes())
};
f.debug_struct("Statement") f.debug_struct("Statement")
.field("conn", self.conn) .field("conn", self.conn)
.field("stmt", &self.stmt) .field("stmt", &self.stmt)
@ -1058,4 +1062,35 @@ mod test {
db.query_row("SELECT ?1, ?2, ?3", data.iter(), |row| row.get::<_, u8>(0)) db.query_row("SELECT ?1, ?2, ?3", data.iter(), |row| row.get::<_, u8>(0))
.unwrap(); .unwrap();
} }
#[test]
fn test_empty_stmt() {
let conn = Connection::open_in_memory().unwrap();
let mut stmt = conn.prepare("").unwrap();
assert_eq!(0, stmt.column_count());
assert!(stmt.parameter_index("test").is_ok());
assert!(stmt.step().is_err());
stmt.reset();
assert!(stmt.execute(NO_PARAMS).is_err());
}
#[test]
fn test_comment_stmt() {
let conn = Connection::open_in_memory().unwrap();
conn.prepare("/*SELECT 1;*/").unwrap();
}
#[test]
fn test_comment_and_sql_stmt() {
let conn = Connection::open_in_memory().unwrap();
let stmt = conn.prepare("/*...*/ SELECT 1;").unwrap();
assert_eq!(1, stmt.column_count());
}
#[test]
fn test_semi_colon_stmt() {
let conn = Connection::open_in_memory().unwrap();
let stmt = conn.prepare(";").unwrap();
assert_eq!(0, stmt.column_count());
}
} }