Skip to content

Commit cf00d54

Browse files
FSMaxBjrasanen
authored andcommitted
Make Encode return a result (launchbadge#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
1 parent 742dcf1 commit cf00d54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+792
-456
lines changed

sqlx-core/src/any/arguments.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::any::value::AnyValueKind;
22
use crate::any::Any;
33
use crate::arguments::Arguments;
4-
use crate::encode::Encode;
4+
use crate::encode::{Encode, IsNull};
5+
use crate::error::BoxDynError;
56
use crate::types::Type;
67

78
pub struct AnyArguments<'q> {
@@ -16,11 +17,16 @@ impl<'q> Arguments<'q> for AnyArguments<'q> {
1617
self.values.0.reserve(additional);
1718
}
1819

19-
fn add<T>(&mut self, value: T)
20+
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
2021
where
2122
T: 'q + Encode<'q, Self::Database> + Type<Self::Database>,
2223
{
23-
let _ = value.encode(&mut self.values);
24+
let _: IsNull = value.encode(&mut self.values)?;
25+
Ok(())
26+
}
27+
28+
fn len(&self) -> usize {
29+
self.values.0.len()
2430
}
2531
}
2632

@@ -36,7 +42,7 @@ impl<'q> Default for AnyArguments<'q> {
3642

3743
impl<'q> AnyArguments<'q> {
3844
#[doc(hidden)]
39-
pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> A
45+
pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> Result<A, BoxDynError>
4046
where
4147
'q: 'a,
4248
Option<i32>: Type<A::Database> + Encode<'a, A::Database>,
@@ -62,9 +68,9 @@ impl<'q> AnyArguments<'q> {
6268
AnyValueKind::Double(d) => out.add(d),
6369
AnyValueKind::Text(t) => out.add(&**t),
6470
AnyValueKind::Blob(b) => out.add(&**b),
65-
}
71+
}?
6672
}
6773

68-
out
74+
Ok(out)
6975
}
7076
}

sqlx-core/src/any/connection/executor.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use crate::executor::{Execute, Executor};
55
use either::Either;
66
use futures_core::future::BoxFuture;
77
use futures_core::stream::BoxStream;
8+
use futures_util::{stream, FutureExt, StreamExt};
9+
use std::future;
810

911
impl<'c> Executor<'c> for &'c mut AnyConnection {
1012
type Database = Any;
@@ -17,7 +19,10 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
1719
'c: 'e,
1820
E: Execute<'q, Any>,
1921
{
20-
let arguments = query.take_arguments();
22+
let arguments = match query.take_arguments().map_err(Error::Encode) {
23+
Ok(arguments) => arguments,
24+
Err(error) => return stream::once(future::ready(Err(error))).boxed(),
25+
};
2126
self.backend.fetch_many(query.sql(), arguments)
2227
}
2328

@@ -29,7 +34,10 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
2934
'c: 'e,
3035
E: Execute<'q, Self::Database>,
3136
{
32-
let arguments = query.take_arguments();
37+
let arguments = match query.take_arguments().map_err(Error::Encode) {
38+
Ok(arguments) => arguments,
39+
Err(error) => return future::ready(Err(error)).boxed(),
40+
};
3341
self.backend.fetch_optional(query.sql(), arguments)
3442
}
3543

sqlx-core/src/any/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@ impl<'q, T> Encode<'q, Any> for Option<T>
6767
where
6868
T: Encode<'q, Any> + 'q,
6969
{
70-
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> crate::encode::IsNull {
70+
fn encode_by_ref(
71+
&self,
72+
buf: &mut AnyArgumentBuffer<'q>,
73+
) -> Result<crate::encode::IsNull, crate::error::BoxDynError> {
7174
if let Some(value) = self {
7275
value.encode_by_ref(buf)
7376
} else {
7477
buf.0.push(AnyValueKind::Null);
75-
crate::encode::IsNull::Yes
78+
Ok(crate::encode::IsNull::Yes)
7679
}
7780
}
7881
}

sqlx-core/src/any/types/blob.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ impl Type<Any> for [u8] {
1515
}
1616

1717
impl<'q> Encode<'q, Any> for &'q [u8] {
18-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
18+
fn encode_by_ref(
19+
&self,
20+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
21+
) -> Result<IsNull, BoxDynError> {
1922
buf.0.push(AnyValueKind::Blob((*self).into()));
20-
IsNull::No
23+
Ok(IsNull::No)
2124
}
2225
}
2326

@@ -42,9 +45,12 @@ impl Type<Any> for Vec<u8> {
4245
}
4346

4447
impl<'q> Encode<'q, Any> for Vec<u8> {
45-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
48+
fn encode_by_ref(
49+
&self,
50+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
51+
) -> Result<IsNull, BoxDynError> {
4652
buf.0.push(AnyValueKind::Blob(Cow::Owned(self.clone())));
47-
IsNull::No
53+
Ok(IsNull::No)
4854
}
4955
}
5056

sqlx-core/src/any/types/bool.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ impl Type<Any> for bool {
1414
}
1515

1616
impl<'q> Encode<'q, Any> for bool {
17-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
17+
fn encode_by_ref(
18+
&self,
19+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
20+
) -> Result<IsNull, BoxDynError> {
1821
buf.0.push(AnyValueKind::Bool(*self));
19-
IsNull::No
22+
Ok(IsNull::No)
2023
}
2124
}
2225

sqlx-core/src/any/types/float.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ impl Type<Any> for f32 {
1414
}
1515

1616
impl<'q> Encode<'q, Any> for f32 {
17-
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> IsNull {
17+
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
1818
buf.0.push(AnyValueKind::Real(*self));
19-
IsNull::No
19+
Ok(IsNull::No)
2020
}
2121
}
2222

@@ -38,9 +38,12 @@ impl Type<Any> for f64 {
3838
}
3939

4040
impl<'q> Encode<'q, Any> for f64 {
41-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
41+
fn encode_by_ref(
42+
&self,
43+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
44+
) -> Result<IsNull, BoxDynError> {
4245
buf.0.push(AnyValueKind::Double(*self));
43-
IsNull::No
46+
Ok(IsNull::No)
4447
}
4548
}
4649

sqlx-core/src/any/types/int.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ impl Type<Any> for i16 {
1818
}
1919

2020
impl<'q> Encode<'q, Any> for i16 {
21-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
21+
fn encode_by_ref(
22+
&self,
23+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
24+
) -> Result<IsNull, BoxDynError> {
2225
buf.0.push(AnyValueKind::SmallInt(*self));
23-
IsNull::No
26+
Ok(IsNull::No)
2427
}
2528
}
2629

@@ -43,9 +46,12 @@ impl Type<Any> for i32 {
4346
}
4447

4548
impl<'q> Encode<'q, Any> for i32 {
46-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
49+
fn encode_by_ref(
50+
&self,
51+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
52+
) -> Result<IsNull, BoxDynError> {
4753
buf.0.push(AnyValueKind::Integer(*self));
48-
IsNull::No
54+
Ok(IsNull::No)
4955
}
5056
}
5157

@@ -68,9 +74,12 @@ impl Type<Any> for i64 {
6874
}
6975

7076
impl<'q> Encode<'q, Any> for i64 {
71-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
77+
fn encode_by_ref(
78+
&self,
79+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
80+
) -> Result<IsNull, BoxDynError> {
7281
buf.0.push(AnyValueKind::BigInt(*self));
73-
IsNull::No
82+
Ok(IsNull::No)
7483
}
7584
}
7685

sqlx-core/src/any/types/str.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ impl Type<Any> for str {
1616
}
1717

1818
impl<'a> Encode<'a, Any> for &'a str {
19-
fn encode(self, buf: &mut <Any as Database>::ArgumentBuffer<'a>) -> IsNull
19+
fn encode(self, buf: &mut <Any as Database>::ArgumentBuffer<'a>) -> Result<IsNull, BoxDynError>
2020
where
2121
Self: Sized,
2222
{
2323
buf.0.push(AnyValueKind::Text(self.into()));
24-
IsNull::No
24+
Ok(IsNull::No)
2525
}
2626

27-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'a>) -> IsNull {
27+
fn encode_by_ref(
28+
&self,
29+
buf: &mut <Any as Database>::ArgumentBuffer<'a>,
30+
) -> Result<IsNull, BoxDynError> {
2831
(*self).encode(buf)
2932
}
3033
}
@@ -50,9 +53,12 @@ impl Type<Any> for String {
5053
}
5154

5255
impl<'q> Encode<'q, Any> for String {
53-
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
56+
fn encode_by_ref(
57+
&self,
58+
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
59+
) -> Result<IsNull, BoxDynError> {
5460
buf.0.push(AnyValueKind::Text(Cow::Owned(self.clone())));
55-
IsNull::No
61+
Ok(IsNull::No)
5662
}
5763
}
5864

sqlx-core/src/arguments.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::database::Database;
44
use crate::encode::Encode;
5+
use crate::error::BoxDynError;
56
use crate::types::Type;
67
use std::fmt::{self, Write};
78

@@ -14,10 +15,13 @@ pub trait Arguments<'q>: Send + Sized + Default {
1415
fn reserve(&mut self, additional: usize, size: usize);
1516

1617
/// Add the value to the end of the arguments.
17-
fn add<T>(&mut self, value: T)
18+
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
1819
where
1920
T: 'q + Encode<'q, Self::Database> + Type<Self::Database>;
2021

22+
/// The number of arguments that were already added.
23+
fn len(&self) -> usize;
24+
2125
fn format_placeholder<W: Write>(&self, writer: &mut W) -> fmt::Result {
2226
writer.write_str("?")
2327
}

sqlx-core/src/encode.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
use std::mem;
44

55
use crate::database::Database;
6+
use crate::error::BoxDynError;
67

78
/// The return type of [Encode::encode].
9+
#[must_use]
810
pub enum IsNull {
911
/// The value is null; no data was written.
1012
Yes,
@@ -15,11 +17,16 @@ pub enum IsNull {
1517
No,
1618
}
1719

20+
impl IsNull {
21+
pub fn is_null(&self) -> bool {
22+
matches!(self, IsNull::Yes)
23+
}
24+
}
25+
1826
/// Encode a single value to be sent to the database.
1927
pub trait Encode<'q, DB: Database> {
2028
/// Writes the value of `self` into `buf` in the expected format for the database.
21-
#[must_use]
22-
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull
29+
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> Result<IsNull, BoxDynError>
2330
where
2431
Self: Sized,
2532
{
@@ -30,8 +37,10 @@ pub trait Encode<'q, DB: Database> {
3037
///
3138
/// Where possible, make use of `encode` instead as it can take advantage of re-using
3239
/// memory.
33-
#[must_use]
34-
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull;
40+
fn encode_by_ref(
41+
&self,
42+
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
43+
) -> Result<IsNull, BoxDynError>;
3544

3645
fn produces(&self) -> Option<DB::TypeInfo> {
3746
// `produces` is inherently a hook to allow database drivers to produce value-dependent
@@ -50,12 +59,15 @@ where
5059
T: Encode<'q, DB>,
5160
{
5261
#[inline]
53-
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
62+
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
5463
<T as Encode<DB>>::encode_by_ref(self, buf)
5564
}
5665

5766
#[inline]
58-
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
67+
fn encode_by_ref(
68+
&self,
69+
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
70+
) -> Result<IsNull, BoxDynError> {
5971
<&T as Encode<DB>>::encode(self, buf)
6072
}
6173

@@ -90,23 +102,23 @@ macro_rules! impl_encode_for_option {
90102
fn encode(
91103
self,
92104
buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer<'q>,
93-
) -> $crate::encode::IsNull {
105+
) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> {
94106
if let Some(v) = self {
95107
v.encode(buf)
96108
} else {
97-
$crate::encode::IsNull::Yes
109+
Ok($crate::encode::IsNull::Yes)
98110
}
99111
}
100112

101113
#[inline]
102114
fn encode_by_ref(
103115
&self,
104116
buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer<'q>,
105-
) -> $crate::encode::IsNull {
117+
) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> {
106118
if let Some(v) = self {
107119
v.encode_by_ref(buf)
108120
} else {
109-
$crate::encode::IsNull::Yes
121+
Ok($crate::encode::IsNull::Yes)
110122
}
111123
}
112124

sqlx-core/src/error.rs

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ pub enum Error {
7878
source: BoxDynError,
7979
},
8080

81+
/// Error occured while encoding a value.
82+
#[error("error occured while encoding a value: {0}")]
83+
Encode(#[source] BoxDynError),
84+
8185
/// Error occurred while decoding a value.
8286
#[error("error occurred while decoding: {0}")]
8387
Decode(#[source] BoxDynError),

0 commit comments

Comments
 (0)