diff --git a/sqlx-core/src/error.rs b/sqlx-core/src/error.rs index 62cd437f..901a0ff4 100644 --- a/sqlx-core/src/error.rs +++ b/sqlx-core/src/error.rs @@ -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()) diff --git a/sqlx-core/src/mysql/error.rs b/sqlx-core/src/mysql/error.rs index 8ff1cb5d..a90649a6 100644 --- a/sqlx-core/src/mysql/error.rs +++ b/sqlx-core/src/mysql/error.rs @@ -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); diff --git a/sqlx-core/src/postgres/error.rs b/sqlx-core/src/postgres/error.rs index 537d0d2d..e6f024ed 100644 --- a/sqlx-core/src/postgres/error.rs +++ b/sqlx-core/src/postgres/error.rs @@ -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()) + } +} diff --git a/sqlx-core/src/sqlite/arguments.rs b/sqlx-core/src/sqlite/arguments.rs index eb863309..51f2f745 100644 --- a/sqlx-core/src/sqlite/arguments.rs +++ b/sqlx-core/src/sqlite/arguments.rs @@ -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(()) diff --git a/sqlx-core/src/sqlite/connection.rs b/sqlx-core/src/sqlite/connection.rs index 7dea21cf..215515a1 100644 --- a/sqlx-core/src/sqlite/connection.rs +++ b/sqlx-core/src/sqlite/connection.rs @@ -20,11 +20,11 @@ use crate::url::Url; /// Thin wrapper around [sqlite3] to impl `Send`. #[derive(Clone, Copy)] -pub(super) struct SqliteConnectionHandle(NonNull); +pub(super) struct SqliteConnectionHandle(pub(super) NonNull); /// 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, @@ -74,7 +74,7 @@ async fn establish(url: crate::Result) -> crate::Result { 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 diff --git a/sqlx-core/src/sqlite/error.rs b/sqlx-core/src/sqlite/error.rs index 79c3d57f..13b83fa0 100644 --- a/sqlx-core/src/sqlite/error.rs +++ b/sqlx-core/src/sqlite/error.rs @@ -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); diff --git a/sqlx-core/src/sqlite/statement.rs b/sqlx-core/src/sqlite/statement.rs index 33ccc4db..0782edd1 100644 --- a/sqlx-core/src/sqlite/statement.rs +++ b/sqlx-core/src/sqlite/statement.rs @@ -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); /// 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, @@ -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()); } } } diff --git a/tests/sqlite.rs b/tests/sqlite.rs index af4b1e03..48fd696e 100644 --- a/tests/sqlite.rs +++ b/tests/sqlite.rs @@ -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::().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<()> {