Make Encode return a result (#3126)

* Make encode and encode_by_ref fallible

This only changes the trait for now and makes it compile, calling .expect() on all users. Those will be removed in a later commit.

* PgNumeric: Turn TryFrom Decimal to an infallible From

* Turn panics in Encode implementations into errors

* Add Encode error analogous to the Decode error

* Propagate decode errors through Arguments::add

This pushes the panics one level further to mostly bind calls. Those will also be removed later.

* Only check argument encoding at the end

* Use Result in Query internally

* Implement query_with functions in terms of _with_result

* Surface encode errors when executing a query.

* Remove remaining panics in AnyConnectionBackend implementations

* PostgreSQL BigDecimal: Return encode error immediately

* Arguments: Add len method to report how many arguments were added

* Query::bind: Report which argument failed to encode

* IsNull: Add is_null method

* MySqlArguments: Replace manual bitmap code with NullBitMap helper type

* Roll back buffer in MySqlArguments if encoding fails

* Roll back buffer in SqliteArguments if encoding fails

* Roll back PgArgumentBuffer if encoding fails
This commit is contained in:
Max Bruckner
2024-05-31 21:42:36 +02:00
committed by GitHub
parent 6c1e3a4e61
commit c57b46ceb6
92 changed files with 791 additions and 455 deletions

View File

@@ -7,6 +7,7 @@ use libsqlite3_sys::SQLITE_OK;
use std::borrow::Cow;
pub(crate) use sqlx_core::arguments::*;
use sqlx_core::error::BoxDynError;
#[derive(Debug, Clone)]
pub enum SqliteArgumentValue<'q> {
@@ -24,13 +25,23 @@ pub struct SqliteArguments<'q> {
}
impl<'q> SqliteArguments<'q> {
pub(crate) fn add<T>(&mut self, value: T)
pub(crate) fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
where
T: Encode<'q, Sqlite>,
{
if let IsNull::Yes = value.encode(&mut self.values) {
self.values.push(SqliteArgumentValue::Null);
}
let value_length_before_encoding = self.values.len();
match value.encode(&mut self.values) {
Ok(IsNull::Yes) => self.values.push(SqliteArgumentValue::Null),
Ok(IsNull::No) => {}
Err(error) => {
// reset the value buffer to its previous value if encoding failed so we don't leave a half-encoded value behind
self.values.truncate(value_length_before_encoding);
return Err(error);
}
};
Ok(())
}
pub(crate) fn into_static(self) -> SqliteArguments<'static> {
@@ -51,12 +62,16 @@ impl<'q> Arguments<'q> for SqliteArguments<'q> {
self.values.reserve(len);
}
fn add<T>(&mut self, value: T)
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
where
T: Encode<'q, Self::Database>,
{
self.add(value)
}
fn len(&self) -> usize {
self.values.len()
}
}
impl SqliteArguments<'_> {

View File

@@ -3,11 +3,12 @@ use crate::{
};
use futures_core::future::BoxFuture;
use futures_core::stream::BoxStream;
use futures_util::{TryFutureExt, TryStreamExt};
use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
use sqlx_core::describe::Describe;
use sqlx_core::error::Error;
use sqlx_core::executor::{Execute, Executor};
use sqlx_core::Either;
use std::future;
impl<'c> Executor<'c> for &'c mut SqliteConnection {
type Database = Sqlite;
@@ -21,7 +22,10 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
E: Execute<'q, Self::Database>,
{
let sql = query.sql();
let arguments = query.take_arguments();
let arguments = match query.take_arguments().map_err(Error::Encode) {
Ok(arguments) => arguments,
Err(error) => return stream::once(future::ready(Err(error))).boxed(),
};
let persistent = query.persistent() && arguments.is_some();
Box::pin(
@@ -41,7 +45,10 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
E: Execute<'q, Self::Database>,
{
let sql = query.sql();
let arguments = query.take_arguments();
let arguments = match query.take_arguments().map_err(Error::Encode) {
Ok(arguments) => arguments,
Err(error) => return future::ready(Err(error)).boxed(),
};
let persistent = query.persistent() && arguments.is_some();
Box::pin(async move {

View File

@@ -16,10 +16,13 @@ impl Type<Sqlite> for bool {
}
impl<'q> Encode<'q, Sqlite> for bool {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int((*self).into()));
IsNull::No
Ok(IsNull::No)
}
}

View File

@@ -18,10 +18,13 @@ impl Type<Sqlite> for [u8] {
}
impl<'q> Encode<'q, Sqlite> for &'q [u8] {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Borrowed(self)));
IsNull::No
Ok(IsNull::No)
}
}
@@ -42,18 +45,21 @@ impl Type<Sqlite> for Box<[u8]> {
}
impl Encode<'_, Sqlite> for Box<[u8]> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self.into_vec())));
IsNull::No
Ok(IsNull::No)
}
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(
self.clone().into_vec(),
)));
IsNull::No
Ok(IsNull::No)
}
}
@@ -74,16 +80,19 @@ impl Type<Sqlite> for Vec<u8> {
}
impl<'q> Encode<'q, Sqlite> for Vec<u8> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self)));
IsNull::No
Ok(IsNull::No)
}
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self.clone())));
IsNull::No
Ok(IsNull::No)
}
}

View File

@@ -65,25 +65,25 @@ impl<Tz: TimeZone> Encode<'_, Sqlite> for DateTime<Tz>
where
Tz::Offset: Display,
{
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.to_rfc3339_opts(SecondsFormat::AutoSi, false), buf)
}
}
impl Encode<'_, Sqlite> for NaiveDateTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format("%F %T%.f").to_string(), buf)
}
}
impl Encode<'_, Sqlite> for NaiveDate {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format("%F").to_string(), buf)
}
}
impl Encode<'_, Sqlite> for NaiveTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format("%T%.f").to_string(), buf)
}
}

View File

@@ -12,10 +12,13 @@ impl Type<Sqlite> for f32 {
}
impl<'q> Encode<'q, Sqlite> for f32 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Double((*self).into()));
IsNull::No
Ok(IsNull::No)
}
}
@@ -32,10 +35,13 @@ impl Type<Sqlite> for f64 {
}
impl<'q> Encode<'q, Sqlite> for f64 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Double(*self));
IsNull::No
Ok(IsNull::No)
}
}

View File

@@ -16,10 +16,13 @@ impl Type<Sqlite> for i8 {
}
impl<'q> Encode<'q, Sqlite> for i8 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
IsNull::No
Ok(IsNull::No)
}
}
@@ -44,10 +47,13 @@ impl Type<Sqlite> for i16 {
}
impl<'q> Encode<'q, Sqlite> for i16 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
IsNull::No
Ok(IsNull::No)
}
}
@@ -68,10 +74,13 @@ impl Type<Sqlite> for i32 {
}
impl<'q> Encode<'q, Sqlite> for i32 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self));
IsNull::No
Ok(IsNull::No)
}
}
@@ -92,10 +101,13 @@ impl Type<Sqlite> for i64 {
}
impl<'q> Encode<'q, Sqlite> for i64 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int64(*self));
IsNull::No
Ok(IsNull::No)
}
}

View File

@@ -20,8 +20,8 @@ impl<T> Encode<'_, Sqlite> for Json<T>
where
T: Serialize,
{
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
Encode::<Sqlite>::encode(self.encode_to_string(), buf)
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.encode_to_string()?, buf)
}
}

View File

@@ -14,10 +14,13 @@ impl Type<Sqlite> for str {
}
impl<'q> Encode<'q, Sqlite> for &'q str {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Borrowed(*self)));
IsNull::No
Ok(IsNull::No)
}
}
@@ -34,18 +37,21 @@ impl Type<Sqlite> for Box<str> {
}
impl Encode<'_, Sqlite> for Box<str> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.into_string())));
IsNull::No
Ok(IsNull::No)
}
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(
self.clone().into_string(),
)));
IsNull::No
Ok(IsNull::No)
}
}
@@ -62,16 +68,19 @@ impl Type<Sqlite> for String {
}
impl<'q> Encode<'q, Sqlite> for String {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self)));
IsNull::No
Ok(IsNull::No)
}
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.clone())));
IsNull::No
Ok(IsNull::No)
}
}
@@ -92,16 +101,19 @@ impl Type<Sqlite> for Cow<'_, str> {
}
impl<'q> Encode<'q, Sqlite> for Cow<'q, str> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(self));
IsNull::No
Ok(IsNull::No)
}
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(self.clone()));
IsNull::No
Ok(IsNull::No)
}
}

View File

@@ -20,7 +20,7 @@ impl<'q, T> Encode<'q, Sqlite> for Text<T>
where
T: Display,
{
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.0.to_string(), buf)
}
}

View File

@@ -55,29 +55,29 @@ impl Type<Sqlite> for Time {
}
impl Encode<'_, Sqlite> for OffsetDateTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
Encode::<Sqlite>::encode(self.format(&Rfc3339).unwrap(), buf)
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format(&Rfc3339)?, buf)
}
}
impl Encode<'_, Sqlite> for PrimitiveDateTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
let format = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
Encode::<Sqlite>::encode(self.format(&format).unwrap(), buf)
Encode::<Sqlite>::encode(self.format(&format)?, buf)
}
}
impl Encode<'_, Sqlite> for Date {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
let format = fd!("[year]-[month]-[day]");
Encode::<Sqlite>::encode(self.format(&format).unwrap(), buf)
Encode::<Sqlite>::encode(self.format(&format)?, buf)
}
}
impl Encode<'_, Sqlite> for Time {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> IsNull {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
let format = fd!("[hour]:[minute]:[second].[subsecond]");
Encode::<Sqlite>::encode(self.format(&format).unwrap(), buf)
Encode::<Sqlite>::encode(self.format(&format)?, buf)
}
}

View File

@@ -16,10 +16,13 @@ impl Type<Sqlite> for u8 {
}
impl<'q> Encode<'q, Sqlite> for u8 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
IsNull::No
Ok(IsNull::No)
}
}
@@ -44,10 +47,13 @@ impl Type<Sqlite> for u16 {
}
impl<'q> Encode<'q, Sqlite> for u16 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
IsNull::No
Ok(IsNull::No)
}
}
@@ -68,10 +74,13 @@ impl Type<Sqlite> for u32 {
}
impl<'q> Encode<'q, Sqlite> for u32 {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int64(*self as i64));
IsNull::No
Ok(IsNull::No)
}
}

View File

@@ -21,12 +21,15 @@ impl Type<Sqlite> for Uuid {
}
impl<'q> Encode<'q, Sqlite> for Uuid {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(
self.as_bytes().to_vec(),
)));
IsNull::No
Ok(IsNull::No)
}
}
@@ -44,10 +47,13 @@ impl Type<Sqlite> for Hyphenated {
}
impl<'q> Encode<'q, Sqlite> for Hyphenated {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string())));
IsNull::No
Ok(IsNull::No)
}
}
@@ -67,10 +73,13 @@ impl Type<Sqlite> for Simple {
}
impl<'q> Encode<'q, Sqlite> for Simple {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string())));
IsNull::No
Ok(IsNull::No)
}
}