mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-10-02 23:35:20 +00:00
fix(sqlite): handle empty statements, fixes #231
This commit is contained in:
parent
b3fd720aad
commit
cd6735b5d7
@ -71,6 +71,16 @@ impl Arguments for SqliteArguments {
|
|||||||
|
|
||||||
impl SqliteArgumentValue {
|
impl SqliteArgumentValue {
|
||||||
pub(super) fn bind(&self, statement: &mut Statement, index: usize) -> crate::Result<()> {
|
pub(super) fn bind(&self, statement: &mut Statement, index: usize) -> crate::Result<()> {
|
||||||
|
let handle = unsafe {
|
||||||
|
if let Some(handle) = statement.handle() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
// drop all requested bindings for a null/empty statement
|
||||||
|
// note that this _should_ not happen as argument size for a null statement should be zero
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Handle error of trying to bind too many parameters here
|
// TODO: Handle error of trying to bind too many parameters here
|
||||||
let index = index as c_int;
|
let index = index as c_int;
|
||||||
|
|
||||||
@ -83,13 +93,7 @@ impl SqliteArgumentValue {
|
|||||||
let bytes_len = bytes.len() as i32;
|
let bytes_len = bytes.len() as i32;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
sqlite3_bind_blob(
|
sqlite3_bind_blob(handle, index, bytes_ptr, bytes_len, SQLITE_TRANSIENT())
|
||||||
statement.handle(),
|
|
||||||
index,
|
|
||||||
bytes_ptr,
|
|
||||||
bytes_len,
|
|
||||||
SQLITE_TRANSIENT(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,29 +104,21 @@ impl SqliteArgumentValue {
|
|||||||
let bytes_len = bytes.len() as i32;
|
let bytes_len = bytes.len() as i32;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
sqlite3_bind_text(
|
sqlite3_bind_text(handle, index, bytes_ptr, bytes_len, SQLITE_TRANSIENT())
|
||||||
statement.handle(),
|
|
||||||
index,
|
|
||||||
bytes_ptr,
|
|
||||||
bytes_len,
|
|
||||||
SQLITE_TRANSIENT(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SqliteArgumentValue::Double(value) => unsafe {
|
SqliteArgumentValue::Double(value) => unsafe {
|
||||||
sqlite3_bind_double(statement.handle(), index, *value)
|
sqlite3_bind_double(handle, index, *value)
|
||||||
},
|
},
|
||||||
|
|
||||||
SqliteArgumentValue::Int(value) => unsafe {
|
SqliteArgumentValue::Int(value) => unsafe { sqlite3_bind_int(handle, index, *value) },
|
||||||
sqlite3_bind_int(statement.handle(), index, *value)
|
|
||||||
},
|
|
||||||
|
|
||||||
SqliteArgumentValue::Int64(value) => unsafe {
|
SqliteArgumentValue::Int64(value) => unsafe {
|
||||||
sqlite3_bind_int64(statement.handle(), index, *value)
|
sqlite3_bind_int64(handle, index, *value)
|
||||||
},
|
},
|
||||||
|
|
||||||
SqliteArgumentValue::Null => unsafe { sqlite3_bind_null(statement.handle(), index) },
|
SqliteArgumentValue::Null => unsafe { sqlite3_bind_null(handle, index) },
|
||||||
};
|
};
|
||||||
|
|
||||||
if status != SQLITE_OK {
|
if status != SQLITE_OK {
|
||||||
|
@ -37,7 +37,7 @@ pub(super) struct SqliteStatementHandle(NonNull<sqlite3_stmt>);
|
|||||||
///
|
///
|
||||||
/// The statement is finalized ( `sqlite3_finalize` ) on drop.
|
/// The statement is finalized ( `sqlite3_finalize` ) on drop.
|
||||||
pub(super) struct Statement {
|
pub(super) struct Statement {
|
||||||
handle: SqliteStatementHandle,
|
handle: Option<SqliteStatementHandle>,
|
||||||
pub(super) connection: SqliteConnectionHandle,
|
pub(super) connection: SqliteConnectionHandle,
|
||||||
pub(super) worker: Worker,
|
pub(super) worker: Worker,
|
||||||
pub(super) tail: usize,
|
pub(super) tail: usize,
|
||||||
@ -94,7 +94,7 @@ impl Statement {
|
|||||||
let mut self_ = Self {
|
let mut self_ = Self {
|
||||||
worker: conn.worker.clone(),
|
worker: conn.worker.clone(),
|
||||||
connection: conn.handle,
|
connection: conn.handle,
|
||||||
handle: SqliteStatementHandle(NonNull::new(statement_handle).unwrap()),
|
handle: NonNull::new(statement_handle).map(SqliteStatementHandle),
|
||||||
columns: HashMap::new(),
|
columns: HashMap::new(),
|
||||||
tail,
|
tail,
|
||||||
};
|
};
|
||||||
@ -113,8 +113,8 @@ impl Statement {
|
|||||||
|
|
||||||
/// Returns a pointer to the raw C pointer backing this statement.
|
/// Returns a pointer to the raw C pointer backing this statement.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) unsafe fn handle(&self) -> *mut sqlite3_stmt {
|
pub(super) unsafe fn handle(&self) -> Option<*mut sqlite3_stmt> {
|
||||||
self.handle.0.as_ptr()
|
self.handle.map(|handle| handle.0.as_ptr())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn data_count(&mut self) -> usize {
|
pub(super) fn data_count(&mut self) -> usize {
|
||||||
@ -126,43 +126,59 @@ impl Statement {
|
|||||||
// The value is correct only if there was a recent call to
|
// The value is correct only if there was a recent call to
|
||||||
// sqlite3_step that returned SQLITE_ROW.
|
// sqlite3_step that returned SQLITE_ROW.
|
||||||
|
|
||||||
let count: c_int = unsafe { sqlite3_data_count(self.handle()) };
|
unsafe { self.handle().map_or(0, |handle| sqlite3_data_count(handle)) as usize }
|
||||||
count as usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn column_count(&mut self) -> usize {
|
pub(super) fn column_count(&mut self) -> usize {
|
||||||
// https://sqlite.org/c3ref/column_count.html
|
// https://sqlite.org/c3ref/column_count.html
|
||||||
let count = unsafe { sqlite3_column_count(self.handle()) };
|
unsafe {
|
||||||
count as usize
|
self.handle()
|
||||||
|
.map_or(0, |handle| sqlite3_column_count(handle)) as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn column_name(&mut self, index: usize) -> &str {
|
pub(super) fn column_name(&mut self, index: usize) -> &str {
|
||||||
// https://sqlite.org/c3ref/column_name.html
|
unsafe {
|
||||||
let name = unsafe {
|
self.handle()
|
||||||
let ptr = sqlite3_column_name(self.handle(), index as c_int);
|
.map(|handle| {
|
||||||
debug_assert!(!ptr.is_null());
|
// https://sqlite.org/c3ref/column_name.html
|
||||||
|
let ptr = sqlite3_column_name(handle, index as c_int);
|
||||||
|
debug_assert!(!ptr.is_null());
|
||||||
|
|
||||||
CStr::from_ptr(ptr)
|
CStr::from_ptr(ptr)
|
||||||
};
|
})
|
||||||
|
.map_or(Ok(""), |name| name.to_str())
|
||||||
name.to_str().unwrap()
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn column_decltype(&mut self, index: usize) -> Option<&str> {
|
pub(super) fn column_decltype(&mut self, index: usize) -> Option<&str> {
|
||||||
let name = unsafe {
|
unsafe {
|
||||||
let ptr = sqlite3_column_decltype(self.handle(), index as c_int);
|
self.handle()
|
||||||
|
.and_then(|handle| {
|
||||||
|
let ptr = sqlite3_column_decltype(handle, index as c_int);
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(CStr::from_ptr(ptr))
|
Some(CStr::from_ptr(ptr))
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
.map(|name| name.to_str().unwrap())
|
||||||
name.map(|s| s.to_str().unwrap())
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn column_not_null(&mut self, index: usize) -> crate::Result<Option<bool>> {
|
pub(super) fn column_not_null(&mut self, index: usize) -> crate::Result<Option<bool>> {
|
||||||
|
let handle = unsafe {
|
||||||
|
if let Some(handle) = self.handle() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
// we do not know the nullablility of a column that doesn't exist on a statement
|
||||||
|
// that doesn't exist
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// https://sqlite.org/c3ref/column_database_name.html
|
// https://sqlite.org/c3ref/column_database_name.html
|
||||||
//
|
//
|
||||||
@ -171,9 +187,9 @@ impl Statement {
|
|||||||
// sqlite3_finalize() or until the statement is automatically reprepared by the
|
// sqlite3_finalize() or until the statement is automatically reprepared by the
|
||||||
// first call to sqlite3_step() for a particular run or until the same information
|
// first call to sqlite3_step() for a particular run or until the same information
|
||||||
// is requested again in a different encoding.
|
// is requested again in a different encoding.
|
||||||
let db_name = sqlite3_column_database_name(self.handle(), index as c_int);
|
let db_name = sqlite3_column_database_name(handle, index as c_int);
|
||||||
let table_name = sqlite3_column_table_name(self.handle(), index as c_int);
|
let table_name = sqlite3_column_table_name(handle, index as c_int);
|
||||||
let origin_name = sqlite3_column_origin_name(self.handle(), index as c_int);
|
let origin_name = sqlite3_column_origin_name(handle, index as c_int);
|
||||||
|
|
||||||
if db_name.is_null() || table_name.is_null() || origin_name.is_null() {
|
if db_name.is_null() || table_name.is_null() || origin_name.is_null() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -213,8 +229,10 @@ impl Statement {
|
|||||||
|
|
||||||
pub(super) fn params(&mut self) -> usize {
|
pub(super) fn params(&mut self) -> usize {
|
||||||
// https://www.hwaci.com/sw/sqlite/c3ref/bind_parameter_count.html
|
// https://www.hwaci.com/sw/sqlite/c3ref/bind_parameter_count.html
|
||||||
let num = unsafe { sqlite3_bind_parameter_count(self.handle()) };
|
unsafe {
|
||||||
num as usize
|
self.handle()
|
||||||
|
.map_or(0, |handle| sqlite3_bind_parameter_count(handle)) as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn bind(&mut self, arguments: &mut SqliteArguments) -> crate::Result<()> {
|
pub(super) fn bind(&mut self, arguments: &mut SqliteArguments) -> crate::Result<()> {
|
||||||
@ -230,36 +248,47 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn reset(&mut self) {
|
pub(super) fn reset(&mut self) {
|
||||||
|
let handle = unsafe {
|
||||||
|
if let Some(handle) = self.handle() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
// nothing to reset if its null
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// https://sqlite.org/c3ref/reset.html
|
// https://sqlite.org/c3ref/reset.html
|
||||||
// https://sqlite.org/c3ref/clear_bindings.html
|
// https://sqlite.org/c3ref/clear_bindings.html
|
||||||
|
|
||||||
// the status value of reset is ignored because it merely propagates
|
// the status value of reset is ignored because it merely propagates
|
||||||
// the status of the most recently invoked step function
|
// the status of the most recently invoked step function
|
||||||
|
|
||||||
let _ = unsafe { sqlite3_reset(self.handle()) };
|
let _ = unsafe { sqlite3_reset(handle) };
|
||||||
|
let _ = unsafe { sqlite3_clear_bindings(handle) };
|
||||||
let _ = unsafe { sqlite3_clear_bindings(self.handle()) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn step(&mut self) -> crate::Result<Step> {
|
pub(super) async fn step(&mut self) -> crate::Result<Step> {
|
||||||
// https://sqlite.org/c3ref/step.html
|
// https://sqlite.org/c3ref/step.html
|
||||||
|
|
||||||
let handle = self.handle;
|
if let Some(handle) = self.handle {
|
||||||
|
let status = unsafe {
|
||||||
|
self.worker
|
||||||
|
.run(move || sqlite3_step(handle.0.as_ptr()))
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
|
||||||
let status = unsafe {
|
match status {
|
||||||
self.worker
|
SQLITE_DONE => Ok(Step::Done),
|
||||||
.run(move || sqlite3_step(handle.0.as_ptr()))
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
|
|
||||||
match status {
|
SQLITE_ROW => Ok(Step::Row),
|
||||||
SQLITE_DONE => Ok(Step::Done),
|
|
||||||
|
|
||||||
SQLITE_ROW => Ok(Step::Row),
|
_ => {
|
||||||
|
return Err(SqliteError::from_connection(self.connection.0.as_ptr()).into());
|
||||||
_ => {
|
}
|
||||||
return Err(SqliteError::from_connection(self.connection.0.as_ptr()).into());
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// An empty (null) query will always emit `Step::Done`
|
||||||
|
Ok(Step::Done)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,7 +297,9 @@ impl Drop for Statement {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// https://sqlite.org/c3ref/finalize.html
|
// https://sqlite.org/c3ref/finalize.html
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = sqlite3_finalize(self.handle());
|
if let Some(handle) = self.handle() {
|
||||||
|
let _ = sqlite3_finalize(handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,14 @@ impl<'c> SqliteValue<'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn r#type(&self) -> Option<SqliteType> {
|
fn r#type(&self) -> Option<SqliteType> {
|
||||||
let type_code = unsafe { sqlite3_column_type(self.statement.handle(), self.index) };
|
let type_code = unsafe {
|
||||||
|
if let Some(handle) = self.statement.handle() {
|
||||||
|
sqlite3_column_type(handle, self.index)
|
||||||
|
} else {
|
||||||
|
// unreachable: null statements do not have any values to type
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, or SQLITE_NULL
|
// SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, or SQLITE_NULL
|
||||||
match type_code {
|
match type_code {
|
||||||
@ -47,41 +54,65 @@ impl<'c> SqliteValue<'c> {
|
|||||||
|
|
||||||
/// Returns the 32-bit INTEGER result.
|
/// Returns the 32-bit INTEGER result.
|
||||||
pub(super) fn int(&self) -> i32 {
|
pub(super) fn int(&self) -> i32 {
|
||||||
unsafe { sqlite3_column_int(self.statement.handle(), self.index) }
|
unsafe {
|
||||||
|
self.statement
|
||||||
|
.handle()
|
||||||
|
.map_or(0, |handle| sqlite3_column_int(handle, self.index))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the 64-bit INTEGER result.
|
/// Returns the 64-bit INTEGER result.
|
||||||
pub(super) fn int64(&self) -> i64 {
|
pub(super) fn int64(&self) -> i64 {
|
||||||
unsafe { sqlite3_column_int64(self.statement.handle(), self.index) }
|
unsafe {
|
||||||
|
self.statement
|
||||||
|
.handle()
|
||||||
|
.map_or(0, |handle| sqlite3_column_int64(handle, self.index))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the 64-bit, REAL result.
|
/// Returns the 64-bit, REAL result.
|
||||||
pub(super) fn double(&self) -> f64 {
|
pub(super) fn double(&self) -> f64 {
|
||||||
unsafe { sqlite3_column_double(self.statement.handle(), self.index) }
|
unsafe {
|
||||||
|
self.statement
|
||||||
|
.handle()
|
||||||
|
.map_or(0.0, |handle| sqlite3_column_double(handle, self.index))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the UTF-8 TEXT result.
|
/// Returns the UTF-8 TEXT result.
|
||||||
pub(super) fn text(&self) -> Option<&'c str> {
|
pub(super) fn text(&self) -> Option<&'c str> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = sqlite3_column_text(self.statement.handle(), self.index);
|
self.statement.handle().and_then(|handle| {
|
||||||
|
let ptr = sqlite3_column_text(handle, self.index);
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(from_utf8_unchecked(CStr::from_ptr(ptr as _).to_bytes()))
|
Some(from_utf8_unchecked(CStr::from_ptr(ptr as _).to_bytes()))
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes(&self) -> usize {
|
fn bytes(&self) -> usize {
|
||||||
// Returns the size of the result in bytes.
|
// Returns the size of the result in bytes.
|
||||||
let len = unsafe { sqlite3_column_bytes(self.statement.handle(), self.index) };
|
unsafe {
|
||||||
len as usize
|
self.statement
|
||||||
|
.handle()
|
||||||
|
.map_or(0, |handle| sqlite3_column_bytes(handle, self.index)) as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the BLOB result.
|
/// Returns the BLOB result.
|
||||||
pub(super) fn blob(&self) -> &'c [u8] {
|
pub(super) fn blob(&self) -> &'c [u8] {
|
||||||
let ptr = unsafe { sqlite3_column_blob(self.statement.handle(), self.index) };
|
let ptr = unsafe {
|
||||||
|
if let Some(handle) = self.statement.handle() {
|
||||||
|
sqlite3_column_blob(handle, self.index)
|
||||||
|
} else {
|
||||||
|
// Null statements do not exist
|
||||||
|
return &[];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
// Empty BLOBs are received as null pointers
|
// Empty BLOBs are received as null pointers
|
||||||
|
Loading…
x
Reference in New Issue
Block a user