mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 06:24:16 +00:00
sqlite: produce connection specific errors
postgres, mysql: use derive for Debug for error types
This commit is contained in:
parent
f667910cbf
commit
68d4a0d258
8 changed files with 65 additions and 33 deletions
|
@ -223,19 +223,6 @@ macro_rules! tls_err {
|
|||
#[allow(unused_macros)]
|
||||
macro_rules! impl_fmt_error {
|
||||
($err:ty) => {
|
||||
impl std::fmt::Debug for $err {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("DatabaseError")
|
||||
.field("message", &self.message())
|
||||
.field("details", &self.details())
|
||||
.field("hint", &self.hint())
|
||||
.field("table_name", &self.table_name())
|
||||
.field("column_name", &self.column_name())
|
||||
.field("constraint_name", &self.constraint_name())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for $err {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.pad(self.message())
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use crate::error::DatabaseError;
|
||||
use crate::mysql::protocol::ErrPacket;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MySqlError(pub(super) ErrPacket);
|
||||
|
||||
impl Display for MySqlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad(self.message())
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseError for MySqlError {
|
||||
fn message(&self) -> &str {
|
||||
&*self.0.error_message
|
||||
}
|
||||
}
|
||||
|
||||
impl_fmt_error!(MySqlError);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use crate::error::DatabaseError;
|
||||
use crate::postgres::protocol::Response;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PgError(pub(super) Response);
|
||||
|
||||
impl DatabaseError for PgError {
|
||||
|
@ -29,4 +32,8 @@ impl DatabaseError for PgError {
|
|||
}
|
||||
}
|
||||
|
||||
impl_fmt_error!(PgError);
|
||||
impl Display for PgError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad(self.message())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ impl SqliteArgumentValue {
|
|||
};
|
||||
|
||||
if status != SQLITE_OK {
|
||||
return Err(SqliteError::new(status).into());
|
||||
return Err(SqliteError::from_connection(statement.connection.0.as_ptr()).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -20,11 +20,11 @@ use crate::url::Url;
|
|||
|
||||
/// Thin wrapper around [sqlite3] to impl `Send`.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct SqliteConnectionHandle(NonNull<sqlite3>);
|
||||
pub(super) struct SqliteConnectionHandle(pub(super) NonNull<sqlite3>);
|
||||
|
||||
/// A connection to a [SQLite][super::Sqlite] database.
|
||||
pub struct SqliteConnection {
|
||||
handle: SqliteConnectionHandle,
|
||||
pub(super) handle: SqliteConnectionHandle,
|
||||
pub(super) worker: Worker,
|
||||
// Storage of the most recently prepared, non-persistent statement
|
||||
pub(super) statement: Option<SqliteStatement>,
|
||||
|
@ -74,7 +74,7 @@ async fn establish(url: crate::Result<Url>) -> crate::Result<SqliteConnection> {
|
|||
let status = unsafe { sqlite3_open_v2(filename.as_ptr(), &mut handle, flags, null()) };
|
||||
|
||||
if status != SQLITE_OK {
|
||||
return Err(SqliteError::new(status).into());
|
||||
return Err(SqliteError::from_connection(handle).into());
|
||||
}
|
||||
|
||||
// Enable extended result codes
|
||||
|
|
|
@ -1,35 +1,48 @@
|
|||
use crate::error::DatabaseError;
|
||||
use libsqlite3_sys::sqlite3_errstr;
|
||||
use bitflags::_core::str::from_utf8_unchecked;
|
||||
use libsqlite3_sys::{sqlite3, sqlite3_errmsg, sqlite3_extended_errcode};
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::{self, Display};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SqliteError {
|
||||
#[allow(dead_code)]
|
||||
code: c_int,
|
||||
message: String,
|
||||
}
|
||||
|
||||
// Error Codes And Messages
|
||||
// https://www.sqlite.org/c3ref/errcode.html
|
||||
|
||||
impl SqliteError {
|
||||
pub(crate) fn new(code: c_int) -> Self {
|
||||
pub(super) fn from_connection(conn: *mut sqlite3) -> Self {
|
||||
#[allow(unsafe_code)]
|
||||
let code: c_int = unsafe { sqlite3_extended_errcode(conn) };
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
let message = unsafe {
|
||||
let err = sqlite3_errstr(code);
|
||||
let err = sqlite3_errmsg(conn);
|
||||
debug_assert!(!err.is_null());
|
||||
|
||||
CStr::from_ptr(err)
|
||||
from_utf8_unchecked(CStr::from_ptr(err).to_bytes())
|
||||
};
|
||||
|
||||
Self {
|
||||
code,
|
||||
message: message.to_string_lossy().into_owned(),
|
||||
message: message.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SqliteError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad(self.message())
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseError for SqliteError {
|
||||
fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
|
||||
impl_fmt_error!(SqliteError);
|
||||
|
|
|
@ -11,6 +11,7 @@ use libsqlite3_sys::{
|
|||
SQLITE_PREPARE_NO_VTAB, SQLITE_PREPARE_PERSISTENT, SQLITE_ROW,
|
||||
};
|
||||
|
||||
use crate::sqlite::connection::SqliteConnectionHandle;
|
||||
use crate::sqlite::worker::Worker;
|
||||
use crate::sqlite::SqliteError;
|
||||
use crate::sqlite::{SqliteArguments, SqliteConnection};
|
||||
|
@ -34,6 +35,7 @@ pub(super) struct SqliteStatementHandle(NonNull<sqlite3_stmt>);
|
|||
/// The statement is finalized ( `sqlite3_finalize` ) on drop.
|
||||
pub(super) struct SqliteStatement {
|
||||
handle: SqliteStatementHandle,
|
||||
pub(super) connection: SqliteConnectionHandle,
|
||||
pub(super) worker: Worker,
|
||||
pub(super) tail: usize,
|
||||
pub(super) columns: HashMap<String, usize>,
|
||||
|
@ -80,17 +82,18 @@ impl SqliteStatement {
|
|||
)
|
||||
};
|
||||
|
||||
if status != SQLITE_OK {
|
||||
return Err(SqliteError::from_connection(conn.handle()).into());
|
||||
}
|
||||
|
||||
// If pzTail is not NULL then *pzTail is made to point to the first byte
|
||||
// past the end of the first SQL statement in zSql.
|
||||
let tail = (tail as usize) - (query_ptr as usize);
|
||||
*query = &query[tail..].trim();
|
||||
|
||||
if status != SQLITE_OK {
|
||||
return Err(SqliteError::new(status).into());
|
||||
}
|
||||
|
||||
let mut self_ = Self {
|
||||
worker: conn.worker.clone(),
|
||||
connection: conn.handle,
|
||||
handle: SqliteStatementHandle(NonNull::new(statement_handle).unwrap()),
|
||||
columns: HashMap::new(),
|
||||
tail,
|
||||
|
@ -215,8 +218,8 @@ impl SqliteStatement {
|
|||
|
||||
SQLITE_ROW => Ok(Step::Row),
|
||||
|
||||
status => {
|
||||
return Err(SqliteError::new(status).into());
|
||||
_ => {
|
||||
return Err(SqliteError::from_connection(self.connection.0.as_ptr()).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,21 @@ async fn it_fails_to_connect() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn it_fails_to_parse() -> anyhow::Result<()> {
|
||||
let mut conn = new::<Sqlite>().await?;
|
||||
let res = conn.execute("SEELCT 1").await;
|
||||
|
||||
assert!(res.is_err());
|
||||
|
||||
let err = res.unwrap_err().to_string();
|
||||
|
||||
assert_eq!("near \"SEELCT\": syntax error", err);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn it_executes() -> anyhow::Result<()> {
|
||||
|
|
Loading…
Reference in a new issue