blob: d1a5e4007fc92f9de87ca3a44f879f98078a19d1 [file] [log] [blame]
extern crate mysqlclient_sys as ffi;
mod iterator;
mod metadata;
use std::ffi::CStr;
use std::os::raw as libc;
use std::ptr::NonNull;
use self::iterator::*;
use self::metadata::*;
use super::bind::Binds;
use crate::mysql::MysqlTypeMetadata;
use crate::result::{DatabaseErrorKind, QueryResult};
pub struct Statement {
stmt: NonNull<ffi::MYSQL_STMT>,
input_binds: Option<Binds>,
}
impl Statement {
pub(crate) fn new(stmt: NonNull<ffi::MYSQL_STMT>) -> Self {
Statement {
stmt: stmt,
input_binds: None,
}
}
pub fn prepare(&self, query: &str) -> QueryResult<()> {
unsafe {
ffi::mysql_stmt_prepare(
self.stmt.as_ptr(),
query.as_ptr() as *const libc::c_char,
query.len() as libc::c_ulong,
);
}
self.did_an_error_occur()
}
pub fn bind<Iter>(&mut self, binds: Iter) -> QueryResult<()>
where
Iter: IntoIterator<Item = (MysqlTypeMetadata, Option<Vec<u8>>)>,
{
let mut input_binds = Binds::from_input_data(binds);
input_binds.with_mysql_binds(|bind_ptr| {
// This relies on the invariant that the current value of `self.input_binds`
// will not change without this function being called
unsafe {
ffi::mysql_stmt_bind_param(self.stmt.as_ptr(), bind_ptr);
}
});
self.input_binds = Some(input_binds);
self.did_an_error_occur()
}
/// This function should be called instead of `results` on queries which
/// have no return value. It should never be called on a statement on
/// which `results` has previously been called?
pub unsafe fn execute(&self) -> QueryResult<()> {
ffi::mysql_stmt_execute(self.stmt.as_ptr());
self.did_an_error_occur()?;
ffi::mysql_stmt_store_result(self.stmt.as_ptr());
self.did_an_error_occur()?;
Ok(())
}
pub fn affected_rows(&self) -> usize {
let affected_rows = unsafe { ffi::mysql_stmt_affected_rows(self.stmt.as_ptr()) };
affected_rows as usize
}
/// This function should be called instead of `execute` for queries which
/// have a return value. After calling this function, `execute` can never
/// be called on this statement.
pub unsafe fn results(
&mut self,
types: Vec<MysqlTypeMetadata>,
) -> QueryResult<StatementIterator> {
StatementIterator::new(self, types)
}
/// This function should be called instead of `execute` for queries which
/// have a return value. After calling this function, `execute` can never
/// be called on this statement.
pub unsafe fn named_results(&mut self) -> QueryResult<NamedStatementIterator> {
NamedStatementIterator::new(self)
}
fn last_error_message(&self) -> String {
unsafe { CStr::from_ptr(ffi::mysql_stmt_error(self.stmt.as_ptr())) }
.to_string_lossy()
.into_owned()
}
/// If the pointers referenced by the `MYSQL_BIND` structures are invalidated,
/// you must call this function again before calling `mysql_stmt_fetch`.
pub unsafe fn bind_result(&self, binds: *mut ffi::MYSQL_BIND) -> QueryResult<()> {
ffi::mysql_stmt_bind_result(self.stmt.as_ptr(), binds);
self.did_an_error_occur()
}
pub unsafe fn fetch_column(
&self,
bind: &mut ffi::MYSQL_BIND,
idx: usize,
offset: usize,
) -> QueryResult<()> {
ffi::mysql_stmt_fetch_column(
self.stmt.as_ptr(),
bind,
idx as libc::c_uint,
offset as libc::c_ulong,
);
self.did_an_error_occur()
}
fn metadata(&self) -> QueryResult<StatementMetadata> {
use crate::result::Error::DeserializationError;
let result_ptr = unsafe { ffi::mysql_stmt_result_metadata(self.stmt.as_ptr()).as_mut() };
self.did_an_error_occur()?;
result_ptr
.map(StatementMetadata::new)
.ok_or_else(|| DeserializationError("No metadata exists".into()))
}
fn did_an_error_occur(&self) -> QueryResult<()> {
use crate::result::Error::DatabaseError;
let error_message = self.last_error_message();
if error_message.is_empty() {
Ok(())
} else {
Err(DatabaseError(
self.last_error_type(),
Box::new(error_message),
))
}
}
fn last_error_type(&self) -> DatabaseErrorKind {
let last_error_number = unsafe { ffi::mysql_stmt_errno(self.stmt.as_ptr()) };
// These values are not exposed by the C API, but are documented
// at https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
// and are from the ANSI SQLSTATE standard
match last_error_number {
1062 | 1586 | 1859 => DatabaseErrorKind::UniqueViolation,
1216 | 1217 | 1451 | 1452 | 1830 | 1834 => DatabaseErrorKind::ForeignKeyViolation,
1792 => DatabaseErrorKind::ReadOnlyTransaction,
_ => DatabaseErrorKind::__Unknown,
}
}
}
impl Drop for Statement {
fn drop(&mut self) {
unsafe { ffi::mysql_stmt_close(self.stmt.as_ptr()) };
}
}