mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 06:24:16 +00:00
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:
parent
6c1e3a4e61
commit
c57b46ceb6
92 changed files with 791 additions and 455 deletions
|
@ -1,7 +1,8 @@
|
|||
use crate::any::value::AnyValueKind;
|
||||
use crate::any::Any;
|
||||
use crate::arguments::Arguments;
|
||||
use crate::encode::Encode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::types::Type;
|
||||
|
||||
pub struct AnyArguments<'q> {
|
||||
|
@ -16,11 +17,16 @@ impl<'q> Arguments<'q> for AnyArguments<'q> {
|
|||
self.values.0.reserve(additional);
|
||||
}
|
||||
|
||||
fn add<T>(&mut self, value: T)
|
||||
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: 'q + Encode<'q, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
let _ = value.encode(&mut self.values);
|
||||
let _: IsNull = value.encode(&mut self.values)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.values.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +42,7 @@ impl<'q> Default for AnyArguments<'q> {
|
|||
|
||||
impl<'q> AnyArguments<'q> {
|
||||
#[doc(hidden)]
|
||||
pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> A
|
||||
pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> Result<A, BoxDynError>
|
||||
where
|
||||
'q: 'a,
|
||||
Option<i32>: Type<A::Database> + Encode<'a, A::Database>,
|
||||
|
@ -62,9 +68,9 @@ impl<'q> AnyArguments<'q> {
|
|||
AnyValueKind::Double(d) => out.add(d),
|
||||
AnyValueKind::Text(t) => out.add(&**t),
|
||||
AnyValueKind::Blob(b) => out.add(&**b),
|
||||
}
|
||||
}?
|
||||
}
|
||||
|
||||
out
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use crate::executor::{Execute, Executor};
|
|||
use either::Either;
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{stream, FutureExt, StreamExt};
|
||||
use std::future;
|
||||
|
||||
impl<'c> Executor<'c> for &'c mut AnyConnection {
|
||||
type Database = Any;
|
||||
|
@ -17,7 +19,10 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
|
|||
'c: 'e,
|
||||
E: Execute<'q, Any>,
|
||||
{
|
||||
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(),
|
||||
};
|
||||
self.backend.fetch_many(query.sql(), arguments)
|
||||
}
|
||||
|
||||
|
@ -29,7 +34,10 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
|
|||
'c: 'e,
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
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(),
|
||||
};
|
||||
self.backend.fetch_optional(query.sql(), arguments)
|
||||
}
|
||||
|
||||
|
|
|
@ -67,12 +67,15 @@ impl<'q, T> Encode<'q, Any> for Option<T>
|
|||
where
|
||||
T: Encode<'q, Any> + 'q,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> crate::encode::IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut AnyArgumentBuffer<'q>,
|
||||
) -> Result<crate::encode::IsNull, crate::error::BoxDynError> {
|
||||
if let Some(value) = self {
|
||||
value.encode_by_ref(buf)
|
||||
} else {
|
||||
buf.0.push(AnyValueKind::Null);
|
||||
crate::encode::IsNull::Yes
|
||||
Ok(crate::encode::IsNull::Yes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,12 @@ impl Type<Any> for [u8] {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for &'q [u8] {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Blob((*self).into()));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,9 +45,12 @@ impl Type<Any> for Vec<u8> {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for Vec<u8> {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Blob(Cow::Owned(self.clone())));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,12 @@ impl Type<Any> for bool {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for bool {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Bool(*self));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ impl Type<Any> for f32 {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for f32 {
|
||||
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Real(*self));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,9 +38,12 @@ impl Type<Any> for f64 {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for f64 {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Double(*self));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,12 @@ impl Type<Any> for i16 {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for i16 {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::SmallInt(*self));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,9 +46,12 @@ impl Type<Any> for i32 {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for i32 {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Integer(*self));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,9 +74,12 @@ impl Type<Any> for i64 {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for i64 {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::BigInt(*self));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,15 +16,18 @@ impl Type<Any> for str {
|
|||
}
|
||||
|
||||
impl<'a> Encode<'a, Any> for &'a str {
|
||||
fn encode(self, buf: &mut <Any as Database>::ArgumentBuffer<'a>) -> IsNull
|
||||
fn encode(self, buf: &mut <Any as Database>::ArgumentBuffer<'a>) -> Result<IsNull, BoxDynError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
buf.0.push(AnyValueKind::Text(self.into()));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'a>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'a>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
(*self).encode(buf)
|
||||
}
|
||||
}
|
||||
|
@ -50,9 +53,12 @@ impl Type<Any> for String {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, Any> for String {
|
||||
fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Any as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
buf.0.push(AnyValueKind::Text(Cow::Owned(self.clone())));
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use crate::database::Database;
|
||||
use crate::encode::Encode;
|
||||
use crate::error::BoxDynError;
|
||||
use crate::types::Type;
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
|
@ -14,10 +15,13 @@ pub trait Arguments<'q>: Send + Sized + Default {
|
|||
fn reserve(&mut self, additional: usize, size: usize);
|
||||
|
||||
/// Add the value to the end of the arguments.
|
||||
fn add<T>(&mut self, value: T)
|
||||
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: 'q + Encode<'q, Self::Database> + Type<Self::Database>;
|
||||
|
||||
/// The number of arguments that were already added.
|
||||
fn len(&self) -> usize;
|
||||
|
||||
fn format_placeholder<W: Write>(&self, writer: &mut W) -> fmt::Result {
|
||||
writer.write_str("?")
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
use std::mem;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::error::BoxDynError;
|
||||
|
||||
/// The return type of [Encode::encode].
|
||||
#[must_use]
|
||||
pub enum IsNull {
|
||||
/// The value is null; no data was written.
|
||||
Yes,
|
||||
|
@ -15,11 +17,16 @@ pub enum IsNull {
|
|||
No,
|
||||
}
|
||||
|
||||
impl IsNull {
|
||||
pub fn is_null(&self) -> bool {
|
||||
matches!(self, IsNull::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a single value to be sent to the database.
|
||||
pub trait Encode<'q, DB: Database> {
|
||||
/// Writes the value of `self` into `buf` in the expected format for the database.
|
||||
#[must_use]
|
||||
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull
|
||||
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> Result<IsNull, BoxDynError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -30,8 +37,10 @@ pub trait Encode<'q, DB: Database> {
|
|||
///
|
||||
/// Where possible, make use of `encode` instead as it can take advantage of re-using
|
||||
/// memory.
|
||||
#[must_use]
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull;
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError>;
|
||||
|
||||
fn produces(&self) -> Option<DB::TypeInfo> {
|
||||
// `produces` is inherently a hook to allow database drivers to produce value-dependent
|
||||
|
@ -50,12 +59,15 @@ where
|
|||
T: Encode<'q, DB>,
|
||||
{
|
||||
#[inline]
|
||||
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
|
||||
<T as Encode<DB>>::encode_by_ref(self, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
<&T as Encode<DB>>::encode(self, buf)
|
||||
}
|
||||
|
||||
|
@ -90,11 +102,11 @@ macro_rules! impl_encode_for_option {
|
|||
fn encode(
|
||||
self,
|
||||
buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer<'q>,
|
||||
) -> $crate::encode::IsNull {
|
||||
) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> {
|
||||
if let Some(v) = self {
|
||||
v.encode(buf)
|
||||
} else {
|
||||
$crate::encode::IsNull::Yes
|
||||
Ok($crate::encode::IsNull::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,11 +114,11 @@ macro_rules! impl_encode_for_option {
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer<'q>,
|
||||
) -> $crate::encode::IsNull {
|
||||
) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> {
|
||||
if let Some(v) = self {
|
||||
v.encode_by_ref(buf)
|
||||
} else {
|
||||
$crate::encode::IsNull::Yes
|
||||
Ok($crate::encode::IsNull::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,10 @@ pub enum Error {
|
|||
source: BoxDynError,
|
||||
},
|
||||
|
||||
/// Error occured while encoding a value.
|
||||
#[error("error occured while encoding a value: {0}")]
|
||||
Encode(#[source] BoxDynError),
|
||||
|
||||
/// Error occurred while decoding a value.
|
||||
#[error("error occurred while decoding: {0}")]
|
||||
Decode(#[source] BoxDynError),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::database::Database;
|
||||
use crate::describe::Describe;
|
||||
use crate::error::Error;
|
||||
use crate::error::{BoxDynError, Error};
|
||||
|
||||
use either::Either;
|
||||
use futures_core::future::BoxFuture;
|
||||
|
@ -199,10 +199,12 @@ pub trait Execute<'q, DB: Database>: Send + Sized {
|
|||
|
||||
/// Returns the arguments to be bound against the query string.
|
||||
///
|
||||
/// Returning `None` for `Arguments` indicates to use a "simple" query protocol and to not
|
||||
/// prepare the query. Returning `Some(Default::default())` is an empty arguments object that
|
||||
/// Returning `Ok(None)` for `Arguments` indicates to use a "simple" query protocol and to not
|
||||
/// prepare the query. Returning `Ok(Some(Default::default()))` is an empty arguments object that
|
||||
/// will be prepared (and cached) before execution.
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>>;
|
||||
///
|
||||
/// Returns `Err` if encoding any of the arguments failed.
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError>;
|
||||
|
||||
/// Returns `true` if the statement should be cached.
|
||||
fn persistent(&self) -> bool;
|
||||
|
@ -222,8 +224,8 @@ impl<'q, DB: Database> Execute<'q, DB> for &'q str {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
None
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -244,8 +246,8 @@ impl<'q, DB: Database> Execute<'q, DB> for (&'q str, Option<<DB as Database>::Ar
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
self.1.take()
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
Ok(self.1.take())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -7,7 +7,7 @@ use futures_util::{future, StreamExt, TryFutureExt, TryStreamExt};
|
|||
use crate::arguments::{Arguments, IntoArguments};
|
||||
use crate::database::{Database, HasStatementCache};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
use crate::error::{BoxDynError, Error};
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::statement::Statement;
|
||||
use crate::types::Type;
|
||||
|
@ -16,7 +16,7 @@ use crate::types::Type;
|
|||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct Query<'q, DB: Database, A> {
|
||||
pub(crate) statement: Either<&'q str, &'q DB::Statement<'q>>,
|
||||
pub(crate) arguments: Option<A>,
|
||||
pub(crate) arguments: Option<Result<A, BoxDynError>>,
|
||||
pub(crate) database: PhantomData<DB>,
|
||||
pub(crate) persistent: bool,
|
||||
}
|
||||
|
@ -59,8 +59,11 @@ where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
self.arguments.take().map(IntoArguments::into_arguments)
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
self.arguments
|
||||
.take()
|
||||
.transpose()
|
||||
.map(|option| option.map(IntoArguments::into_arguments))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -78,13 +81,43 @@ impl<'q, DB: Database> Query<'q, DB, <DB as Database>::Arguments<'q>> {
|
|||
///
|
||||
/// There is no validation that the value is of the type expected by the query. Most SQL
|
||||
/// flavors will perform type coercion (Postgres will return a database error).
|
||||
///
|
||||
/// If encoding the value fails, the error is stored and later surfaced when executing the query.
|
||||
pub fn bind<T: 'q + Encode<'q, DB> + Type<DB>>(mut self, value: T) -> Self {
|
||||
if let Some(arguments) = &mut self.arguments {
|
||||
arguments.add(value);
|
||||
let Ok(arguments) = self.get_arguments() else {
|
||||
return self;
|
||||
};
|
||||
|
||||
let argument_number = arguments.len() + 1;
|
||||
if let Err(error) = arguments.add(value) {
|
||||
self.arguments = Some(Err(format!(
|
||||
"Encoding argument ${argument_number} failed: {error}"
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Like [`Query::try_bind`] but immediately returns an error if encoding the value failed.
|
||||
pub fn try_bind<T: 'q + Encode<'q, DB> + Type<DB>>(
|
||||
&mut self,
|
||||
value: T,
|
||||
) -> Result<(), BoxDynError> {
|
||||
let arguments = self.get_arguments()?;
|
||||
|
||||
arguments.add(value)
|
||||
}
|
||||
|
||||
fn get_arguments(&mut self) -> Result<&mut DB::Arguments<'q>, BoxDynError> {
|
||||
let Some(Ok(arguments)) = self.arguments.as_mut().map(Result::as_mut) else {
|
||||
return Err("A previous call to Query::bind produced an error"
|
||||
.to_owned()
|
||||
.into());
|
||||
};
|
||||
|
||||
Ok(arguments)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, A> Query<'q, DB, A>
|
||||
|
@ -280,7 +313,7 @@ where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
self.inner.take_arguments()
|
||||
}
|
||||
|
||||
|
@ -472,7 +505,7 @@ where
|
|||
{
|
||||
Query {
|
||||
database: PhantomData,
|
||||
arguments: Some(Default::default()),
|
||||
arguments: Some(Ok(Default::default())),
|
||||
statement: Either::Right(statement),
|
||||
persistent: true,
|
||||
}
|
||||
|
@ -489,7 +522,7 @@ where
|
|||
{
|
||||
Query {
|
||||
database: PhantomData,
|
||||
arguments: Some(arguments),
|
||||
arguments: Some(Ok(arguments)),
|
||||
statement: Either::Right(statement),
|
||||
persistent: true,
|
||||
}
|
||||
|
@ -625,7 +658,7 @@ where
|
|||
{
|
||||
Query {
|
||||
database: PhantomData,
|
||||
arguments: Some(Default::default()),
|
||||
arguments: Some(Ok(Default::default())),
|
||||
statement: Either::Left(sql),
|
||||
persistent: true,
|
||||
}
|
||||
|
@ -635,6 +668,18 @@ where
|
|||
///
|
||||
/// See [`query()`][query] for details, such as supported syntax.
|
||||
pub fn query_with<'q, DB, A>(sql: &'q str, arguments: A) -> Query<'q, DB, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
{
|
||||
query_with_result(sql, Ok(arguments))
|
||||
}
|
||||
|
||||
/// Same as [`query_with`] but is initialized with a Result of arguments instead
|
||||
pub fn query_with_result<'q, DB, A>(
|
||||
sql: &'q str,
|
||||
arguments: Result<A, BoxDynError>,
|
||||
) -> Query<'q, DB, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
|
|
|
@ -7,10 +7,10 @@ use futures_util::{StreamExt, TryStreamExt};
|
|||
use crate::arguments::IntoArguments;
|
||||
use crate::database::{Database, HasStatementCache};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
use crate::error::{BoxDynError, Error};
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::from_row::FromRow;
|
||||
use crate::query::{query, query_statement, query_statement_with, query_with, Query};
|
||||
use crate::query::{query, query_statement, query_statement_with, query_with_result, Query};
|
||||
use crate::types::Type;
|
||||
|
||||
/// A single SQL query as a prepared statement, mapping results using [`FromRow`].
|
||||
|
@ -37,7 +37,7 @@ where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
self.inner.take_arguments()
|
||||
}
|
||||
|
||||
|
@ -358,13 +358,27 @@ where
|
|||
/// For details about type mapping from [`FromRow`], see [`query_as()`].
|
||||
#[inline]
|
||||
pub fn query_as_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryAs<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
O: for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
query_as_with_result(sql, Ok(arguments))
|
||||
}
|
||||
|
||||
/// Same as [`query_as_with`] but takes arguments as a Result
|
||||
#[inline]
|
||||
pub fn query_as_with_result<'q, DB, O, A>(
|
||||
sql: &'q str,
|
||||
arguments: Result<A, BoxDynError>,
|
||||
) -> QueryAs<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
O: for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
QueryAs {
|
||||
inner: query_with(sql, arguments),
|
||||
inner: query_with_result(sql, arguments),
|
||||
output: PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ where
|
|||
.arguments
|
||||
.as_mut()
|
||||
.expect("BUG: Arguments taken already");
|
||||
arguments.add(value);
|
||||
arguments.add(value).expect("Failed to add argument");
|
||||
|
||||
arguments
|
||||
.format_placeholder(&mut self.query)
|
||||
|
@ -450,7 +450,7 @@ where
|
|||
|
||||
Query {
|
||||
statement: Either::Left(&self.query),
|
||||
arguments: self.arguments.take(),
|
||||
arguments: self.arguments.take().map(Ok),
|
||||
database: PhantomData,
|
||||
persistent: true,
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ use futures_util::{StreamExt, TryFutureExt, TryStreamExt};
|
|||
use crate::arguments::IntoArguments;
|
||||
use crate::database::{Database, HasStatementCache};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
use crate::error::{BoxDynError, Error};
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::from_row::FromRow;
|
||||
use crate::query_as::{
|
||||
query_as, query_as_with, query_statement_as, query_statement_as_with, QueryAs,
|
||||
query_as, query_as_with_result, query_statement_as, query_statement_as_with, QueryAs,
|
||||
};
|
||||
use crate::types::Type;
|
||||
|
||||
|
@ -34,7 +34,7 @@ where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
self.inner.take_arguments()
|
||||
}
|
||||
|
||||
|
@ -338,13 +338,27 @@ where
|
|||
/// For details about prepared statements and allowed SQL syntax, see [`query()`][crate::query::query].
|
||||
#[inline]
|
||||
pub fn query_scalar_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryScalar<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
(O,): for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
query_scalar_with_result(sql, Ok(arguments))
|
||||
}
|
||||
|
||||
/// Same as [`query_scalar_with`] but takes arguments as Result
|
||||
#[inline]
|
||||
pub fn query_scalar_with_result<'q, DB, O, A>(
|
||||
sql: &'q str,
|
||||
arguments: Result<A, BoxDynError>,
|
||||
) -> QueryScalar<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
(O,): for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
QueryScalar {
|
||||
inner: query_as_with(sql, arguments),
|
||||
inner: query_as_with_result(sql, arguments),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use either::Either;
|
|||
use futures_core::stream::BoxStream;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::error::BoxDynError;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::Error;
|
||||
|
||||
|
@ -126,8 +127,8 @@ impl<'q, DB: Database> Execute<'q, DB> for RawSql<'q> {
|
|||
None
|
||||
}
|
||||
|
||||
fn take_arguments(&mut self) -> Option<<DB as Database>::Arguments<'q>> {
|
||||
None
|
||||
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn persistent(&self) -> bool {
|
||||
|
|
|
@ -37,7 +37,10 @@ where
|
|||
DB: Database,
|
||||
&'q [u8]: Encode<'q, DB>,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
<&[u8] as Encode<DB>>::encode(self.as_bytes(), buf)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +50,10 @@ where
|
|||
DB: Database,
|
||||
Vec<u8>: Encode<'q, DB>,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
<Vec<u8> as Encode<DB>>::encode(self.as_bytes().to_vec(), buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,21 +89,15 @@ impl<T> AsMut<T> for Json<T> {
|
|||
}
|
||||
}
|
||||
|
||||
const JSON_SERIALIZE_ERR: &str = "failed to encode value as JSON; the most likely cause is \
|
||||
attempting to serialize a map with a non-string key type";
|
||||
|
||||
// UNSTABLE: for driver use only!
|
||||
#[doc(hidden)]
|
||||
impl<T: Serialize> Json<T> {
|
||||
pub fn encode_to_string(&self) -> String {
|
||||
// Encoding is supposed to be infallible so we don't have much choice but to panic here.
|
||||
// However, I believe that's the right thing to do anyway as an object being unable
|
||||
// to serialize to JSON is likely due to a bug or a malformed datastructure.
|
||||
serde_json::to_string(self).expect(JSON_SERIALIZE_ERR)
|
||||
pub fn encode_to_string(&self) -> Result<String, serde_json::Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
|
||||
pub fn encode_to(&self, buf: &mut Vec<u8>) {
|
||||
serde_json::to_writer(buf, self).expect(JSON_SERIALIZE_ERR)
|
||||
pub fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), serde_json::Error> {
|
||||
serde_json::to_writer(buf, self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +135,10 @@ where
|
|||
for<'a> Json<&'a Self>: Encode<'q, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
<Json<&Self> as Encode<'q, DB>>::encode(Json(self), buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ where
|
|||
String: Encode<'q, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
|
||||
self.0.to_string().encode(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ fn expand_derive_encode_transparent(
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as ::sqlx::database::Database>::ArgumentBuffer<#lifetime>,
|
||||
) -> ::sqlx::encode::IsNull {
|
||||
) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> {
|
||||
<#ty as ::sqlx::encode::Encode<#lifetime, DB>>::encode_by_ref(&self.0, buf)
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ fn expand_derive_encode_weak_enum(
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as ::sqlx::database::Database>::ArgumentBuffer<'q>,
|
||||
) -> ::sqlx::encode::IsNull {
|
||||
) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> {
|
||||
let value = match self {
|
||||
#(#values)*
|
||||
};
|
||||
|
@ -174,7 +174,7 @@ fn expand_derive_encode_strong_enum(
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <DB as ::sqlx::database::Database>::ArgumentBuffer<'q>,
|
||||
) -> ::sqlx::encode::IsNull {
|
||||
) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> {
|
||||
let val = match self {
|
||||
#(#value_arms)*
|
||||
};
|
||||
|
@ -228,7 +228,7 @@ fn expand_derive_encode_struct(
|
|||
let id = &field.ident;
|
||||
|
||||
parse_quote!(
|
||||
encoder.encode(&self. #id);
|
||||
encoder.encode(&self. #id)?;
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -249,14 +249,14 @@ fn expand_derive_encode_struct(
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut ::sqlx::postgres::PgArgumentBuffer,
|
||||
) -> ::sqlx::encode::IsNull {
|
||||
) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> {
|
||||
let mut encoder = ::sqlx::postgres::types::PgRecordEncoder::new(buf);
|
||||
|
||||
#(#writes)*
|
||||
|
||||
encoder.finish();
|
||||
|
||||
::sqlx::encode::IsNull::No
|
||||
::std::result::Result::Ok(::sqlx::encode::IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> ::std::primitive::usize {
|
||||
|
|
|
@ -17,7 +17,7 @@ pub fn quote_args<DB: DatabaseExt>(
|
|||
|
||||
if input.arg_exprs.is_empty() {
|
||||
return Ok(quote! {
|
||||
let query_args = <#db_path as ::sqlx::database::Database>::Arguments::<'_>::default();
|
||||
let query_args = ::core::result::Result::<_, ::sqlx::error::BoxDynError>::Ok(<#db_path as ::sqlx::database::Database>::Arguments::<'_>::default());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,8 @@ pub fn quote_args<DB: DatabaseExt>(
|
|||
#args_count,
|
||||
0 #(+ ::sqlx::encode::Encode::<#db_path>::size_hint(#arg_name))*
|
||||
);
|
||||
#(query_args.add(#arg_name);)*
|
||||
let query_args = ::core::result::Result::<_, ::sqlx::error::BoxDynError>::Ok(query_args)
|
||||
#(.and_then(move |mut query_args| query_args.add(#arg_name).map(move |()| query_args) ))*;
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ where
|
|||
let sql = &input.sql;
|
||||
|
||||
quote! {
|
||||
::sqlx::query_with::<#db_path, _>(#sql, #query_args)
|
||||
::sqlx::__query_with_result::<#db_path, _>(#sql, #query_args)
|
||||
}
|
||||
} else {
|
||||
match input.record_type {
|
||||
|
|
|
@ -173,7 +173,7 @@ pub fn quote_query_as<DB: DatabaseExt>(
|
|||
};
|
||||
|
||||
quote! {
|
||||
::sqlx::query_with::<#db_path, _>(#sql, #bind_args).try_map(|row: #row_path| {
|
||||
::sqlx::__query_with_result::<#db_path, _>(#sql, #bind_args).try_map(|row: #row_path| {
|
||||
use ::sqlx::Row as _;
|
||||
|
||||
#(#instantiations)*
|
||||
|
@ -216,7 +216,7 @@ pub fn quote_query_scalar<DB: DatabaseExt>(
|
|||
let query = &input.sql;
|
||||
|
||||
Ok(quote! {
|
||||
::sqlx::query_scalar_with::<#db, #ty, _>(#query, #bind_args)
|
||||
::sqlx::__query_scalar_with_result::<#db, #ty, _>(#query, #bind_args)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
use either::Either;
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{StreamExt, TryFutureExt, TryStreamExt};
|
||||
use futures_util::{stream, StreamExt, TryFutureExt, TryStreamExt};
|
||||
use sqlx_core::any::{
|
||||
Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow,
|
||||
AnyStatement, AnyTypeInfo, AnyTypeInfoKind,
|
||||
|
@ -16,6 +16,7 @@ use sqlx_core::database::Database;
|
|||
use sqlx_core::describe::Describe;
|
||||
use sqlx_core::executor::Executor;
|
||||
use sqlx_core::transaction::TransactionManager;
|
||||
use std::future;
|
||||
|
||||
sqlx_core::declare_driver_with_optional_migrate!(DRIVER = MySql);
|
||||
|
||||
|
@ -77,10 +78,15 @@ impl AnyConnectionBackend for MySqlConnection {
|
|||
arguments: Option<AnyArguments<'q>>,
|
||||
) -> BoxStream<'q, sqlx_core::Result<Either<AnyQueryResult, AnyRow>>> {
|
||||
let persistent = arguments.is_some();
|
||||
let args = arguments.as_ref().map(AnyArguments::convert_to);
|
||||
let arguments = match arguments.as_ref().map(AnyArguments::convert_to).transpose() {
|
||||
Ok(arguments) => arguments,
|
||||
Err(error) => {
|
||||
return stream::once(future::ready(Err(sqlx_core::Error::Encode(error)))).boxed()
|
||||
}
|
||||
};
|
||||
|
||||
Box::pin(
|
||||
self.run(query, args, persistent)
|
||||
self.run(query, arguments, persistent)
|
||||
.try_flatten_stream()
|
||||
.map(|res| {
|
||||
Ok(match res? {
|
||||
|
@ -97,10 +103,15 @@ impl AnyConnectionBackend for MySqlConnection {
|
|||
arguments: Option<AnyArguments<'q>>,
|
||||
) -> BoxFuture<'q, sqlx_core::Result<Option<AnyRow>>> {
|
||||
let persistent = arguments.is_some();
|
||||
let args = arguments.as_ref().map(AnyArguments::convert_to);
|
||||
let arguments = arguments
|
||||
.as_ref()
|
||||
.map(AnyArguments::convert_to)
|
||||
.transpose()
|
||||
.map_err(sqlx_core::Error::Encode);
|
||||
|
||||
Box::pin(async move {
|
||||
let stream = self.run(query, args, persistent).await?;
|
||||
let arguments = arguments?;
|
||||
let stream = self.run(query, arguments, persistent).await?;
|
||||
futures_util::pin_mut!(stream);
|
||||
|
||||
while let Some(result) = stream.try_next().await? {
|
||||
|
|
|
@ -2,34 +2,38 @@ use crate::encode::{Encode, IsNull};
|
|||
use crate::types::Type;
|
||||
use crate::{MySql, MySqlTypeInfo};
|
||||
pub(crate) use sqlx_core::arguments::*;
|
||||
use sqlx_core::error::BoxDynError;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Implementation of [`Arguments`] for MySQL.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MySqlArguments {
|
||||
pub(crate) values: Vec<u8>,
|
||||
pub(crate) types: Vec<MySqlTypeInfo>,
|
||||
pub(crate) null_bitmap: Vec<u8>,
|
||||
pub(crate) null_bitmap: NullBitMap,
|
||||
}
|
||||
|
||||
impl MySqlArguments {
|
||||
pub(crate) fn add<'q, T>(&mut self, value: T)
|
||||
pub(crate) fn add<'q, T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: Encode<'q, MySql> + Type<MySql>,
|
||||
{
|
||||
let ty = value.produces().unwrap_or_else(T::type_info);
|
||||
let index = self.types.len();
|
||||
|
||||
let value_length_before_encoding = self.values.len();
|
||||
let is_null = match value.encode(&mut self.values) {
|
||||
Ok(is_null) => is_null,
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
self.types.push(ty);
|
||||
self.null_bitmap.resize((index / 8) + 1, 0);
|
||||
self.null_bitmap.push(is_null);
|
||||
|
||||
if let IsNull::Yes = value.encode(&mut self.values) {
|
||||
self.null_bitmap[index / 8] |= (1 << (index % 8)) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.types.len()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,10 +45,64 @@ impl<'q> Arguments<'q> for MySqlArguments {
|
|||
self.values.reserve(size);
|
||||
}
|
||||
|
||||
fn add<T>(&mut self, value: T)
|
||||
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: Encode<'q, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
self.add(value)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.types.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct NullBitMap {
|
||||
bytes: Vec<u8>,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl NullBitMap {
|
||||
fn push(&mut self, is_null: IsNull) {
|
||||
let byte_index = self.length / (u8::BITS as usize);
|
||||
let bit_offset = self.length % (u8::BITS as usize);
|
||||
|
||||
if bit_offset == 0 {
|
||||
self.bytes.push(0);
|
||||
}
|
||||
|
||||
self.bytes[byte_index] |= u8::from(is_null.is_null()) << bit_offset;
|
||||
self.length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for NullBitMap {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn null_bit_map_should_push_is_null() {
|
||||
let mut bit_map = NullBitMap::default();
|
||||
|
||||
bit_map.push(IsNull::Yes);
|
||||
bit_map.push(IsNull::No);
|
||||
bit_map.push(IsNull::Yes);
|
||||
bit_map.push(IsNull::No);
|
||||
bit_map.push(IsNull::Yes);
|
||||
bit_map.push(IsNull::No);
|
||||
bit_map.push(IsNull::Yes);
|
||||
bit_map.push(IsNull::No);
|
||||
bit_map.push(IsNull::Yes);
|
||||
|
||||
assert_eq!([0b01010101, 0b1].as_slice(), bit_map.deref());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,10 +244,11 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection {
|
|||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
let sql = query.sql();
|
||||
let arguments = query.take_arguments();
|
||||
let arguments = query.take_arguments().map_err(Error::Encode);
|
||||
let persistent = query.persistent();
|
||||
|
||||
Box::pin(try_stream! {
|
||||
let arguments = arguments?;
|
||||
let s = self.run(sql, arguments, persistent).await?;
|
||||
pin_mut!(s);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ impl<'q> Encode<'_, Capabilities> for Execute<'q> {
|
|||
buf.extend(&1_u32.to_le_bytes()); // iterations (always 1): int<4>
|
||||
|
||||
if !self.arguments.types.is_empty() {
|
||||
buf.extend(&*self.arguments.null_bitmap);
|
||||
buf.extend_from_slice(&self.arguments.null_bitmap);
|
||||
buf.push(1); // send type to server
|
||||
|
||||
for ty in &self.arguments.types {
|
||||
|
|
|
@ -19,10 +19,10 @@ impl Type<MySql> for BigDecimal {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for BigDecimal {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ impl Type<MySql> for bool {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for bool {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
<i8 as Encode<MySql>>::encode(*self as i8, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@ impl Type<MySql> for [u8] {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for &'_ [u8] {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_bytes_lenenc(self);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ impl Type<MySql> for Box<[u8]> {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Box<[u8]> {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
<&[u8] as Encode<MySql>>::encode(self.as_ref(), buf)
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ impl Type<MySql> for Vec<u8> {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Vec<u8> {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
<&[u8] as Encode<MySql>>::encode(&**self, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ impl Type<MySql> for DateTime<Utc> {
|
|||
|
||||
/// Note: assumes the connection's `time_zone` is set to `+00:00` (UTC).
|
||||
impl Encode<'_, MySql> for DateTime<Utc> {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
Encode::<MySql>::encode(&self.naive_utc(), buf)
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ impl Type<MySql> for DateTime<Local> {
|
|||
|
||||
/// Note: assumes the connection's `time_zone` is set to `+00:00` (UTC).
|
||||
impl Encode<'_, MySql> for DateTime<Local> {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
Encode::<MySql>::encode(&self.naive_utc(), buf)
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ impl Type<MySql> for NaiveTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for NaiveTime {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
let len = Encode::<MySql>::size_hint(self) - 1;
|
||||
buf.push(len as u8);
|
||||
|
||||
|
@ -82,7 +82,7 @@ impl Encode<'_, MySql> for NaiveTime {
|
|||
|
||||
encode_time(self, len > 9, buf);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -174,12 +174,12 @@ impl Type<MySql> for NaiveDate {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for NaiveDate {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.push(4);
|
||||
|
||||
encode_date(self, buf);
|
||||
encode_date(self, buf)?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -216,17 +216,17 @@ impl Type<MySql> for NaiveDateTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for NaiveDateTime {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
let len = Encode::<MySql>::size_hint(self) - 1;
|
||||
buf.push(len as u8);
|
||||
|
||||
encode_date(&self.date(), buf);
|
||||
encode_date(&self.date(), buf)?;
|
||||
|
||||
if len > 4 {
|
||||
encode_time(&self.time(), len > 8, buf);
|
||||
}
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -282,14 +282,16 @@ impl<'r> Decode<'r, MySql> for NaiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_date(date: &NaiveDate, buf: &mut Vec<u8>) {
|
||||
fn encode_date(date: &NaiveDate, buf: &mut Vec<u8>) -> Result<(), BoxDynError> {
|
||||
// MySQL supports years from 1000 - 9999
|
||||
let year = u16::try_from(date.year())
|
||||
.unwrap_or_else(|_| panic!("NaiveDateTime out of range for Mysql: {date}"));
|
||||
.map_err(|_| format!("NaiveDateTime out of range for Mysql: {date}"))?;
|
||||
|
||||
buf.extend_from_slice(&year.to_le_bytes());
|
||||
buf.push(date.month() as u8);
|
||||
buf.push(date.day() as u8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_date(mut buf: &[u8]) -> Result<Option<NaiveDate>, BoxDynError> {
|
||||
|
|
|
@ -33,18 +33,18 @@ impl Type<MySql> for f64 {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for f32 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for f64 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@ impl Type<MySql> for Ipv4Addr {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Ipv4Addr {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,10 @@ impl Type<MySql> for Ipv6Addr {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Ipv6Addr {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,10 +74,10 @@ impl Type<MySql> for IpAddr {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for IpAddr {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,34 +59,34 @@ impl Type<MySql> for i64 {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for i8 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for i16 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for i32 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for i64 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ impl<T> Encode<'_, MySql> for Json<T>
|
|||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
// Encode JSON as a length-prefixed string.
|
||||
//
|
||||
// The previous implementation encoded into an intermediate buffer to get the final length.
|
||||
|
@ -47,14 +47,14 @@ where
|
|||
buf.extend_from_slice(&[0u8; 9]);
|
||||
|
||||
let encode_start = buf.len();
|
||||
self.encode_to(buf);
|
||||
self.encode_to(buf)?;
|
||||
let encoded_len = (buf.len() - encode_start) as u64;
|
||||
|
||||
// This prefix indicates that the following 8 bytes are a little-endian integer.
|
||||
buf[lenenc_start] = 0xFE;
|
||||
buf[lenenc_start + 1..][..8].copy_from_slice(&encoded_len.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -410,10 +410,13 @@ impl<'r> Decode<'r, MySql> for MySqlTime {
|
|||
}
|
||||
|
||||
impl<'q> Encode<'q, MySql> for MySqlTime {
|
||||
fn encode_by_ref(&self, buf: &mut <MySql as Database>::ArgumentBuffer<'q>) -> IsNull {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <MySql as Database>::ArgumentBuffer<'q>,
|
||||
) -> Result<IsNull, BoxDynError> {
|
||||
if self.is_zero() {
|
||||
buf.put_u8(0);
|
||||
return IsNull::No;
|
||||
return Ok(IsNull::No);
|
||||
}
|
||||
|
||||
buf.put_u8(self.encoded_len());
|
||||
|
@ -438,7 +441,7 @@ impl<'q> Encode<'q, MySql> for MySqlTime {
|
|||
buf.put_u32_le(microseconds);
|
||||
}
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
|
|
@ -19,10 +19,10 @@ impl Type<MySql> for Decimal {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Decimal {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,10 +49,10 @@ impl Type<MySql> for str {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for &'_ str {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(self);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ impl Type<MySql> for Box<str> {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Box<str> {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
<&str as Encode<MySql>>::encode(&**self, buf)
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ impl Type<MySql> for String {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for String {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
<&str as Encode<MySql>>::encode(&**self, buf)
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ impl Type<MySql> for Cow<'_, str> {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Cow<'_, str> {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
match self {
|
||||
Cow::Borrowed(str) => <&str as Encode<MySql>>::encode(*str, buf),
|
||||
Cow::Owned(str) => <&str as Encode<MySql>>::encode(&**str, buf),
|
||||
|
|
|
@ -20,7 +20,7 @@ impl<'q, T> Encode<'q, MySql> for Text<T>
|
|||
where
|
||||
T: Display,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
// We can't really do the trick like with Postgres where we reserve the space for the
|
||||
// length up-front and then overwrite it later, because MySQL appears to enforce that
|
||||
// length-encoded integers use the smallest encoding for the value:
|
||||
|
|
|
@ -23,7 +23,7 @@ impl Type<MySql> for OffsetDateTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for OffsetDateTime {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
let utc_dt = self.to_offset(UtcOffset::UTC);
|
||||
let primitive_dt = PrimitiveDateTime::new(utc_dt.date(), utc_dt.time());
|
||||
|
||||
|
@ -46,7 +46,7 @@ impl Type<MySql> for Time {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Time {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
let len = Encode::<MySql>::size_hint(self) - 1;
|
||||
buf.push(len as u8);
|
||||
|
||||
|
@ -59,7 +59,7 @@ impl Encode<'_, MySql> for Time {
|
|||
|
||||
encode_time(self, len > 9, buf);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -149,12 +149,12 @@ impl Type<MySql> for Date {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Date {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.push(4);
|
||||
|
||||
encode_date(self, buf);
|
||||
encode_date(self, buf)?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -190,17 +190,17 @@ impl Type<MySql> for PrimitiveDateTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for PrimitiveDateTime {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
let len = Encode::<MySql>::size_hint(self) - 1;
|
||||
buf.push(len as u8);
|
||||
|
||||
encode_date(&self.date(), buf);
|
||||
encode_date(&self.date(), buf)?;
|
||||
|
||||
if len > 4 {
|
||||
encode_time(&self.time(), len > 8, buf);
|
||||
}
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -267,14 +267,16 @@ impl<'r> Decode<'r, MySql> for PrimitiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_date(date: &Date, buf: &mut Vec<u8>) {
|
||||
fn encode_date(date: &Date, buf: &mut Vec<u8>) -> Result<(), BoxDynError> {
|
||||
// MySQL supports years from 1000 - 9999
|
||||
let year = u16::try_from(date.year())
|
||||
.unwrap_or_else(|_| panic!("Date out of range for Mysql: {date}"));
|
||||
let year =
|
||||
u16::try_from(date.year()).map_err(|_| format!("Date out of range for Mysql: {date}"))?;
|
||||
|
||||
buf.extend_from_slice(&year.to_le_bytes());
|
||||
buf.push(date.month().into());
|
||||
buf.push(date.day());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_date(buf: &[u8]) -> Result<Option<Date>, BoxDynError> {
|
||||
|
|
|
@ -69,34 +69,34 @@ impl Type<MySql> for u64 {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for u8 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for u16 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for u32 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for u64 {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_le_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ impl Type<MySql> for Uuid {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Uuid {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_bytes_lenenc(self.as_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,10 +49,10 @@ impl Type<MySql> for Hyphenated {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Hyphenated {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,10 +79,10 @@ impl Type<MySql> for Simple {
|
|||
}
|
||||
|
||||
impl Encode<'_, MySql> for Simple {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
|
||||
buf.put_str_lenenc(&self.to_string());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ use crate::{
|
|||
};
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{StreamExt, TryFutureExt, TryStreamExt};
|
||||
use futures_util::{stream, StreamExt, TryFutureExt, TryStreamExt};
|
||||
use std::future;
|
||||
|
||||
pub use sqlx_core::any::*;
|
||||
|
||||
|
@ -76,10 +77,15 @@ impl AnyConnectionBackend for PgConnection {
|
|||
arguments: Option<AnyArguments<'q>>,
|
||||
) -> BoxStream<'q, sqlx_core::Result<Either<AnyQueryResult, AnyRow>>> {
|
||||
let persistent = arguments.is_some();
|
||||
let args = arguments.as_ref().map(AnyArguments::convert_to);
|
||||
let arguments = match arguments.as_ref().map(AnyArguments::convert_to).transpose() {
|
||||
Ok(arguments) => arguments,
|
||||
Err(error) => {
|
||||
return stream::once(future::ready(Err(sqlx_core::Error::Encode(error)))).boxed()
|
||||
}
|
||||
};
|
||||
|
||||
Box::pin(
|
||||
self.run(query, args, 0, persistent, None)
|
||||
self.run(query, arguments, 0, persistent, None)
|
||||
.try_flatten_stream()
|
||||
.map(
|
||||
move |res: sqlx_core::Result<Either<PgQueryResult, PgRow>>| match res? {
|
||||
|
@ -96,10 +102,15 @@ impl AnyConnectionBackend for PgConnection {
|
|||
arguments: Option<AnyArguments<'q>>,
|
||||
) -> BoxFuture<'q, sqlx_core::Result<Option<AnyRow>>> {
|
||||
let persistent = arguments.is_some();
|
||||
let args = arguments.as_ref().map(AnyArguments::convert_to);
|
||||
let arguments = arguments
|
||||
.as_ref()
|
||||
.map(AnyArguments::convert_to)
|
||||
.transpose()
|
||||
.map_err(sqlx_core::Error::Encode);
|
||||
|
||||
Box::pin(async move {
|
||||
let stream = self.run(query, args, 1, persistent, None).await?;
|
||||
let arguments = arguments?;
|
||||
let stream = self.run(query, arguments, 1, persistent, None).await?;
|
||||
futures_util::pin_mut!(stream);
|
||||
|
||||
if let Some(Either::Right(row)) = stream.try_next().await? {
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::types::Type;
|
|||
use crate::{PgConnection, PgTypeInfo, Postgres};
|
||||
|
||||
pub(crate) use sqlx_core::arguments::Arguments;
|
||||
use sqlx_core::error::BoxDynError;
|
||||
|
||||
// TODO: buf.patch(|| ...) is a poor name, can we think of a better name? Maybe `buf.lazy(||)` ?
|
||||
// TODO: Extend the patch system to support dynamic lengths
|
||||
|
@ -59,19 +60,27 @@ pub struct PgArguments {
|
|||
}
|
||||
|
||||
impl PgArguments {
|
||||
pub(crate) fn add<'q, T>(&mut self, value: T)
|
||||
pub(crate) fn add<'q, T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: Encode<'q, Postgres> + Type<Postgres>,
|
||||
{
|
||||
// remember the type information for this value
|
||||
self.types
|
||||
.push(value.produces().unwrap_or_else(T::type_info));
|
||||
let type_info = value.produces().unwrap_or_else(T::type_info);
|
||||
|
||||
let buffer_snapshot = self.buffer.snapshot();
|
||||
|
||||
// encode the value into our buffer
|
||||
self.buffer.encode(value);
|
||||
if let Err(error) = self.buffer.encode(value) {
|
||||
// reset the value buffer to its previous value if encoding failed so we don't leave a half-encoded value behind
|
||||
self.buffer.reset_to_snapshot(buffer_snapshot);
|
||||
return Err(error);
|
||||
};
|
||||
|
||||
// remember the type information for this value
|
||||
self.types.push(type_info);
|
||||
// increment the number of arguments we are tracking
|
||||
self.buffer.count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Apply patches
|
||||
|
@ -112,7 +121,7 @@ impl<'q> Arguments<'q> for PgArguments {
|
|||
self.buffer.reserve(size);
|
||||
}
|
||||
|
||||
fn add<T>(&mut self, value: T)
|
||||
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: Encode<'q, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
|
@ -122,10 +131,14 @@ impl<'q> Arguments<'q> for PgArguments {
|
|||
fn format_placeholder<W: Write>(&self, writer: &mut W) -> fmt::Result {
|
||||
write!(writer, "${}", self.buffer.count)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.buffer.count
|
||||
}
|
||||
}
|
||||
|
||||
impl PgArgumentBuffer {
|
||||
pub(crate) fn encode<'q, T>(&mut self, value: T)
|
||||
pub(crate) fn encode<'q, T>(&mut self, value: T) -> Result<(), BoxDynError>
|
||||
where
|
||||
T: Encode<'q, Postgres>,
|
||||
{
|
||||
|
@ -134,7 +147,7 @@ impl PgArgumentBuffer {
|
|||
self.extend(&[0; 4]);
|
||||
|
||||
// encode the value into our buffer
|
||||
let len = if let IsNull::No = value.encode(self) {
|
||||
let len = if let IsNull::No = value.encode(self)? {
|
||||
(self.len() - offset - 4) as i32
|
||||
} else {
|
||||
// Write a -1 to indicate NULL
|
||||
|
@ -145,6 +158,8 @@ impl PgArgumentBuffer {
|
|||
|
||||
// write the len to the beginning of the value
|
||||
self[offset..(offset + 4)].copy_from_slice(&len.to_be_bytes());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Adds a callback to be invoked later when we know the parameter type
|
||||
|
@ -167,6 +182,44 @@ impl PgArgumentBuffer {
|
|||
self.extend_from_slice(&0_u32.to_be_bytes());
|
||||
self.type_holes.push((offset, type_name.clone()));
|
||||
}
|
||||
|
||||
fn snapshot(&self) -> PgArgumentBufferSnapshot {
|
||||
let Self {
|
||||
buffer,
|
||||
count,
|
||||
patches,
|
||||
type_holes,
|
||||
} = self;
|
||||
|
||||
PgArgumentBufferSnapshot {
|
||||
buffer_length: buffer.len(),
|
||||
count: *count,
|
||||
patches_length: patches.len(),
|
||||
type_holes_length: type_holes.len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_to_snapshot(
|
||||
&mut self,
|
||||
PgArgumentBufferSnapshot {
|
||||
buffer_length,
|
||||
count,
|
||||
patches_length,
|
||||
type_holes_length,
|
||||
}: PgArgumentBufferSnapshot,
|
||||
) {
|
||||
self.buffer.truncate(buffer_length);
|
||||
self.count = count;
|
||||
self.patches.truncate(patches_length);
|
||||
self.type_holes.truncate(type_holes_length);
|
||||
}
|
||||
}
|
||||
|
||||
struct PgArgumentBufferSnapshot {
|
||||
buffer_length: usize,
|
||||
count: usize,
|
||||
patches_length: usize,
|
||||
type_holes_length: usize,
|
||||
}
|
||||
|
||||
impl Deref for PgArgumentBuffer {
|
||||
|
|
|
@ -382,9 +382,10 @@ WHERE rngtypid = $1
|
|||
bind + 2
|
||||
);
|
||||
|
||||
args.add(i as i32);
|
||||
args.add(column.relation_id);
|
||||
args.add(column.relation_attribute_no);
|
||||
args.add(i as i32).map_err(Error::Encode)?;
|
||||
args.add(column.relation_id).map_err(Error::Encode)?;
|
||||
args.add(column.relation_attribute_no)
|
||||
.map_err(Error::Encode)?;
|
||||
}
|
||||
|
||||
nullable_query.push_str(
|
||||
|
|
|
@ -370,10 +370,11 @@ impl<'c> Executor<'c> for &'c mut PgConnection {
|
|||
{
|
||||
let sql = query.sql();
|
||||
let metadata = query.statement().map(|s| Arc::clone(&s.metadata));
|
||||
let arguments = query.take_arguments();
|
||||
let arguments = query.take_arguments().map_err(Error::Encode);
|
||||
let persistent = query.persistent();
|
||||
|
||||
Box::pin(try_stream! {
|
||||
let arguments = arguments?;
|
||||
let s = self.run(sql, arguments, 0, persistent, metadata).await?;
|
||||
pin_mut!(s);
|
||||
|
||||
|
@ -395,10 +396,11 @@ impl<'c> Executor<'c> for &'c mut PgConnection {
|
|||
{
|
||||
let sql = query.sql();
|
||||
let metadata = query.statement().map(|s| Arc::clone(&s.metadata));
|
||||
let arguments = query.take_arguments();
|
||||
let arguments = query.take_arguments().map_err(Error::Encode);
|
||||
let persistent = query.persistent();
|
||||
|
||||
Box::pin(async move {
|
||||
let arguments = arguments?;
|
||||
let s = self.run(sql, arguments, 1, persistent, metadata).await?;
|
||||
pin_mut!(s);
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ where
|
|||
T: Encode<'q, Postgres>,
|
||||
{
|
||||
#[inline]
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
self.as_slice().encode_by_ref(buf)
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ where
|
|||
for<'a> &'a [T]: Encode<'q, Postgres>,
|
||||
T: Encode<'q, Postgres>,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
self.as_slice().encode_by_ref(buf)
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ impl<'q, T> Encode<'q, Postgres> for &'_ [T]
|
|||
where
|
||||
T: Encode<'q, Postgres> + Type<Postgres>,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
let type_info = if self.len() < 1 {
|
||||
T::type_info()
|
||||
} else {
|
||||
|
@ -178,10 +178,10 @@ where
|
|||
buf.extend(&1_i32.to_be_bytes()); // lower bound
|
||||
|
||||
for element in self.iter() {
|
||||
buf.encode(element);
|
||||
buf.encode(element)?;
|
||||
}
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,24 +137,10 @@ impl TryFrom<&'_ BigDecimal> for PgNumeric {
|
|||
|
||||
#[doc=include_str!("bigdecimal-range.md")]
|
||||
impl Encode<'_, Postgres> for BigDecimal {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
// If the argument is too big, then we replace it with a less big argument.
|
||||
// This less big argument is already outside the range of allowed PostgreSQL DECIMAL, which
|
||||
// means that PostgreSQL will return the 22P03 error kind upon receiving it. This is the
|
||||
// expected error, and the user should be ready to handle it anyway.
|
||||
PgNumeric::try_from(self)
|
||||
.unwrap_or_else(|_| {
|
||||
PgNumeric::Number {
|
||||
digits: vec![1],
|
||||
// This is larger than the maximum allowed value, so Postgres should return an error.
|
||||
scale: 0x4000,
|
||||
weight: 0,
|
||||
sign: sign_to_pg(self.sign()),
|
||||
}
|
||||
})
|
||||
.encode(buf);
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
PgNumeric::try_from(self)?.encode(buf);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
|
|
@ -30,11 +30,11 @@ impl PgHasArrayType for BitVec {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for BitVec {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&(self.len() as i32).to_be_bytes());
|
||||
buf.extend(self.to_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
|
|
@ -17,10 +17,10 @@ impl PgHasArrayType for bool {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for bool {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.push(*self as u8);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,27 +35,27 @@ impl<const N: usize> PgHasArrayType for [u8; N] {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for &'_ [u8] {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend_from_slice(self);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Box<[u8]> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
<&[u8] as Encode<Postgres>>::encode(self.as_ref(), buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Vec<u8> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
<&[u8] as Encode<Postgres>>::encode(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Encode<'_, Postgres> for [u8; N] {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
<&[u8] as Encode<Postgres>>::encode(self.as_slice(), buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ impl PgHasArrayType for NaiveDate {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for NaiveDate {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// DATE is encoded as the days since epoch
|
||||
let days = (*self - postgres_epoch_date()).num_days() as i32;
|
||||
Encode::<Postgres>::encode(&days, buf)
|
||||
|
|
|
@ -33,12 +33,11 @@ impl<Tz: TimeZone> PgHasArrayType for DateTime<Tz> {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for NaiveDateTime {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
// FIXME: We should *really* be returning an error, Encode needs to be fallible
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// TIMESTAMP is encoded as the microseconds since the epoch
|
||||
let us = (*self - postgres_epoch_datetime())
|
||||
.num_microseconds()
|
||||
.unwrap_or_else(|| panic!("NaiveDateTime out of range for Postgres: {self:?}"));
|
||||
.ok_or_else(|| format!("NaiveDateTime out of range for Postgres: {self:?}"))?;
|
||||
|
||||
Encode::<Postgres>::encode(&us, buf)
|
||||
}
|
||||
|
@ -76,7 +75,7 @@ impl<'r> Decode<'r, Postgres> for NaiveDateTime {
|
|||
}
|
||||
|
||||
impl<Tz: TimeZone> Encode<'_, Postgres> for DateTime<Tz> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
Encode::<Postgres>::encode(self.naive_utc(), buf)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,11 @@ impl PgHasArrayType for NaiveTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for NaiveTime {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// TIME is encoded as the microseconds since midnight
|
||||
// NOTE: panic! is on overflow and 1 day does not have enough micros to overflow
|
||||
let us = (*self - NaiveTime::default()).num_microseconds().unwrap();
|
||||
let us = (*self - NaiveTime::default())
|
||||
.num_microseconds()
|
||||
.ok_or_else(|| format!("Time out of range for PostgreSQL: {self}"))?;
|
||||
|
||||
Encode::<Postgres>::encode(&us, buf)
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ impl PgHasArrayType for PgCiText {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgCiText {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
<&str as Encode<Postgres>>::encode(&**self, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ impl PgHasArrayType for f32 {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for f32 {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,10 +48,10 @@ impl PgHasArrayType for f64 {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for f64 {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,10 +44,10 @@ impl PgHasArrayType for i8 {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for i8 {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,10 +89,10 @@ impl PgHasArrayType for i16 {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for i16 {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,10 +115,10 @@ impl PgHasArrayType for i32 {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for i32 {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,10 +141,10 @@ impl PgHasArrayType for i64 {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for i64 {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,12 +54,12 @@ impl<'de> Decode<'de, Postgres> for PgInterval {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgInterval {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.microseconds.to_be_bytes());
|
||||
buf.extend(&self.days.to_be_bytes());
|
||||
buf.extend(&self.months.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -83,10 +83,8 @@ impl PgHasArrayType for std::time::Duration {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for std::time::Duration {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
PgInterval::try_from(*self)
|
||||
.expect("failed to encode `std::time::Duration`")
|
||||
.encode_by_ref(buf)
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
PgInterval::try_from(*self)?.encode_by_ref(buf)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -130,8 +128,8 @@ impl PgHasArrayType for chrono::Duration {
|
|||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl Encode<'_, Postgres> for chrono::Duration {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let pg_interval = PgInterval::try_from(*self).expect("Failed to encode chrono::Duration");
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
let pg_interval = PgInterval::try_from(*self)?;
|
||||
pg_interval.encode_by_ref(buf)
|
||||
}
|
||||
|
||||
|
@ -192,8 +190,8 @@ impl PgHasArrayType for time::Duration {
|
|||
|
||||
#[cfg(feature = "time")]
|
||||
impl Encode<'_, Postgres> for time::Duration {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let pg_interval = PgInterval::try_from(*self).expect("Failed to encode time::Duration");
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
let pg_interval = PgInterval::try_from(*self)?;
|
||||
pg_interval.encode_by_ref(buf)
|
||||
}
|
||||
|
||||
|
@ -234,7 +232,7 @@ fn test_encode_interval() {
|
|||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
@ -246,7 +244,7 @@ fn test_encode_interval() {
|
|||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
@ -258,7 +256,7 @@ fn test_encode_interval() {
|
|||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 15, 66, 64, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
@ -270,7 +268,7 @@ fn test_encode_interval() {
|
|||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
));
|
||||
assert_eq!(
|
||||
&**buf,
|
||||
|
@ -285,7 +283,7 @@ fn test_encode_interval() {
|
|||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
@ -297,7 +295,7 @@ fn test_encode_interval() {
|
|||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
buf.clear();
|
||||
|
|
|
@ -35,7 +35,7 @@ impl<'db> Encode<'db, Postgres> for IpAddr
|
|||
where
|
||||
IpNetwork: Encode<'db, Postgres>,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
IpNetwork::from(*self).encode_by_ref(buf)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ impl PgHasArrayType for IpNetwork {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for IpNetwork {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// https://github.com/postgres/postgres/blob/574925bfd0a8175f6e161936ea11d9695677ba09/src/backend/utils/adt/network.c#L293
|
||||
// https://github.com/postgres/postgres/blob/574925bfd0a8175f6e161936ea11d9695677ba09/src/backend/utils/adt/network.c#L271
|
||||
|
||||
|
@ -58,7 +58,7 @@ impl Encode<'_, Postgres> for IpNetwork {
|
|||
}
|
||||
}
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
|
|
@ -58,7 +58,7 @@ impl<'q, T> Encode<'q, Postgres> for Json<T>
|
|||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// we have a tiny amount of dynamic behavior depending if we are resolved to be JSON
|
||||
// instead of JSONB
|
||||
buf.patch(|buf, ty: &PgTypeInfo| {
|
||||
|
@ -71,10 +71,9 @@ where
|
|||
buf.push(1);
|
||||
|
||||
// the JSON data written to the buffer is the same regardless of parameter type
|
||||
serde_json::to_writer(&mut **buf, &self.0)
|
||||
.expect("failed to serialize to JSON for encoding on transmission to the database");
|
||||
serde_json::to_writer(&mut **buf, &self.0)?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -139,12 +139,11 @@ impl Type<Postgres> for PgLQuery {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgLQuery {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(1i8.to_le_bytes());
|
||||
write!(buf, "{self}")
|
||||
.expect("Display implementation panicked while writing to PgArgumentBuffer");
|
||||
write!(buf, "{self}")?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -181,12 +181,11 @@ impl PgHasArrayType for PgLTree {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgLTree {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(1i8.to_le_bytes());
|
||||
write!(buf, "{self}")
|
||||
.expect("Display implementation panicked while writing to PgArgumentBuffer");
|
||||
write!(buf, "{self}")?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@ impl PgHasArrayType for MacAddress {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for MacAddress {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend_from_slice(&self.bytes()); // write just the address
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
|
|
@ -165,10 +165,10 @@ where
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgMoney {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.0.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,10 @@ impl PgHasArrayType for Oid {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Oid {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(&self.0.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -292,7 +292,7 @@ impl<'q, T> Encode<'q, Postgres> for PgRange<T>
|
|||
where
|
||||
T: Encode<'q, Postgres>,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// https://github.com/postgres/postgres/blob/2f48ede080f42b97b594fb14102c82ca1001b80c/src/backend/utils/adt/rangetypes.c#L245
|
||||
|
||||
let mut flags = RangeFlags::empty();
|
||||
|
@ -312,15 +312,15 @@ where
|
|||
buf.push(flags.bits());
|
||||
|
||||
if let Bound::Included(v) | Bound::Excluded(v) = &self.start {
|
||||
buf.encode(v);
|
||||
buf.encode(v)?;
|
||||
}
|
||||
|
||||
if let Bound::Included(v) | Bound::Excluded(v) = &self.end {
|
||||
buf.encode(v);
|
||||
buf.encode(v)?;
|
||||
}
|
||||
|
||||
// ranges are themselves never null
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ impl<'a> PgRecordEncoder<'a> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn encode<'q, T>(&mut self, value: T) -> &mut Self
|
||||
pub fn encode<'q, T>(&mut self, value: T) -> Result<&mut Self, BoxDynError>
|
||||
where
|
||||
'a: 'q,
|
||||
T: Encode<'q, Postgres> + Type<Postgres>,
|
||||
|
@ -50,10 +50,10 @@ impl<'a> PgRecordEncoder<'a> {
|
|||
self.buf.extend(&ty.0.oid().0.to_be_bytes());
|
||||
}
|
||||
|
||||
self.buf.encode(value);
|
||||
self.buf.encode(value)?;
|
||||
self.num += 1;
|
||||
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,18 +72,16 @@ impl TryFrom<PgNumeric> for Decimal {
|
|||
}
|
||||
|
||||
// This impl is effectively infallible because `NUMERIC` has a greater range than `Decimal`.
|
||||
impl TryFrom<&'_ Decimal> for PgNumeric {
|
||||
type Error = BoxDynError;
|
||||
|
||||
fn try_from(decimal: &Decimal) -> Result<Self, BoxDynError> {
|
||||
impl From<&'_ Decimal> for PgNumeric {
|
||||
fn from(decimal: &Decimal) -> Self {
|
||||
// `Decimal` added `is_zero()` as an inherent method in a more recent version
|
||||
if Zero::is_zero(decimal) {
|
||||
return Ok(PgNumeric::Number {
|
||||
PgNumeric::Number {
|
||||
sign: PgNumericSign::Positive,
|
||||
scale: 0,
|
||||
weight: 0,
|
||||
digits: vec![],
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
let scale = decimal.scale() as u16;
|
||||
|
@ -131,7 +129,7 @@ impl TryFrom<&'_ Decimal> for PgNumeric {
|
|||
digits.pop();
|
||||
}
|
||||
|
||||
Ok(PgNumeric::Number {
|
||||
PgNumeric::Number {
|
||||
sign: match decimal.is_sign_negative() {
|
||||
false => PgNumericSign::Positive,
|
||||
true => PgNumericSign::Negative,
|
||||
|
@ -139,17 +137,15 @@ impl TryFrom<&'_ Decimal> for PgNumeric {
|
|||
scale: scale as i16,
|
||||
weight,
|
||||
digits,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Decimal {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
PgNumeric::try_from(self)
|
||||
.expect("BUG: `Decimal` to `PgNumeric` conversion should be infallible")
|
||||
.encode(buf);
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
PgNumeric::from(self).encode(buf);
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,15 +95,15 @@ impl PgHasArrayType for String {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for &'_ str {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend(self.as_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Cow<'_, str> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
match self {
|
||||
Cow::Borrowed(str) => <&str as Encode<Postgres>>::encode(*str, buf),
|
||||
Cow::Owned(str) => <&str as Encode<Postgres>>::encode(&**str, buf),
|
||||
|
@ -112,13 +112,13 @@ impl Encode<'_, Postgres> for Cow<'_, str> {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Box<str> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
<&str as Encode<Postgres>>::encode(&**self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for String {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
<&str as Encode<Postgres>>::encode(&**self, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,19 +22,9 @@ impl<'q, T> Encode<'q, Postgres> for Text<T>
|
|||
where
|
||||
T: Display,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
// Unfortunately, our API design doesn't give us a way to bubble up the error here.
|
||||
//
|
||||
// Fortunately, writing to `Vec<u8>` is infallible so the only possible source of
|
||||
// errors is from the implementation of `Display::fmt()` itself,
|
||||
// where the onus is on the user.
|
||||
//
|
||||
// The blanket impl of `ToString` also panics if there's an error, so this is not
|
||||
// unprecedented.
|
||||
//
|
||||
// However, the panic should be documented anyway.
|
||||
write!(**buf, "{}", self.0).expect("unexpected error from `Display::fmt()`");
|
||||
IsNull::No
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
write!(**buf, "{}", self.0)?;
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ impl PgHasArrayType for Date {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Date {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// DATE is encoded as the days since epoch
|
||||
let days = (*self - PG_EPOCH).whole_days() as i32;
|
||||
Encode::<Postgres>::encode(&days, buf)
|
||||
|
|
|
@ -35,7 +35,7 @@ impl PgHasArrayType for OffsetDateTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PrimitiveDateTime {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// TIMESTAMP is encoded as the microseconds since the epoch
|
||||
let us = (*self - PG_EPOCH.midnight()).whole_microseconds() as i64;
|
||||
Encode::<Postgres>::encode(&us, buf)
|
||||
|
@ -84,7 +84,7 @@ impl<'r> Decode<'r, Postgres> for PrimitiveDateTime {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for OffsetDateTime {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
let utc = self.to_offset(offset!(UTC));
|
||||
let primitive = PrimitiveDateTime::new(utc.date(), utc.time());
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ impl PgHasArrayType for Time {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Time {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
// TIME is encoded as the microseconds since midnight
|
||||
let us = (*self - Time::MIDNIGHT).whole_microseconds() as i64;
|
||||
Encode::<Postgres>::encode(&us, buf)
|
||||
|
|
|
@ -51,11 +51,12 @@ mod chrono {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgTimeTz<NaiveTime, FixedOffset> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let _ = <NaiveTime as Encode<'_, Postgres>>::encode(self.time, buf);
|
||||
let _ = <i32 as Encode<'_, Postgres>>::encode(self.offset.utc_minus_local(), buf);
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
let _: IsNull = <NaiveTime as Encode<'_, Postgres>>::encode(self.time, buf)?;
|
||||
let _: IsNull =
|
||||
<i32 as Encode<'_, Postgres>>::encode(self.offset.utc_minus_local(), buf)?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
@ -134,11 +135,12 @@ mod time {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgTimeTz<Time, UtcOffset> {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let _ = <Time as Encode<'_, Postgres>>::encode(self.time, buf);
|
||||
let _ = <i32 as Encode<'_, Postgres>>::encode(-self.offset.whole_seconds(), buf);
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
let _: IsNull = <Time as Encode<'_, Postgres>>::encode(self.time, buf)?;
|
||||
let _: IsNull =
|
||||
<i32 as Encode<'_, Postgres>>::encode(-self.offset.whole_seconds(), buf)?;
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
|
|
|
@ -19,10 +19,10 @@ impl PgHasArrayType for Uuid {
|
|||
}
|
||||
|
||||
impl Encode<'_, Postgres> for Uuid {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
buf.extend_from_slice(self.as_bytes());
|
||||
|
||||
IsNull::No
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<'_> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,13 @@ pub use sqlx_core::describe::Describe;
|
|||
pub use sqlx_core::executor::{Execute, Executor};
|
||||
pub use sqlx_core::from_row::FromRow;
|
||||
pub use sqlx_core::pool::{self, Pool};
|
||||
#[doc(hidden)]
|
||||
pub use sqlx_core::query::query_with_result as __query_with_result;
|
||||
pub use sqlx_core::query::{query, query_with};
|
||||
pub use sqlx_core::query_as::{query_as, query_as_with};
|
||||
pub use sqlx_core::query_builder::{self, QueryBuilder};
|
||||
#[doc(hidden)]
|
||||
pub use sqlx_core::query_scalar::query_scalar_with_result as __query_scalar_with_result;
|
||||
pub use sqlx_core::query_scalar::{query_scalar, query_scalar_with};
|
||||
pub use sqlx_core::raw_sql::{raw_sql, RawSql};
|
||||
pub use sqlx_core::row::Row;
|
||||
|
|
|
@ -6,7 +6,7 @@ use sqlx::postgres::{
|
|||
PgPoolOptions, PgRow, PgSeverity, Postgres,
|
||||
};
|
||||
use sqlx::{Column, Connection, Executor, Row, Statement, TypeInfo};
|
||||
use sqlx_core::bytes::Bytes;
|
||||
use sqlx_core::{bytes::Bytes, error::BoxDynError};
|
||||
use sqlx_test::{new, pool, setup_if_needed};
|
||||
use std::env;
|
||||
use std::pin::Pin;
|
||||
|
@ -1124,7 +1124,7 @@ CREATE TABLE heating_bills (
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut sqlx::postgres::PgArgumentBuffer,
|
||||
) -> sqlx::encode::IsNull {
|
||||
) -> Result<sqlx::encode::IsNull, BoxDynError> {
|
||||
<i16 as sqlx::Encode<Postgres>>::encode(self.0, buf)
|
||||
}
|
||||
}
|
||||
|
@ -1162,12 +1162,12 @@ CREATE TABLE heating_bills (
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut sqlx::postgres::PgArgumentBuffer,
|
||||
) -> sqlx::encode::IsNull {
|
||||
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||
let mut encoder = sqlx::postgres::types::PgRecordEncoder::new(buf);
|
||||
encoder.encode(self.year);
|
||||
encoder.encode(self.month);
|
||||
encoder.encode(self.year)?;
|
||||
encoder.encode(self.month)?;
|
||||
encoder.finish();
|
||||
sqlx::encode::IsNull::No
|
||||
Ok(sqlx::encode::IsNull::No)
|
||||
}
|
||||
}
|
||||
let mut conn = new::<Postgres>().await?;
|
||||
|
@ -1484,7 +1484,7 @@ CREATE TYPE another.some_enum_type AS ENUM ('d', 'e', 'f');
|
|||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut sqlx::postgres::PgArgumentBuffer,
|
||||
) -> sqlx::encode::IsNull {
|
||||
) -> Result<sqlx::encode::IsNull, BoxDynError> {
|
||||
<String as sqlx::Encode<Postgres>>::encode_by_ref(&self.0, buf)
|
||||
}
|
||||
}
|
||||
|
@ -1673,7 +1673,7 @@ async fn it_encodes_custom_array_issue_1504() -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
|
||||
match self {
|
||||
Value::String(s) => <String as Encode<'_, Postgres>>::encode_by_ref(s, buf),
|
||||
Value::Number(n) => <i32 as Encode<'_, Postgres>>::encode_by_ref(n, buf),
|
||||
|
@ -1912,31 +1912,24 @@ async fn test_issue_3052() {
|
|||
|
||||
let mut conn = new::<Postgres>().await.unwrap();
|
||||
|
||||
let too_small_res = sqlx::query_scalar::<_, BigDecimal>("SELECT $1::numeric")
|
||||
let too_small_error = sqlx::query_scalar::<_, BigDecimal>("SELECT $1::numeric")
|
||||
.bind(&too_small)
|
||||
.fetch_one(&mut conn)
|
||||
.await;
|
||||
.await
|
||||
.expect_err("Too small number should have failed");
|
||||
assert!(
|
||||
matches!(&too_small_error, sqlx::Error::Encode(_)),
|
||||
"expected encode error, got {too_small_error:?}"
|
||||
);
|
||||
|
||||
match too_small_res {
|
||||
Err(sqlx::Error::Database(dbe)) => {
|
||||
let dbe = dbe.downcast::<PgDatabaseError>();
|
||||
|
||||
assert_eq!(dbe.code(), "22P03");
|
||||
}
|
||||
other => panic!("expected Err(DatabaseError), got {other:?}"),
|
||||
}
|
||||
|
||||
let too_large_res = sqlx::query_scalar::<_, BigDecimal>("SELECT $1::numeric")
|
||||
let too_large_error = sqlx::query_scalar::<_, BigDecimal>("SELECT $1::numeric")
|
||||
.bind(&too_large)
|
||||
.fetch_one(&mut conn)
|
||||
.await;
|
||||
.await
|
||||
.expect_err("Too large number should have failed");
|
||||
|
||||
match too_large_res {
|
||||
Err(sqlx::Error::Database(dbe)) => {
|
||||
let dbe = dbe.downcast::<PgDatabaseError>();
|
||||
|
||||
assert_eq!(dbe.code(), "22P03");
|
||||
}
|
||||
other => panic!("expected Err(DatabaseError), got {other:?}"),
|
||||
}
|
||||
assert!(
|
||||
matches!(&too_large_error, sqlx::Error::Encode(_)),
|
||||
"expected encode error, got {too_large_error:?}",
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue