diff --git a/Cargo.lock b/Cargo.lock index 79906bb607..9ba4c4dd7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,9 +719,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -862,6 +862,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.7.1" @@ -2046,7 +2056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3442,6 +3452,7 @@ dependencies = [ "bstr", "bytes", "chrono", + "combine", "crc", "crossbeam-queue", "either", diff --git a/Cargo.toml b/Cargo.toml index 6d08df23d3..5e11932b31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -244,6 +244,10 @@ disallowed_methods = 'deny' level = 'warn' check-cfg = [ 'cfg(mariadb, values(any()))', + 'cfg(postgres_12)', + 'cfg(postgres_13)', + 'cfg(postgres_14)', + 'cfg(postgres_15)', 'cfg(sqlite_ipaddr)', 'cfg(sqlite_test_sqlcipher)', ] diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index 37cf9d3b91..b582979e24 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -66,6 +66,7 @@ async-io = { version = "2.4.1", optional = true } base64 = { version = "0.22.0", default-features = false, features = ["std"] } bytes = "1.1.0" chrono = { version = "0.4.34", default-features = false, features = ["clock"], optional = true } +combine = "4.6" crc = { version = "3", optional = true } crossbeam-queue = "0.3.2" either = "1.6.1" diff --git a/sqlx-core/src/any/arguments.rs b/sqlx-core/src/any/arguments.rs index 2c05e3fd5b..2fda07ab5a 100644 --- a/sqlx-core/src/any/arguments.rs +++ b/sqlx-core/src/any/arguments.rs @@ -2,26 +2,29 @@ use crate::any::value::AnyValueKind; use crate::any::{Any, AnyTypeInfoKind}; use crate::arguments::Arguments; use crate::encode::{Encode, IsNull}; +use crate::encode_owned::IntoEncode; use crate::error::BoxDynError; use crate::types::Type; +use std::sync::Arc; -pub struct AnyArguments<'q> { +#[derive(Default)] +pub struct AnyArguments { #[doc(hidden)] - pub values: AnyArgumentBuffer<'q>, + pub values: AnyArgumentBuffer, } -impl<'q> Arguments<'q> for AnyArguments<'q> { +impl Arguments for AnyArguments { type Database = Any; fn reserve(&mut self, additional: usize, _size: usize) { self.values.0.reserve(additional); } - fn add(&mut self, value: T) -> Result<(), BoxDynError> + fn add<'t, T>(&mut self, value: T) -> Result<(), BoxDynError> where - T: 'q + Encode<'q, Self::Database> + Type, + T: IntoEncode + Type, { - let _: IsNull = value.encode(&mut self.values)?; + let _: IsNull = value.into_encode().encode(&mut self.values)?; Ok(()) } @@ -30,42 +33,37 @@ impl<'q> Arguments<'q> for AnyArguments<'q> { } } -pub struct AnyArgumentBuffer<'q>(#[doc(hidden)] pub Vec>); +#[derive(Default)] +pub struct AnyArgumentBuffer(#[doc(hidden)] pub Vec); -impl Default for AnyArguments<'_> { - fn default() -> Self { - AnyArguments { - values: AnyArgumentBuffer(vec![]), - } - } -} - -impl<'q> AnyArguments<'q> { +impl AnyArguments { #[doc(hidden)] - pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> Result + pub fn convert_into(self) -> Result where - 'q: 'a, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option: Type + Encode<'a, A::Database>, - Option>: Type + Encode<'a, A::Database>, - bool: Type + Encode<'a, A::Database>, - i16: Type + Encode<'a, A::Database>, - i32: Type + Encode<'a, A::Database>, - i64: Type + Encode<'a, A::Database>, - f32: Type + Encode<'a, A::Database>, - f64: Type + Encode<'a, A::Database>, - &'a str: Type + Encode<'a, A::Database>, - &'a [u8]: Type + Encode<'a, A::Database>, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option: IntoEncode + Type, + Option>: IntoEncode + Type, + bool: IntoEncode + Type, + i16: IntoEncode + Type, + i32: IntoEncode + Type, + i64: IntoEncode + Type, + f32: IntoEncode + Type, + f64: IntoEncode + Type, + String: IntoEncode + Type, + Vec: IntoEncode + Type, + Arc: IntoEncode + Type, + Arc: IntoEncode + Type, + Arc>: IntoEncode + Type, { let mut out = A::default(); - for arg in &self.values.0 { + for arg in self.values.0 { match arg { AnyValueKind::Null(AnyTypeInfoKind::Null) => out.add(Option::::None), AnyValueKind::Null(AnyTypeInfoKind::Bool) => out.add(Option::::None), @@ -82,8 +80,9 @@ impl<'q> AnyArguments<'q> { AnyValueKind::BigInt(i) => out.add(i), AnyValueKind::Real(r) => out.add(r), AnyValueKind::Double(d) => out.add(d), - AnyValueKind::Text(t) => out.add(&**t), - AnyValueKind::Blob(b) => out.add(&**b), + AnyValueKind::Text(t) => out.add(t), + AnyValueKind::TextSlice(t) => out.add(t), + AnyValueKind::Blob(b) => out.add(b), }? } Ok(out) diff --git a/sqlx-core/src/any/connection/backend.rs b/sqlx-core/src/any/connection/backend.rs index e59b345ed9..8254a7a6c6 100644 --- a/sqlx-core/src/any/connection/backend.rs +++ b/sqlx-core/src/any/connection/backend.rs @@ -4,6 +4,7 @@ use crate::sql_str::SqlStr; use either::Either; use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; +use futures_util::TryStreamExt; use std::fmt::Debug; pub trait AnyConnectionBackend: std::any::Any + Debug + Send + 'static { @@ -94,19 +95,31 @@ pub trait AnyConnectionBackend: std::any::Any + Debug + Send + 'static { )) } - fn fetch_many<'q>( - &'q mut self, + fn fetch_many( + &mut self, query: SqlStr, persistent: bool, - arguments: Option>, - ) -> BoxStream<'q, crate::Result>>; + arguments: Option, + ) -> BoxStream<'_, crate::Result>>; - fn fetch_optional<'q>( - &'q mut self, + fn fetch_optional( + &mut self, query: SqlStr, persistent: bool, - arguments: Option>, - ) -> BoxFuture<'q, crate::Result>>; + arguments: Option, + ) -> BoxFuture<'_, crate::Result>> { + let mut stream = self.fetch_many(query, persistent, arguments); + + Box::pin(async move { + while let Some(result) = stream.try_next().await? { + if let Either::Right(row) = result { + return Ok(Some(row)); + } + } + + Ok(None) + }) + } fn prepare_with<'c, 'q: 'c>( &'c mut self, diff --git a/sqlx-core/src/any/database.rs b/sqlx-core/src/any/database.rs index 6e8343e928..6dd47003a2 100644 --- a/sqlx-core/src/any/database.rs +++ b/sqlx-core/src/any/database.rs @@ -25,8 +25,8 @@ impl Database for Any { type Value = AnyValue; type ValueRef<'r> = AnyValueRef<'r>; - type Arguments<'q> = AnyArguments<'q>; - type ArgumentBuffer<'q> = AnyArgumentBuffer<'q>; + type Arguments = AnyArguments; + type ArgumentBuffer = AnyArgumentBuffer; type Statement = AnyStatement; diff --git a/sqlx-core/src/any/mod.rs b/sqlx-core/src/any/mod.rs index 032f4dda03..9d37bbf5ab 100644 --- a/sqlx-core/src/any/mod.rs +++ b/sqlx-core/src/any/mod.rs @@ -56,7 +56,7 @@ pub trait AnyExecutor<'c>: Executor<'c, Database = Any> {} impl<'c, T: Executor<'c, Database = Any>> AnyExecutor<'c> for T {} // NOTE: required due to the lack of lazy normalization -impl_into_arguments_for_arguments!(AnyArguments<'q>); +impl_into_arguments_for_arguments!(AnyArguments); // impl_executor_for_pool_connection!(Any, AnyConnection, AnyRow); // impl_executor_for_transaction!(Any, AnyRow); impl_acquire!(Any, AnyConnection); @@ -71,7 +71,7 @@ where { fn encode_by_ref( &self, - buf: &mut AnyArgumentBuffer<'q>, + buf: &mut AnyArgumentBuffer, ) -> Result { if let Some(value) = self { value.encode_by_ref(buf) diff --git a/sqlx-core/src/any/statement.rs b/sqlx-core/src/any/statement.rs index 98b28c2c32..42c7255736 100644 --- a/sqlx-core/src/any/statement.rs +++ b/sqlx-core/src/any/statement.rs @@ -44,7 +44,7 @@ impl Statement for AnyStatement { &self.columns } - impl_statement_query!(AnyArguments<'_>); + impl_statement_query!(AnyArguments); } impl ColumnIndex for &'_ str { diff --git a/sqlx-core/src/any/types/blob.rs b/sqlx-core/src/any/types/blob.rs index 851c93bf5a..c6c82dde88 100644 --- a/sqlx-core/src/any/types/blob.rs +++ b/sqlx-core/src/any/types/blob.rs @@ -4,7 +4,7 @@ use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::types::Type; -use std::borrow::Cow; +use std::sync::Arc; impl Type for [u8] { fn type_info() -> AnyTypeInfo { @@ -17,9 +17,9 @@ impl Type for [u8] { impl<'q> Encode<'q, Any> for &'q [u8] { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { - buf.0.push(AnyValueKind::Blob((*self).into())); + buf.0.push(AnyValueKind::Blob(Arc::new(self.to_vec()))); Ok(IsNull::No) } } @@ -27,12 +27,7 @@ impl<'q> Encode<'q, Any> for &'q [u8] { impl<'r> Decode<'r, Any> for &'r [u8] { fn decode(value: ::ValueRef<'r>) -> Result { match value.kind { - AnyValueKind::Blob(Cow::Borrowed(blob)) => Ok(blob), - // This shouldn't happen in practice, it means the user got an `AnyValueRef` - // constructed from an owned `Vec` which shouldn't be allowed by the API. - AnyValueKind::Blob(Cow::Owned(_text)) => { - panic!("attempting to return a borrow that outlives its buffer") - } + AnyValueKind::Blob(blob) => Ok(blob.as_slice()), other => other.unexpected(), } } @@ -44,12 +39,12 @@ impl Type for Vec { } } -impl<'q> Encode<'q, Any> for Vec { +impl Encode<'_, Any> for Vec { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { - buf.0.push(AnyValueKind::Blob(Cow::Owned(self.clone()))); + buf.0.push(AnyValueKind::Blob(Arc::new(self.clone()))); Ok(IsNull::No) } } @@ -57,7 +52,7 @@ impl<'q> Encode<'q, Any> for Vec { impl<'r> Decode<'r, Any> for Vec { fn decode(value: ::ValueRef<'r>) -> Result { match value.kind { - AnyValueKind::Blob(blob) => Ok(blob.into_owned()), + AnyValueKind::Blob(blob) => Ok(blob.as_ref().clone()), other => other.unexpected(), } } diff --git a/sqlx-core/src/any/types/bool.rs b/sqlx-core/src/any/types/bool.rs index fb7ee9d5dd..bc143e9bd5 100644 --- a/sqlx-core/src/any/types/bool.rs +++ b/sqlx-core/src/any/types/bool.rs @@ -13,10 +13,10 @@ impl Type for bool { } } -impl<'q> Encode<'q, Any> for bool { +impl Encode<'_, Any> for bool { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { buf.0.push(AnyValueKind::Bool(*self)); Ok(IsNull::No) @@ -26,7 +26,7 @@ impl<'q> Encode<'q, Any> for bool { impl<'r> Decode<'r, Any> for bool { fn decode(value: ::ValueRef<'r>) -> Result { match value.kind { - AnyValueKind::Bool(b) => Ok(b), + AnyValueKind::Bool(b) => Ok(*b), other => other.unexpected(), } } diff --git a/sqlx-core/src/any/types/float.rs b/sqlx-core/src/any/types/float.rs index 01d6073a2f..119e5ca95f 100644 --- a/sqlx-core/src/any/types/float.rs +++ b/sqlx-core/src/any/types/float.rs @@ -13,8 +13,8 @@ impl Type for f32 { } } -impl<'q> Encode<'q, Any> for f32 { - fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> Result { +impl Encode<'_, Any> for f32 { + fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result { buf.0.push(AnyValueKind::Real(*self)); Ok(IsNull::No) } @@ -23,7 +23,7 @@ impl<'q> Encode<'q, Any> for f32 { impl<'r> Decode<'r, Any> for f32 { fn decode(value: AnyValueRef<'r>) -> Result { match value.kind { - AnyValueKind::Real(r) => Ok(r), + AnyValueKind::Real(r) => Ok(*r), other => other.unexpected(), } } @@ -37,10 +37,10 @@ impl Type for f64 { } } -impl<'q> Encode<'q, Any> for f64 { +impl Encode<'_, Any> for f64 { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { buf.0.push(AnyValueKind::Double(*self)); Ok(IsNull::No) @@ -51,8 +51,8 @@ impl<'r> Decode<'r, Any> for f64 { fn decode(value: ::ValueRef<'r>) -> Result { match value.kind { // Widening is safe - AnyValueKind::Real(r) => Ok(r as f64), - AnyValueKind::Double(d) => Ok(d), + AnyValueKind::Real(r) => Ok(*r as f64), + AnyValueKind::Double(d) => Ok(*d), other => other.unexpected(), } } diff --git a/sqlx-core/src/any/types/int.rs b/sqlx-core/src/any/types/int.rs index 56152af146..e2bb43212f 100644 --- a/sqlx-core/src/any/types/int.rs +++ b/sqlx-core/src/any/types/int.rs @@ -17,10 +17,10 @@ impl Type for i16 { } } -impl<'q> Encode<'q, Any> for i16 { +impl Encode<'_, Any> for i16 { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { buf.0.push(AnyValueKind::SmallInt(*self)); Ok(IsNull::No) @@ -45,10 +45,10 @@ impl Type for i32 { } } -impl<'q> Encode<'q, Any> for i32 { +impl Encode<'_, Any> for i32 { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { buf.0.push(AnyValueKind::Integer(*self)); Ok(IsNull::No) @@ -73,10 +73,10 @@ impl Type for i64 { } } -impl<'q> Encode<'q, Any> for i64 { +impl Encode<'_, Any> for i64 { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { buf.0.push(AnyValueKind::BigInt(*self)); Ok(IsNull::No) diff --git a/sqlx-core/src/any/types/str.rs b/sqlx-core/src/any/types/str.rs index 4c00832690..620262491b 100644 --- a/sqlx-core/src/any/types/str.rs +++ b/sqlx-core/src/any/types/str.rs @@ -5,7 +5,7 @@ use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::types::Type; -use std::borrow::Cow; +use std::sync::Arc; impl Type for str { fn type_info() -> AnyTypeInfo { @@ -16,17 +16,17 @@ impl Type for str { } impl<'a> Encode<'a, Any> for &'a str { - fn encode(self, buf: &mut ::ArgumentBuffer<'a>) -> Result + fn encode(self, buf: &mut ::ArgumentBuffer) -> Result where Self: Sized, { - buf.0.push(AnyValueKind::Text(self.into())); + buf.0.push(AnyValueKind::Text(Arc::new(self.into()))); Ok(IsNull::No) } fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'a>, + buf: &mut ::ArgumentBuffer, ) -> Result { (*self).encode(buf) } @@ -35,12 +35,7 @@ impl<'a> Encode<'a, Any> for &'a str { impl<'a> Decode<'a, Any> for &'a str { fn decode(value: ::ValueRef<'a>) -> Result { match value.kind { - AnyValueKind::Text(Cow::Borrowed(text)) => Ok(text), - // This shouldn't happen in practice, it means the user got an `AnyValueRef` - // constructed from an owned `String` which shouldn't be allowed by the API. - AnyValueKind::Text(Cow::Owned(_text)) => { - panic!("attempting to return a borrow that outlives its buffer") - } + AnyValueKind::Text(text) => Ok(text.as_str()), other => other.unexpected(), } } @@ -52,12 +47,17 @@ impl Type for String { } } -impl<'q> Encode<'q, Any> for String { +impl Encode<'_, Any> for String { + fn encode(self, buf: &mut ::ArgumentBuffer) -> Result { + buf.0.push(AnyValueKind::Text(Arc::new(self))); + Ok(IsNull::No) + } + fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { - buf.0.push(AnyValueKind::Text(Cow::Owned(self.clone()))); + buf.0.push(AnyValueKind::Text(Arc::new(self.clone()))); Ok(IsNull::No) } } @@ -65,7 +65,7 @@ impl<'q> Encode<'q, Any> for String { impl<'r> Decode<'r, Any> for String { fn decode(value: ::ValueRef<'r>) -> Result { match value.kind { - AnyValueKind::Text(text) => Ok(text.into_owned()), + AnyValueKind::Text(text) => Ok(text.to_string()), other => other.unexpected(), } } diff --git a/sqlx-core/src/any/value.rs b/sqlx-core/src/any/value.rs index 9917b39f4f..a85c1dc69c 100644 --- a/sqlx-core/src/any/value.rs +++ b/sqlx-core/src/any/value.rs @@ -1,14 +1,14 @@ -use std::borrow::Cow; - use crate::any::{Any, AnyTypeInfo, AnyTypeInfoKind}; use crate::database::Database; use crate::error::BoxDynError; use crate::types::Type; use crate::value::{Value, ValueRef}; +use std::borrow::Cow; +use std::sync::Arc; #[derive(Clone, Debug)] #[non_exhaustive] -pub enum AnyValueKind<'a> { +pub enum AnyValueKind { Null(AnyTypeInfoKind), Bool(bool), SmallInt(i16), @@ -16,11 +16,12 @@ pub enum AnyValueKind<'a> { BigInt(i64), Real(f32), Double(f64), - Text(Cow<'a, str>), - Blob(Cow<'a, [u8]>), + Text(Arc), + TextSlice(Arc), + Blob(Arc>), } -impl AnyValueKind<'_> { +impl AnyValueKind { fn type_info(&self) -> AnyTypeInfo { AnyTypeInfo { kind: match self { @@ -32,6 +33,7 @@ impl AnyValueKind<'_> { AnyValueKind::Real(_) => AnyTypeInfoKind::Real, AnyValueKind::Double(_) => AnyTypeInfoKind::Double, AnyValueKind::Text(_) => AnyTypeInfoKind::Text, + AnyValueKind::TextSlice(_) => AnyTypeInfoKind::Text, AnyValueKind::Blob(_) => AnyTypeInfoKind::Blob, }, } @@ -60,31 +62,19 @@ impl AnyValueKind<'_> { #[derive(Clone, Debug)] pub struct AnyValue { #[doc(hidden)] - pub kind: AnyValueKind<'static>, + pub kind: AnyValueKind, } #[derive(Clone, Debug)] pub struct AnyValueRef<'a> { - pub(crate) kind: AnyValueKind<'a>, + pub(crate) kind: &'a AnyValueKind, } impl Value for AnyValue { type Database = Any; fn as_ref(&self) -> ::ValueRef<'_> { - AnyValueRef { - kind: match &self.kind { - AnyValueKind::Null(k) => AnyValueKind::Null(*k), - AnyValueKind::Bool(b) => AnyValueKind::Bool(*b), - AnyValueKind::SmallInt(i) => AnyValueKind::SmallInt(*i), - AnyValueKind::Integer(i) => AnyValueKind::Integer(*i), - AnyValueKind::BigInt(i) => AnyValueKind::BigInt(*i), - AnyValueKind::Real(r) => AnyValueKind::Real(*r), - AnyValueKind::Double(d) => AnyValueKind::Double(*d), - AnyValueKind::Text(t) => AnyValueKind::Text(Cow::Borrowed(t)), - AnyValueKind::Blob(b) => AnyValueKind::Blob(Cow::Borrowed(b)), - }, - } + AnyValueRef { kind: &self.kind } } fn type_info(&self) -> Cow<'_, ::TypeInfo> { @@ -101,17 +91,7 @@ impl<'a> ValueRef<'a> for AnyValueRef<'a> { fn to_owned(&self) -> ::Value { AnyValue { - kind: match &self.kind { - AnyValueKind::Null(k) => AnyValueKind::Null(*k), - AnyValueKind::Bool(b) => AnyValueKind::Bool(*b), - AnyValueKind::SmallInt(i) => AnyValueKind::SmallInt(*i), - AnyValueKind::Integer(i) => AnyValueKind::Integer(*i), - AnyValueKind::BigInt(i) => AnyValueKind::BigInt(*i), - AnyValueKind::Real(r) => AnyValueKind::Real(*r), - AnyValueKind::Double(d) => AnyValueKind::Double(*d), - AnyValueKind::Text(t) => AnyValueKind::Text(Cow::Owned(t.to_string())), - AnyValueKind::Blob(b) => AnyValueKind::Blob(Cow::Owned(b.to_vec())), - }, + kind: self.kind.clone(), } } diff --git a/sqlx-core/src/arguments.rs b/sqlx-core/src/arguments.rs index 4bf826ed31..89cc67e8dd 100644 --- a/sqlx-core/src/arguments.rs +++ b/sqlx-core/src/arguments.rs @@ -1,7 +1,7 @@ //! Types and traits for passing arguments to SQL queries. use crate::database::Database; -use crate::encode::Encode; +use crate::encode_owned::IntoEncode; use crate::error::BoxDynError; use crate::types::Type; use std::fmt::{self, Write}; @@ -9,7 +9,7 @@ use std::fmt::{self, Write}; /// A tuple of arguments to be sent to the database. // This lint is designed for general collections, but `Arguments` is not meant to be as such. #[allow(clippy::len_without_is_empty)] -pub trait Arguments<'q>: Send + Sized + Default { +pub trait Arguments: Send + Sized + Default { type Database: Database; /// Reserves the capacity for at least `additional` more values (of `size` total bytes) to @@ -19,7 +19,7 @@ pub trait Arguments<'q>: Send + Sized + Default { /// Add the value to the end of the arguments. fn add(&mut self, value: T) -> Result<(), BoxDynError> where - T: 'q + Encode<'q, Self::Database> + Type; + T: IntoEncode + Type; /// The number of arguments that were already added. fn len(&self) -> usize; @@ -29,19 +29,17 @@ pub trait Arguments<'q>: Send + Sized + Default { } } -pub trait IntoArguments<'q, DB: Database>: Sized + Send { - fn into_arguments(self) -> ::Arguments<'q>; +pub trait IntoArguments: Sized + Send { + fn into_arguments(self) -> ::Arguments; } // NOTE: required due to lack of lazy normalization #[macro_export] macro_rules! impl_into_arguments_for_arguments { ($Arguments:path) => { - impl<'q> - $crate::arguments::IntoArguments< - 'q, - <$Arguments as $crate::arguments::Arguments<'q>>::Database, - > for $Arguments + impl + $crate::arguments::IntoArguments<<$Arguments as $crate::arguments::Arguments>::Database> + for $Arguments { fn into_arguments(self) -> $Arguments { self @@ -51,13 +49,10 @@ macro_rules! impl_into_arguments_for_arguments { } /// used by the query macros to prevent supernumerary `.bind()` calls -pub struct ImmutableArguments<'q, DB: Database>(pub ::Arguments<'q>); +pub struct ImmutableArguments(pub ::Arguments); -impl<'q, DB: Database> IntoArguments<'q, DB> for ImmutableArguments<'q, DB> { - fn into_arguments(self) -> ::Arguments<'q> { +impl IntoArguments for ImmutableArguments { + fn into_arguments(self) -> ::Arguments { self.0 } } - -// TODO: Impl `IntoArguments` for &[&dyn Encode] -// TODO: Impl `IntoArguments` for (impl Encode, ...) x16 diff --git a/sqlx-core/src/database.rs b/sqlx-core/src/database.rs index 02d7a1214e..a18624bf23 100644 --- a/sqlx-core/src/database.rs +++ b/sqlx-core/src/database.rs @@ -69,7 +69,7 @@ use crate::value::{Value, ValueRef}; /// /// This trait encapsulates a complete set of traits that implement a driver for a /// specific database (e.g., MySQL, PostgreSQL). -pub trait Database: 'static + Sized + Send + Debug { +pub trait Database: 'static + Sized + Send + Sync + Debug { /// The concrete `Connection` implementation for this database. type Connection: Connection; @@ -96,9 +96,9 @@ pub trait Database: 'static + Sized + Send + Debug { type ValueRef<'r>: ValueRef<'r, Database = Self>; /// The concrete `Arguments` implementation for this database. - type Arguments<'q>: Arguments<'q, Database = Self>; + type Arguments: Arguments; /// The concrete type used as a buffer for arguments while encoding. - type ArgumentBuffer<'q>; + type ArgumentBuffer; /// The concrete `Statement` implementation for this database. type Statement: Statement; diff --git a/sqlx-core/src/encode.rs b/sqlx-core/src/encode.rs index ba9a1d40c9..7d9ea9b0b4 100644 --- a/sqlx-core/src/encode.rs +++ b/sqlx-core/src/encode.rs @@ -29,7 +29,7 @@ impl IsNull { /// 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. - fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result + fn encode(self, buf: &mut ::ArgumentBuffer) -> Result where Self: Sized, { @@ -42,7 +42,7 @@ pub trait Encode<'q, DB: Database> { /// memory. fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result; fn produces(&self) -> Option { @@ -62,14 +62,14 @@ where T: Encode<'q, DB>, { #[inline] - fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + fn encode(self, buf: &mut ::ArgumentBuffer) -> Result { >::encode_by_ref(self, buf) } #[inline] fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { <&T as Encode>::encode(self, buf) } @@ -104,7 +104,7 @@ macro_rules! impl_encode_for_option { #[inline] fn encode( self, - buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer<'q>, + buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer, ) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> { if let Some(v) = self { v.encode(buf) @@ -116,7 +116,7 @@ macro_rules! impl_encode_for_option { #[inline] fn encode_by_ref( &self, - buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer<'q>, + buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer, ) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> { if let Some(v) = self { v.encode_by_ref(buf) @@ -142,7 +142,7 @@ macro_rules! impl_encode_for_smartpointer { #[inline] fn encode( self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { >::encode_by_ref(self.as_ref(), buf) } @@ -150,7 +150,7 @@ macro_rules! impl_encode_for_smartpointer { #[inline] fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { <&T as Encode>::encode(self, buf) } @@ -172,20 +172,20 @@ impl_encode_for_smartpointer!(Arc); impl_encode_for_smartpointer!(Box); impl_encode_for_smartpointer!(Rc); -impl<'q, T, DB: Database> Encode<'q, DB> for Cow<'q, T> +impl Encode<'_, DB> for Cow<'_, T> where - T: Encode<'q, DB>, + T: for<'e> Encode<'e, DB>, T: ToOwned, { #[inline] - fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + fn encode(self, buf: &mut ::ArgumentBuffer) -> Result { <&T as Encode>::encode_by_ref(&self.as_ref(), buf) } #[inline] fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { <&T as Encode>::encode_by_ref(&self.as_ref(), buf) } @@ -207,10 +207,19 @@ macro_rules! forward_encode_impl { impl<'q> Encode<'q, $db> for $for_type { fn encode_by_ref( &self, - buf: &mut <$db as sqlx_core::database::Database>::ArgumentBuffer<'q>, + buf: &mut <$db as sqlx_core::database::Database>::ArgumentBuffer, ) -> Result { <$forward_to as Encode<$db>>::encode(self.as_ref(), buf) } } }; } + +impl<'q, DB: Database> Encode<'q, DB> for Box> { + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer, + ) -> Result { + self.as_ref().encode_by_ref(buf) + } +} diff --git a/sqlx-core/src/encode_owned.rs b/sqlx-core/src/encode_owned.rs new file mode 100644 index 0000000000..f9089d7674 --- /dev/null +++ b/sqlx-core/src/encode_owned.rs @@ -0,0 +1,402 @@ +use crate::database::Database; +use crate::encode::{Encode, IsNull}; +use crate::error::BoxDynError; +use std::borrow::Cow; +use std::fmt::Debug; +use std::rc::Rc; +use std::sync::Arc; + +use crate::types::Type; + +#[cfg(feature = "uuid")] +use uuid::Uuid; + +pub trait IntoEncode +where + DB: Database, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's; + + fn into_encode_owned(self) -> impl EncodeOwned + 'static; +} + +impl IntoEncode for &T +where + DB: Database, + T: for<'e> Encode<'e, DB>, + T: Clone + IntoEncode + Debug + Send + Sync, + T: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + (*self).clone().into_encode_owned() + } +} + +impl<'a, DB, T> IntoEncode for &'a [T] +where + DB: Database, + Self: for<'e> Encode<'e, DB>, + Self: ToOwned, + Self: Type, + <&'a [T] as ToOwned>::Owned: Debug + Send + Sync + 'static + Encode<'static, DB> + Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.to_owned()) + } +} + +impl IntoEncode for [T; N] +where + DB: Database, + for<'e> Self: Encode<'e, DB>, + Self: Type, + T: Clone, + Vec: Debug + Send + Sync + 'static + Encode<'static, DB> + Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + Type + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.to_vec()) + } +} + +impl<'a, DB> IntoEncode for &'a str +where + Self: 'a, + DB: Database, + for<'e> &'e str: Encode<'e, DB> + Type, + String: Encode<'static, DB>, + String: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.to_string()) + } +} + +pub trait EncodeOwned: Encode<'static, DB> + Debug + Send + Sync { + fn type_info(&self) -> DB::TypeInfo; + fn type_compatible(&self, ty: &DB::TypeInfo) -> bool; +} + +#[derive(Debug)] +pub struct EncodeClone> { + value: T, + db: std::marker::PhantomData, +} + +impl + Type> Encode<'static, DB> + for EncodeClone +{ + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer, + ) -> Result { + self.value.encode_by_ref(buf) + } +} + +impl + Type> Type + for EncodeClone +{ + fn type_info() -> ::TypeInfo + where + Self: Sized, + { + T::type_info() + } +} + +impl + Type> EncodeOwned + for EncodeClone +{ + fn type_info(&self) -> DB::TypeInfo { + >::type_info() + } + + fn type_compatible(&self, ty: &DB::TypeInfo) -> bool { + >::compatible(ty) + } +} + +impl + Type> From + for EncodeClone +{ + fn from(value: T) -> Self { + Self { + value, + db: std::marker::PhantomData, + } + } +} + +#[macro_export] +macro_rules! impl_into_encode { + ($t:ty) => { + impl $crate::encode_owned::IntoEncode for $t + where + DB: $crate::database::Database, + Self: for<'e> Encode<'e, DB>, + Self: Type, + { + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl $crate::encode_owned::EncodeOwned + 'static { + $crate::encode_owned::EncodeClone::from(self) + } + } + }; +} + +#[macro_export] +macro_rules! impl_into_encode_for_db { + ($db:ty, $t:ty) => { + impl $crate::encode_owned::IntoEncode<$db> for $t + where + Self: for<'e> $crate::encode::Encode<'e, $db> + $crate::types::Type<$db>, + { + fn into_encode<'s>( + self, + ) -> impl $crate::encode::Encode<'s, $db> + $crate::types::Type<$db> + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl $crate::encode_owned::EncodeOwned<$db> + 'static { + $crate::encode_owned::EncodeClone::from(self) + } + } + }; +} + +impl IntoEncode for Box +where + DB: Database, + Self: for<'e> Encode<'e, DB> + Type, + T: Debug + Send + Sync + 'static, + T: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self) + } +} + +impl IntoEncode for Arc +where + DB: Database, + Self: for<'e> Encode<'e, DB> + Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self) + } +} + +impl IntoEncode for Rc +where + DB: Database, + Rc: for<'e> Encode<'e, DB>, + T: Clone + Debug + Send + Sync + 'static, + T: for<'e> Encode<'e, DB>, + T: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.as_ref().clone()) + } +} + +impl IntoEncode for Rc +where + DB: Database, + Rc: for<'e> Encode<'e, DB> + Type, + String: for<'e> Encode<'e, DB>, + String: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.to_string()) + } +} + +impl IntoEncode for Rc<[u8]> +where + DB: Database, + Rc<[u8]>: for<'e> Encode<'e, DB> + Type, + Vec: for<'e> Encode<'e, DB>, + Vec: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.as_ref().to_vec()) + } +} + +impl IntoEncode for Arc +where + DB: Database, + T: Debug + Send + Sync + 'static, + Arc: for<'e> Encode<'e, DB>, + Self: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self) + } +} + +impl IntoEncode for Cow<'_, T> +where + DB: Database, + T: ToOwned + ?Sized, + Self: for<'f> Encode<'f, DB> + Type, + T::Owned: for<'f> Encode<'f, DB> + Type + Debug + Send + Sync + 'static, + T::Owned: IntoEncode + Clone + Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + >::into_encode_owned(self.as_ref().to_owned()) + } +} + +impl IntoEncode for Option +where + DB: Database, + T: Encode<'static, DB> + Type + Debug + Send + Sync, + Self: for<'e> Encode<'e, DB>, + Self: Type, + // todo: remove after into_encode_owned is fixed + i32: Encode<'static, DB> + Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + // todo: fix. Needs T to be 'static but that breaks usage in query macros + EncodeClone::from(0i32) + } +} + +impl IntoEncode for Vec +where + DB: Database, + Vec: for<'e> Encode<'e, DB>, + T: Clone + Debug + Send + Sync + 'static, + Self: for<'e> Encode<'e, DB>, + Self: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self) + } +} + +impl_into_encode!(bool); + +impl_into_encode!(f32); +impl_into_encode!(f64); + +impl_into_encode!(i8); +impl_into_encode!(i16); +impl_into_encode!(i32); +impl_into_encode!(i64); + +impl_into_encode!(u8); +impl_into_encode!(u16); +impl_into_encode!(u32); +impl_into_encode!(u64); + +impl_into_encode!(String); +impl_into_encode!(Box); +impl_into_encode!(Box<[u8]>); +impl_into_encode!(Arc<[u8]>); + +#[cfg(feature = "uuid")] +impl_into_encode!(Uuid); diff --git a/sqlx-core/src/executor.rs b/sqlx-core/src/executor.rs index ab9737c9cd..e1c42fc706 100644 --- a/sqlx-core/src/executor.rs +++ b/sqlx-core/src/executor.rs @@ -204,13 +204,13 @@ pub trait Execute<'q, DB: Database>: Send + Sized { /// will be prepared (and cached) before execution. /// /// Returns `Err` if encoding any of the arguments failed. - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError>; + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError>; /// Returns `true` if the statement should be cached. fn persistent(&self) -> bool; } -impl<'q, DB: Database, T> Execute<'q, DB> for T +impl Execute<'_, DB> for T where T: SqlSafeStr + Send, { @@ -225,7 +225,7 @@ where } #[inline] - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { Ok(None) } @@ -235,7 +235,7 @@ where } } -impl<'q, DB: Database, T> Execute<'q, DB> for (T, Option<::Arguments<'q>>) +impl Execute<'_, DB> for (T, Option<::Arguments>) where T: SqlSafeStr + Send, { @@ -250,7 +250,7 @@ where } #[inline] - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { Ok(self.1.take()) } diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 494c41e9bf..80a88c8912 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -63,6 +63,8 @@ pub mod statement; pub mod common; pub mod database; pub mod describe; +#[macro_use] +pub mod encode_owned; pub mod executor; pub mod from_row; pub mod fs; diff --git a/sqlx-core/src/query.rs b/sqlx-core/src/query.rs index 97c166116a..bf8d7eee51 100644 --- a/sqlx-core/src/query.rs +++ b/sqlx-core/src/query.rs @@ -6,7 +6,7 @@ use futures_util::{StreamExt, TryFutureExt, TryStreamExt}; use crate::arguments::{Arguments, IntoArguments}; use crate::database::{Database, HasStatementCache}; -use crate::encode::Encode; +use crate::encode_owned::IntoEncode; use crate::error::{BoxDynError, Error}; use crate::executor::{Execute, Executor}; use crate::sql_str::{SqlSafeStr, SqlStr}; @@ -42,7 +42,7 @@ pub struct Map<'q, DB: Database, F, A> { impl<'q, DB, A> Execute<'q, DB> for Query<'q, DB, A> where DB: Database, - A: Send + IntoArguments<'q, DB>, + A: Send + IntoArguments, { #[inline] fn sql(self) -> SqlStr { @@ -60,7 +60,7 @@ where } #[inline] - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { self.arguments .take() .transpose() @@ -73,7 +73,7 @@ where } } -impl<'q, DB: Database> Query<'q, DB, ::Arguments<'q>> { +impl Query<'_, DB, ::Arguments> { /// Bind a value for use with this SQL query. /// /// If the number of times this is called does not match the number of bind parameters that @@ -84,7 +84,7 @@ impl<'q, DB: Database> Query<'q, DB, ::Arguments<'q>> { /// 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 + Type>(mut self, value: T) -> Self { + pub fn bind + Type>(mut self, value: T) -> Self { let Ok(arguments) = self.get_arguments() else { return self; }; @@ -101,7 +101,7 @@ impl<'q, DB: Database> Query<'q, DB, ::Arguments<'q>> { } /// Like [`Query::try_bind`] but immediately returns an error if encoding the value failed. - pub fn try_bind + Type>( + pub fn try_bind<'t, T: 't + IntoEncode + Type>( &mut self, value: T, ) -> Result<(), BoxDynError> { @@ -110,7 +110,7 @@ impl<'q, DB: Database> Query<'q, DB, ::Arguments<'q>> { arguments.add(value) } - fn get_arguments(&mut self) -> Result<&mut DB::Arguments<'q>, BoxDynError> { + fn get_arguments(&mut self) -> Result<&mut DB::Arguments, 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() @@ -144,7 +144,7 @@ where impl<'q, DB, A: Send> Query<'q, DB, A> where DB: Database, - A: 'q + IntoArguments<'q, DB>, + A: 'q + IntoArguments, { /// Map each row in the result to another type. /// @@ -301,7 +301,7 @@ where impl<'q, DB, F: Send, A: Send> Execute<'q, DB> for Map<'q, DB, F, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, { #[inline] fn sql(self) -> SqlStr { @@ -314,7 +314,7 @@ where } #[inline] - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { self.inner.take_arguments() } @@ -329,7 +329,7 @@ where DB: Database, F: FnMut(DB::Row) -> Result + Send, O: Send + Unpin, - A: 'q + Send + IntoArguments<'q, DB>, + A: 'q + Send + IntoArguments, { /// Map each row in the result to another type. /// @@ -500,9 +500,7 @@ where } /// Execute a single SQL query as a prepared statement (explicitly created). -pub fn query_statement( - statement: &DB::Statement, -) -> Query<'_, DB, ::Arguments<'_>> +pub fn query_statement(statement: &DB::Statement) -> Query<'_, DB, ::Arguments> where DB: Database, { @@ -515,13 +513,10 @@ where } /// Execute a single SQL query as a prepared statement (explicitly created), with the given arguments. -pub fn query_statement_with<'q, DB, A>( - statement: &'q DB::Statement, - arguments: A, -) -> Query<'q, DB, A> +pub fn query_statement_with(statement: &DB::Statement, arguments: A) -> Query<'_, DB, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, { Query { database: PhantomData, @@ -655,7 +650,7 @@ where /// /// As an additional benefit, query parameters are usually sent in a compact binary encoding instead of a human-readable /// text encoding, which saves bandwidth. -pub fn query<'a, DB>(sql: impl SqlSafeStr) -> Query<'a, DB, ::Arguments<'a>> +pub fn query<'a, DB>(sql: impl SqlSafeStr) -> Query<'a, DB, ::Arguments> where DB: Database, { @@ -673,7 +668,7 @@ where pub fn query_with<'q, DB, A>(sql: impl SqlSafeStr, arguments: A) -> Query<'q, DB, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, { query_with_result(sql, Ok(arguments)) } @@ -685,7 +680,7 @@ pub fn query_with_result<'q, DB, A>( ) -> Query<'q, DB, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, { Query { database: PhantomData, diff --git a/sqlx-core/src/query_as.rs b/sqlx-core/src/query_as.rs index e58a3f0f8b..54eb266eaa 100644 --- a/sqlx-core/src/query_as.rs +++ b/sqlx-core/src/query_as.rs @@ -6,7 +6,7 @@ use futures_util::{StreamExt, TryStreamExt}; use crate::arguments::IntoArguments; use crate::database::{Database, HasStatementCache}; -use crate::encode::Encode; +use crate::encode_owned::IntoEncode; use crate::error::{BoxDynError, Error}; use crate::executor::{Execute, Executor}; use crate::from_row::FromRow; @@ -25,7 +25,7 @@ pub struct QueryAs<'q, DB: Database, O, A> { impl<'q, DB, O: Send, A: Send> Execute<'q, DB> for QueryAs<'q, DB, O, A> where DB: Database, - A: 'q + IntoArguments<'q, DB>, + A: 'q + IntoArguments, { #[inline] fn sql(self) -> SqlStr { @@ -38,7 +38,7 @@ where } #[inline] - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { self.inner.take_arguments() } @@ -48,11 +48,11 @@ where } } -impl<'q, DB: Database, O> QueryAs<'q, DB, O, ::Arguments<'q>> { +impl<'q, DB: Database, O> QueryAs<'q, DB, O, ::Arguments> { /// Bind a value for use with this SQL query. /// /// See [`Query::bind`](Query::bind). - pub fn bind + Type>(mut self, value: T) -> Self { + pub fn bind + Type>(mut self, value: T) -> Self { self.inner = self.inner.bind(value); self } @@ -83,7 +83,7 @@ where impl<'q, DB, O, A> QueryAs<'q, DB, O, A> where DB: Database, - A: 'q + IntoArguments<'q, DB>, + A: 'q + IntoArguments, O: Send + Unpin + for<'r> FromRow<'r, DB::Row>, { /// Execute the query and return the generated results as a stream. @@ -338,9 +338,7 @@ where /// /// ``` #[inline] -pub fn query_as<'q, DB, O>( - sql: impl SqlSafeStr, -) -> QueryAs<'q, DB, O, ::Arguments<'q>> +pub fn query_as<'q, DB, O>(sql: impl SqlSafeStr) -> QueryAs<'q, DB, O, ::Arguments> where DB: Database, O: for<'r> FromRow<'r, DB::Row>, @@ -361,7 +359,7 @@ where pub fn query_as_with<'q, DB, O, A>(sql: impl SqlSafeStr, arguments: A) -> QueryAs<'q, DB, O, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, O: for<'r> FromRow<'r, DB::Row>, { query_as_with_result(sql, Ok(arguments)) @@ -375,7 +373,7 @@ pub fn query_as_with_result<'q, DB, O, A>( ) -> QueryAs<'q, DB, O, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, O: for<'r> FromRow<'r, DB::Row>, { QueryAs { @@ -387,7 +385,7 @@ where // Make a SQL query from a statement, that is mapped to a concrete type. pub fn query_statement_as( statement: &DB::Statement, -) -> QueryAs<'_, DB, O, ::Arguments<'_>> +) -> QueryAs<'_, DB, O, ::Arguments> where DB: Database, O: for<'r> FromRow<'r, DB::Row>, @@ -405,7 +403,7 @@ pub fn query_statement_as_with<'q, DB, O, A>( ) -> QueryAs<'q, DB, O, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, O: for<'r> FromRow<'r, DB::Row>, { QueryAs { diff --git a/sqlx-core/src/query_builder.rs b/sqlx-core/src/query_builder.rs index 7dff67831c..198cb48a11 100644 --- a/sqlx-core/src/query_builder.rs +++ b/sqlx-core/src/query_builder.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use crate::arguments::{Arguments, IntoArguments}; use crate::database::Database; -use crate::encode::Encode; +use crate::encode_owned::IntoEncode; use crate::from_row::FromRow; use crate::query::Query; use crate::query_as::QueryAs; @@ -25,16 +25,16 @@ use crate::Either; /// and `UNNEST()`. [See our FAQ] for details. /// /// [See our FAQ]: https://github.com/launchbadge/sqlx/blob/master/FAQ.md#how-can-i-bind-an-array-to-a-values-clause-how-can-i-do-bulk-inserts -pub struct QueryBuilder<'args, DB> +pub struct QueryBuilder where DB: Database, { query: Arc, init_len: usize, - arguments: Option<::Arguments<'args>>, + arguments: Option<::Arguments>, } -impl Default for QueryBuilder<'_, DB> { +impl Default for QueryBuilder { fn default() -> Self { QueryBuilder { init_len: 0, @@ -46,7 +46,7 @@ impl Default for QueryBuilder<'_, DB> { const ERROR: &str = "BUG: query must not be shared at this point in time"; -impl<'args, DB: Database> QueryBuilder<'args, DB> +impl QueryBuilder where DB: Database, { @@ -55,7 +55,7 @@ where /// Start building a query with an initial SQL fragment, which may be an empty string. pub fn new(init: impl Into) -> Self where - ::Arguments<'args>: Default, + ::Arguments: Default, { let init = init.into(); @@ -73,7 +73,7 @@ where pub fn with_arguments(init: impl Into, arguments: A) -> Self where DB: Database, - A: IntoArguments<'args, DB>, + A: IntoArguments, { let init = init.into(); @@ -154,7 +154,7 @@ where /// [postgres-limit-issue]: https://github.com/launchbadge/sqlx/issues/671#issuecomment-687043510 pub fn push_bind(&mut self, value: T) -> &mut Self where - T: 'args + Encode<'args, DB> + Type, + T: IntoEncode + Type, { self.sanity_check(); @@ -165,6 +165,7 @@ where arguments.add(value).expect("Failed to add argument"); let query: &mut String = Arc::get_mut(&mut self.query).expect(ERROR); + arguments .format_placeholder(query) .expect("error in format_placeholder"); @@ -199,9 +200,8 @@ where /// assert!(sql.ends_with("in (?, ?) ")); /// # } /// ``` - pub fn separated<'qb, Sep>(&'qb mut self, separator: Sep) -> Separated<'qb, 'args, DB, Sep> + pub fn separated(&mut self, separator: Sep) -> Separated<'_, DB, Sep> where - 'args: 'qb, Sep: Display, { self.sanity_check(); @@ -313,7 +313,7 @@ where pub fn push_values(&mut self, tuples: I, mut push_tuple: F) -> &mut Self where I: IntoIterator, - F: FnMut(Separated<'_, 'args, DB, &'static str>, I::Item), + F: FnMut(Separated<'_, DB, &'static str>, I::Item), { self.sanity_check(); @@ -425,7 +425,7 @@ where pub fn push_tuples(&mut self, tuples: I, mut push_tuple: F) -> &mut Self where I: IntoIterator, - F: FnMut(Separated<'_, 'args, DB, &'static str>, I::Item), + F: FnMut(Separated<'_, DB, &'static str>, I::Item), { self.sanity_check(); @@ -457,7 +457,7 @@ where /// to the state it was in immediately after [`new()`][Self::new]. /// /// Calling any other method but `.reset()` after `.build()` will panic for sanity reasons. - pub fn build(&mut self) -> Query<'_, DB, ::Arguments<'args>> { + pub fn build(&mut self) -> Query<'_, DB, ::Arguments> { self.sanity_check(); Query { @@ -482,7 +482,7 @@ where /// Calling any other method but `.reset()` after `.build()` will panic for sanity reasons. pub fn build_query_as<'q, T: FromRow<'q, DB::Row>>( &'q mut self, - ) -> QueryAs<'q, DB, T, ::Arguments<'args>> { + ) -> QueryAs<'q, DB, T, ::Arguments> { QueryAs { inner: self.build(), output: PhantomData, @@ -503,7 +503,7 @@ where /// Calling any other method but `.reset()` after `.build()` will panic for sanity reasons. pub fn build_query_scalar<'q, T>( &'q mut self, - ) -> QueryScalar<'q, DB, T, ::Arguments<'args>> + ) -> QueryScalar<'q, DB, T, ::Arguments> where DB: Database, (T,): for<'r> FromRow<'r, DB::Row>, @@ -547,16 +547,16 @@ where /// /// See [`QueryBuilder::separated()`] for details. #[allow(explicit_outlives_requirements)] -pub struct Separated<'qb, 'args: 'qb, DB, Sep> +pub struct Separated<'qb, DB, Sep> where DB: Database, { - query_builder: &'qb mut QueryBuilder<'args, DB>, + query_builder: &'qb mut QueryBuilder, separator: Sep, push_separator: bool, } -impl<'qb, 'args: 'qb, DB, Sep> Separated<'qb, 'args, DB, Sep> +impl Separated<'_, DB, Sep> where DB: Database, Sep: Display, @@ -589,7 +589,7 @@ where /// See [`QueryBuilder::push_bind()`] for details. pub fn push_bind(&mut self, value: T) -> &mut Self where - T: 'args + Encode<'args, DB> + Type, + T: IntoEncode + Type, { if self.push_separator { self.query_builder.push(&self.separator); @@ -607,7 +607,7 @@ where /// Simply calls [`QueryBuilder::push_bind()`] directly. pub fn push_bind_unseparated(&mut self, value: T) -> &mut Self where - T: 'args + Encode<'args, DB> + Type, + T: IntoEncode + Type, { self.query_builder.push_bind(value); self diff --git a/sqlx-core/src/query_scalar.rs b/sqlx-core/src/query_scalar.rs index 1059463874..70bd3e48fa 100644 --- a/sqlx-core/src/query_scalar.rs +++ b/sqlx-core/src/query_scalar.rs @@ -4,7 +4,7 @@ use futures_util::{StreamExt, TryFutureExt, TryStreamExt}; use crate::arguments::IntoArguments; use crate::database::{Database, HasStatementCache}; -use crate::encode::Encode; +use crate::encode_owned::IntoEncode; use crate::error::{BoxDynError, Error}; use crate::executor::{Execute, Executor}; use crate::from_row::FromRow; @@ -23,7 +23,7 @@ pub struct QueryScalar<'q, DB: Database, O, A> { impl<'q, DB: Database, O: Send, A: Send> Execute<'q, DB> for QueryScalar<'q, DB, O, A> where - A: 'q + IntoArguments<'q, DB>, + A: 'q + IntoArguments, { #[inline] fn sql(self) -> SqlStr { @@ -35,7 +35,7 @@ where } #[inline] - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { self.inner.take_arguments() } @@ -45,11 +45,11 @@ where } } -impl<'q, DB: Database, O> QueryScalar<'q, DB, O, ::Arguments<'q>> { +impl<'q, DB: Database, O> QueryScalar<'q, DB, O, ::Arguments> { /// Bind a value for use with this SQL query. /// /// See [`Query::bind`](crate::query::Query::bind). - pub fn bind + Type>(mut self, value: T) -> Self { + pub fn bind + Type>(mut self, value: T) -> Self { self.inner = self.inner.bind(value); self } @@ -81,7 +81,7 @@ impl<'q, DB, O, A> QueryScalar<'q, DB, O, A> where DB: Database, O: Send + Unpin, - A: 'q + IntoArguments<'q, DB>, + A: 'q + IntoArguments, (O,): Send + Unpin + for<'r> FromRow<'r, DB::Row>, { /// Execute the query and return the generated results as a stream. @@ -321,7 +321,7 @@ where #[inline] pub fn query_scalar<'q, DB, O>( sql: impl SqlSafeStr, -) -> QueryScalar<'q, DB, O, ::Arguments<'q>> +) -> QueryScalar<'q, DB, O, ::Arguments> where DB: Database, (O,): for<'r> FromRow<'r, DB::Row>, @@ -344,7 +344,7 @@ pub fn query_scalar_with<'q, DB, O, A>( ) -> QueryScalar<'q, DB, O, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, (O,): for<'r> FromRow<'r, DB::Row>, { query_scalar_with_result(sql, Ok(arguments)) @@ -358,7 +358,7 @@ pub fn query_scalar_with_result<'q, DB, O, A>( ) -> QueryScalar<'q, DB, O, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, (O,): for<'r> FromRow<'r, DB::Row>, { QueryScalar { @@ -369,7 +369,7 @@ where // Make a SQL query from a statement, that is mapped to a concrete value. pub fn query_statement_scalar( statement: &DB::Statement, -) -> QueryScalar<'_, DB, O, ::Arguments<'_>> +) -> QueryScalar<'_, DB, O, ::Arguments> where DB: Database, (O,): for<'r> FromRow<'r, DB::Row>, @@ -386,7 +386,7 @@ pub fn query_statement_scalar_with<'q, DB, O, A>( ) -> QueryScalar<'q, DB, O, A> where DB: Database, - A: IntoArguments<'q, DB>, + A: IntoArguments, (O,): for<'r> FromRow<'r, DB::Row>, { QueryScalar { diff --git a/sqlx-core/src/raw_sql.rs b/sqlx-core/src/raw_sql.rs index d2668ebe40..0292885422 100644 --- a/sqlx-core/src/raw_sql.rs +++ b/sqlx-core/src/raw_sql.rs @@ -120,7 +120,7 @@ pub fn raw_sql(sql: impl SqlSafeStr) -> RawSql { RawSql(sql.into_sql_str()) } -impl<'q, DB: Database> Execute<'q, DB> for RawSql { +impl Execute<'_, DB> for RawSql { fn sql(self) -> SqlStr { self.0 } @@ -129,7 +129,7 @@ impl<'q, DB: Database> Execute<'q, DB> for RawSql { None } - fn take_arguments(&mut self) -> Result::Arguments<'q>>, BoxDynError> { + fn take_arguments(&mut self) -> Result::Arguments>, BoxDynError> { Ok(None) } diff --git a/sqlx-core/src/statement.rs b/sqlx-core/src/statement.rs index 76d0325639..92ae8a629e 100644 --- a/sqlx-core/src/statement.rs +++ b/sqlx-core/src/statement.rs @@ -59,33 +59,33 @@ pub trait Statement: Send + Sync + Clone { Ok(&self.columns()[index.index(self)?]) } - fn query(&self) -> Query<'_, Self::Database, ::Arguments<'_>>; + fn query(&self) -> Query<'_, Self::Database, ::Arguments>; - fn query_with<'s, A>(&'s self, arguments: A) -> Query<'s, Self::Database, A> + fn query_with(&self, arguments: A) -> Query<'_, Self::Database, A> where - A: IntoArguments<'s, Self::Database>; + A: IntoArguments; fn query_as( &self, - ) -> QueryAs<'_, Self::Database, O, ::Arguments<'_>> + ) -> QueryAs<'_, Self::Database, O, ::Arguments> where O: for<'r> FromRow<'r, ::Row>; fn query_as_with<'s, O, A>(&'s self, arguments: A) -> QueryAs<'s, Self::Database, O, A> where O: for<'r> FromRow<'r, ::Row>, - A: IntoArguments<'s, Self::Database>; + A: IntoArguments; fn query_scalar( &self, - ) -> QueryScalar<'_, Self::Database, O, ::Arguments<'_>> + ) -> QueryScalar<'_, Self::Database, O, ::Arguments> where (O,): for<'r> FromRow<'r, ::Row>; fn query_scalar_with<'s, O, A>(&'s self, arguments: A) -> QueryScalar<'s, Self::Database, O, A> where (O,): for<'r> FromRow<'r, ::Row>, - A: IntoArguments<'s, Self::Database>; + A: IntoArguments; } #[macro_export] @@ -97,9 +97,9 @@ macro_rules! impl_statement_query { } #[inline] - fn query_with<'s, A>(&'s self, arguments: A) -> $crate::query::Query<'s, Self::Database, A> + fn query_with(&self, arguments: A) -> $crate::query::Query<'_, Self::Database, A> where - A: $crate::arguments::IntoArguments<'s, Self::Database>, + A: $crate::arguments::IntoArguments, { $crate::query::query_statement_with(self, arguments) } @@ -111,7 +111,7 @@ macro_rules! impl_statement_query { '_, Self::Database, O, - ::Arguments<'_>, + ::Arguments, > where O: for<'r> $crate::from_row::FromRow< @@ -132,7 +132,7 @@ macro_rules! impl_statement_query { 'r, ::Row, >, - A: $crate::arguments::IntoArguments<'s, Self::Database>, + A: $crate::arguments::IntoArguments, { $crate::query_as::query_statement_as_with(self, arguments) } @@ -144,7 +144,7 @@ macro_rules! impl_statement_query { '_, Self::Database, O, - ::Arguments<'_>, + ::Arguments, > where (O,): for<'r> $crate::from_row::FromRow< @@ -165,7 +165,7 @@ macro_rules! impl_statement_query { 'r, ::Row, >, - A: $crate::arguments::IntoArguments<'s, Self::Database>, + A: $crate::arguments::IntoArguments, { $crate::query_scalar::query_statement_scalar_with(self, arguments) } diff --git a/sqlx-core/src/testing/fixtures.rs b/sqlx-core/src/testing/fixtures.rs index 32fdfe2219..17f67e0fda 100644 --- a/sqlx-core/src/testing/fixtures.rs +++ b/sqlx-core/src/testing/fixtures.rs @@ -112,7 +112,7 @@ impl FixtureSnapshot { #[allow(clippy::to_string_trait_impl)] impl ToString for Fixture where - for<'a> ::Arguments<'a>: Default, + for<'a> ::Arguments: Default, { fn to_string(&self) -> String { let mut query = QueryBuilder::::new(""); diff --git a/sqlx-core/src/types/bstr.rs b/sqlx-core/src/types/bstr.rs index 4b6daadfde..3065effeb1 100644 --- a/sqlx-core/src/types/bstr.rs +++ b/sqlx-core/src/types/bstr.rs @@ -3,6 +3,7 @@ use crate::database::Database; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; +use crate::impl_into_encode; use crate::types::Type; #[doc(no_inline)] @@ -39,7 +40,7 @@ where { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { <&[u8] as Encode>::encode(self.as_bytes(), buf) } @@ -52,8 +53,10 @@ where { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { as Encode>::encode(self.as_bytes().to_vec(), buf) } } + +impl_into_encode!(BString); diff --git a/sqlx-core/src/types/json.rs b/sqlx-core/src/types/json.rs index fa187f4aab..ffb875032b 100644 --- a/sqlx-core/src/types/json.rs +++ b/sqlx-core/src/types/json.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::ops::{Deref, DerefMut}; use serde::{Deserialize, Serialize}; @@ -7,7 +8,9 @@ pub use serde_json::Value as JsonValue; use crate::database::Database; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; +use crate::encode_owned::{EncodeClone, EncodeOwned, IntoEncode}; use crate::error::BoxDynError; +use crate::impl_into_encode; use crate::types::Type; /// Json for json and jsonb fields @@ -166,12 +169,31 @@ where { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { as Encode<'q, DB>>::encode(Json(self), buf) } } +impl IntoEncode for Json +where + DB: Database, + Self: for<'e> Encode<'e, DB>, + Self: Debug + Send + Sync + 'static, + Self: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self) + } +} + impl<'r, DB> Decode<'r, DB> for JsonValue where Json: Decode<'r, DB>, @@ -207,3 +229,5 @@ where as Decode>::decode(value).map(|item| item.0) } } + +impl_into_encode!(JsonValue); diff --git a/sqlx-core/src/types/non_zero.rs b/sqlx-core/src/types/non_zero.rs index 3d92810ef9..4f04768468 100644 --- a/sqlx-core/src/types/non_zero.rs +++ b/sqlx-core/src/types/non_zero.rs @@ -33,11 +33,11 @@ macro_rules! impl_non_zero { DB: Database, $int: Encode<'q, DB>, { - fn encode_by_ref(&self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + fn encode_by_ref(&self, buf: &mut ::ArgumentBuffer) -> Result { <$int as Encode<'q, DB>>::encode_by_ref(&self.get(), buf) } - fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result + fn encode(self, buf: &mut ::ArgumentBuffer) -> Result where Self: Sized, { diff --git a/sqlx-core/src/types/text.rs b/sqlx-core/src/types/text.rs index f5e323eea8..30a9cc04d4 100644 --- a/sqlx-core/src/types/text.rs +++ b/sqlx-core/src/types/text.rs @@ -1,3 +1,8 @@ +use crate::database::Database; +use crate::encode::Encode; +use crate::encode_owned::{EncodeClone, EncodeOwned, IntoEncode}; +use crate::types::Type; +use std::fmt::Display; use std::ops::{Deref, DerefMut}; /// Map a SQL text value to/from a Rust type using [`Display`] and [`FromStr`]. @@ -115,7 +120,7 @@ where String: Encode<'q, DB>, DB: Database, { - fn encode_by_ref(&self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + fn encode_by_ref(&self, buf: &mut ::ArgumentBuffer) -> Result { self.0.to_string().encode(buf) } } @@ -132,3 +137,22 @@ where } } */ + +impl IntoEncode for Text +where + DB: Database, + T: Display, + Self: for<'e> Encode<'e, DB> + Type, + String: Encode<'static, DB> + Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, DB> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.to_string()) + } +} diff --git a/sqlx-macros-core/src/derives/encode.rs b/sqlx-macros-core/src/derives/encode.rs index 9f26aba0a9..1022a1b137 100644 --- a/sqlx-macros-core/src/derives/encode.rs +++ b/sqlx-macros-core/src/derives/encode.rs @@ -84,7 +84,7 @@ fn expand_derive_encode_transparent( { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<#lifetime>, + buf: &mut ::ArgumentBuffer, ) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> { <#ty as ::sqlx::encode::Encode<#lifetime, DB>>::encode_by_ref(&self.0, buf) } @@ -123,7 +123,7 @@ fn expand_derive_encode_weak_enum( { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> { let value = match self { #(#values)* @@ -173,7 +173,7 @@ fn expand_derive_encode_strong_enum( { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> ::std::result::Result<::sqlx::encode::IsNull, ::sqlx::error::BoxDynError> { let val = match self { #(#value_arms)* @@ -217,6 +217,9 @@ fn expand_derive_encode_struct( ]); } + let generics2 = generics.clone(); + let (impl_generics_2, _, _) = generics2.split_for_impl(); + generics.params.push(parse_quote!('q)); let (impl_generics, _, _) = generics.split_for_impl(); @@ -262,6 +265,27 @@ fn expand_derive_encode_struct( } } )); + + tts.extend(quote!( + #[automatically_derived] + impl #impl_generics_2 ::sqlx::encode_owned::IntoEncode<::sqlx::Postgres> for #ident #ty_generics + where + #ident: for<'e> ::sqlx::encode::Encode<'e, ::sqlx::Postgres>, + Self: std::fmt::Debug + Send + Sync + 'static, + Self: ::sqlx::types::Type<::sqlx::Postgres>, + { + fn into_encode<'s>(self) -> impl ::sqlx::encode::Encode<'s, ::sqlx::Postgres> + ::sqlx::types::Type<::sqlx::Postgres> + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl ::sqlx::encode_owned::EncodeOwned<::sqlx::Postgres> + 'static { + ::sqlx::encode_owned::EncodeClone::from(self) + } + } + )); } Ok(tts) diff --git a/sqlx-macros-core/src/derives/type.rs b/sqlx-macros-core/src/derives/type.rs index 8ecd55230c..037572a16c 100644 --- a/sqlx-macros-core/src/derives/type.rs +++ b/sqlx-macros-core/src/derives/type.rs @@ -95,6 +95,25 @@ fn expand_derive_has_sql_type_transparent( <#ty as ::sqlx::Type>::compatible(ty) } } + + #[automatically_derived] + impl #impl_generics ::sqlx::encode_owned::IntoEncode for #ident #ty_generics + where + #ident: for<'e> ::sqlx::encode::Encode<'e, DB>, + Self: std::fmt::Debug + Send + Sync + 'static, + Self: ::sqlx::types::Type, + { + fn into_encode<'s>(self) -> impl ::sqlx::encode::Encode<'s, DB> + ::sqlx::types::Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl ::sqlx::encode_owned::EncodeOwned + 'static { + ::sqlx::encode_owned::EncodeClone::from(self) + } + } ); if cfg!(feature = "postgres") && !attr.no_pg_array { diff --git a/sqlx-macros-core/src/query/args.rs b/sqlx-macros-core/src/query/args.rs index 1b338efa3e..701957b42e 100644 --- a/sqlx-macros-core/src/query/args.rs +++ b/sqlx-macros-core/src/query/args.rs @@ -22,7 +22,7 @@ pub fn quote_args( if input.arg_exprs.is_empty() { return Ok(quote! { - let query_args = ::core::result::Result::<_, ::sqlx::error::BoxDynError>::Ok(<#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()); }); } @@ -95,7 +95,7 @@ pub fn quote_args( #args_check - let mut query_args = <#db_path as ::sqlx::database::Database>::Arguments::<'_>::default(); + let mut query_args = <#db_path as ::sqlx::database::Database>::Arguments::default(); query_args.reserve( #args_count, 0 #(+ ::sqlx::encode::Encode::<#db_path>::size_hint(#arg_name))* diff --git a/sqlx-mysql/src/any.rs b/sqlx-mysql/src/any.rs index 028b6c55b8..548bb49a64 100644 --- a/sqlx-mysql/src/any.rs +++ b/sqlx-mysql/src/any.rs @@ -6,7 +6,7 @@ use crate::{ use either::Either; use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; -use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; +use futures_util::{stream, FutureExt, StreamExt, TryFutureExt}; use sqlx_core::any::{ Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo, AnyTypeInfoKind, @@ -17,7 +17,7 @@ use sqlx_core::describe::Describe; use sqlx_core::executor::Executor; use sqlx_core::sql_str::SqlStr; use sqlx_core::transaction::TransactionManager; -use std::{future, pin::pin}; +use std::future; sqlx_core::declare_driver_with_optional_migrate!(DRIVER = MySql); @@ -77,14 +77,14 @@ impl AnyConnectionBackend for MySqlConnection { Ok(self) } - fn fetch_many<'q>( - &'q mut self, + fn fetch_many( + &mut self, query: SqlStr, persistent: bool, - arguments: Option>, - ) -> BoxStream<'q, sqlx_core::Result>> { + arguments: Option, + ) -> BoxStream<'_, sqlx_core::Result>> { let persistent = persistent && arguments.is_some(); - let arguments = match arguments.as_ref().map(AnyArguments::convert_to).transpose() { + let arguments = match arguments.map(AnyArguments::convert_into).transpose() { Ok(arguments) => arguments, Err(error) => { return stream::once(future::ready(Err(sqlx_core::Error::Encode(error)))).boxed() @@ -103,33 +103,6 @@ impl AnyConnectionBackend for MySqlConnection { ) } - fn fetch_optional<'q>( - &'q mut self, - query: SqlStr, - persistent: bool, - arguments: Option>, - ) -> BoxFuture<'q, sqlx_core::Result>> { - let persistent = persistent && arguments.is_some(); - let arguments = arguments - .as_ref() - .map(AnyArguments::convert_to) - .transpose() - .map_err(sqlx_core::Error::Encode); - - Box::pin(async move { - let arguments = arguments?; - let mut stream = pin!(self.run(query, arguments, persistent).await?); - - while let Some(result) = stream.try_next().await? { - if let Either::Right(row) = result { - return Ok(Some(AnyRow::try_from(&row)?)); - } - } - - Ok(None) - }) - } - fn prepare_with<'c, 'q: 'c>( &'c mut self, sql: SqlStr, diff --git a/sqlx-mysql/src/arguments.rs b/sqlx-mysql/src/arguments.rs index 464529cba2..af2c0216cb 100644 --- a/sqlx-mysql/src/arguments.rs +++ b/sqlx-mysql/src/arguments.rs @@ -1,9 +1,11 @@ +use std::ops::Deref; + use crate::encode::{Encode, IsNull}; use crate::types::Type; use crate::{MySql, MySqlTypeInfo}; pub(crate) use sqlx_core::arguments::*; +use sqlx_core::encode_owned::IntoEncode; use sqlx_core::error::BoxDynError; -use std::ops::Deref; /// Implementation of [`Arguments`] for MySQL. #[derive(Debug, Default, Clone)] @@ -37,7 +39,7 @@ impl MySqlArguments { } } -impl<'q> Arguments<'q> for MySqlArguments { +impl Arguments for MySqlArguments { type Database = MySql; fn reserve(&mut self, len: usize, size: usize) { @@ -47,9 +49,9 @@ impl<'q> Arguments<'q> for MySqlArguments { fn add(&mut self, value: T) -> Result<(), BoxDynError> where - T: Encode<'q, Self::Database> + Type, + T: IntoEncode + Type, { - self.add(value) + MySqlArguments::add(self, value.into_encode()) } fn len(&self) -> usize { diff --git a/sqlx-mysql/src/database.rs b/sqlx-mysql/src/database.rs index 0e3f51f532..849ddbde61 100644 --- a/sqlx-mysql/src/database.rs +++ b/sqlx-mysql/src/database.rs @@ -25,8 +25,8 @@ impl Database for MySql { type Value = MySqlValue; type ValueRef<'r> = MySqlValueRef<'r>; - type Arguments<'q> = MySqlArguments; - type ArgumentBuffer<'q> = Vec; + type Arguments = MySqlArguments; + type ArgumentBuffer = Vec; type Statement = MySqlStatement; diff --git a/sqlx-mysql/src/migrate.rs b/sqlx-mysql/src/migrate.rs index 0176f93c26..93f3cbe8e5 100644 --- a/sqlx-mysql/src/migrate.rs +++ b/sqlx-mysql/src/migrate.rs @@ -215,8 +215,8 @@ CREATE TABLE IF NOT EXISTS {table_name} ( "# ))) .bind(migration.version) - .bind(&*migration.description) - .bind(&*migration.checksum) + .bind(&migration.description) + .bind(&migration.checksum) .execute(&mut *tx) .await?; diff --git a/sqlx-mysql/src/testing/mod.rs b/sqlx-mysql/src/testing/mod.rs index f509f9da45..ce2874d22b 100644 --- a/sqlx-mysql/src/testing/mod.rs +++ b/sqlx-mysql/src/testing/mod.rs @@ -53,8 +53,10 @@ impl TestSupport for MySql { let mut builder = QueryBuilder::new("drop database if exists "); - for db_name in &delete_db_names { - builder.push(db_name); + let delete_db_names_len = delete_db_names.len(); + + for db_name in delete_db_names { + builder.push(&db_name); match builder.build().execute(&mut conn).await { Ok(_deleted) => { @@ -86,7 +88,7 @@ impl TestSupport for MySql { query.push(")").build().execute(&mut conn).await?; let _ = conn.close().await; - Ok(Some(delete_db_names.len())) + Ok(Some(delete_db_names_len)) } async fn snapshot(_conn: &mut Self::Connection) -> Result, Error> { diff --git a/sqlx-mysql/src/types/bigdecimal.rs b/sqlx-mysql/src/types/bigdecimal.rs index 11bca04806..95eb242bd0 100644 --- a/sqlx-mysql/src/types/bigdecimal.rs +++ b/sqlx-mysql/src/types/bigdecimal.rs @@ -26,6 +26,8 @@ impl Encode<'_, MySql> for BigDecimal { } } +impl_into_encode_for_db!(MySql, BigDecimal); + impl Decode<'_, MySql> for BigDecimal { fn decode(value: MySqlValueRef<'_>) -> Result { Ok(value.as_str()?.parse()?) diff --git a/sqlx-mysql/src/types/chrono.rs b/sqlx-mysql/src/types/chrono.rs index 39e215bec5..4e9a7a09f8 100644 --- a/sqlx-mysql/src/types/chrono.rs +++ b/sqlx-mysql/src/types/chrono.rs @@ -29,6 +29,8 @@ impl Encode<'_, MySql> for DateTime { } } +impl_into_encode_for_db!(MySql, DateTime); + /// Note: assumes the connection's `time_zone` is set to `+00:00` (UTC). impl<'r> Decode<'r, MySql> for DateTime { fn decode(value: MySqlValueRef<'r>) -> Result { @@ -90,6 +92,8 @@ impl Encode<'_, MySql> for NaiveTime { } } +impl_into_encode_for_db!(MySql, NaiveTime); + /// Decode from a `TIME` value. /// /// ### Errors @@ -181,6 +185,8 @@ impl Encode<'_, MySql> for NaiveDate { } } +impl_into_encode_for_db!(MySql, NaiveDate); + impl<'r> Decode<'r, MySql> for NaiveDate { fn decode(value: MySqlValueRef<'r>) -> Result { match value.format() { @@ -228,6 +234,8 @@ impl Encode<'_, MySql> for NaiveDateTime { } } +impl_into_encode_for_db!(MySql, NaiveDateTime); + impl<'r> Decode<'r, MySql> for NaiveDateTime { fn decode(value: MySqlValueRef<'r>) -> Result { match value.format() { diff --git a/sqlx-mysql/src/types/mysql_time.rs b/sqlx-mysql/src/types/mysql_time.rs index b549af5765..bbe08a62bd 100644 --- a/sqlx-mysql/src/types/mysql_time.rs +++ b/sqlx-mysql/src/types/mysql_time.rs @@ -409,10 +409,10 @@ impl<'r> Decode<'r, MySql> for MySqlTime { } } -impl<'q> Encode<'q, MySql> for MySqlTime { +impl Encode<'_, MySql> for MySqlTime { fn encode_by_ref( &self, - buf: &mut ::ArgumentBuffer<'q>, + buf: &mut ::ArgumentBuffer, ) -> Result { if self.is_zero() { buf.put_u8(0); @@ -449,6 +449,8 @@ impl<'q> Encode<'q, MySql> for MySqlTime { } } +impl_into_encode_for_db!(MySql, MySqlTime); + /// Convert [`MySqlTime`] from [`std::time::Duration`]. /// /// ### Note: Precision Truncation diff --git a/sqlx-mysql/src/types/rust_decimal.rs b/sqlx-mysql/src/types/rust_decimal.rs index 6e78243c7a..175e5f7991 100644 --- a/sqlx-mysql/src/types/rust_decimal.rs +++ b/sqlx-mysql/src/types/rust_decimal.rs @@ -26,6 +26,8 @@ impl Encode<'_, MySql> for Decimal { } } +impl_into_encode_for_db!(MySql, Decimal); + impl Decode<'_, MySql> for Decimal { fn decode(value: MySqlValueRef<'_>) -> Result { Ok(value.as_str()?.parse()?) diff --git a/sqlx-mysql/src/types/time.rs b/sqlx-mysql/src/types/time.rs index e04f8928c9..9872b05360 100644 --- a/sqlx-mysql/src/types/time.rs +++ b/sqlx-mysql/src/types/time.rs @@ -31,6 +31,8 @@ impl Encode<'_, MySql> for OffsetDateTime { } } +impl_into_encode_for_db!(MySql, OffsetDateTime); + impl<'r> Decode<'r, MySql> for OffsetDateTime { fn decode(value: MySqlValueRef<'r>) -> Result { let primitive: PrimitiveDateTime = Decode::::decode(value)?; @@ -67,6 +69,8 @@ impl Encode<'_, MySql> for Time { } } +impl_into_encode_for_db!(MySql, Time); + impl<'r> Decode<'r, MySql> for Time { fn decode(value: MySqlValueRef<'r>) -> Result { match value.format() { @@ -159,6 +163,8 @@ impl Encode<'_, MySql> for Date { } } +impl_into_encode_for_db!(MySql, Date); + impl<'r> Decode<'r, MySql> for Date { fn decode(value: MySqlValueRef<'r>) -> Result { match value.format() { @@ -186,6 +192,8 @@ impl Type for PrimitiveDateTime { } } +impl_into_encode_for_db!(MySql, PrimitiveDateTime); + impl Encode<'_, MySql> for PrimitiveDateTime { fn encode_by_ref(&self, buf: &mut Vec) -> Result { let len = primitive_dt_encoded_len(self); diff --git a/sqlx-mysql/src/types/uuid.rs b/sqlx-mysql/src/types/uuid.rs index 8bd4d37f48..f6f0925d53 100644 --- a/sqlx-mysql/src/types/uuid.rs +++ b/sqlx-mysql/src/types/uuid.rs @@ -106,3 +106,6 @@ impl Decode<'_, MySql> for Simple { .map(|u| u.simple()) } } + +impl_into_encode_for_db!(MySql, Hyphenated); +impl_into_encode_for_db!(MySql, Simple); diff --git a/sqlx-postgres/src/any.rs b/sqlx-postgres/src/any.rs index 75ee0d73df..d0542ca47d 100644 --- a/sqlx-postgres/src/any.rs +++ b/sqlx-postgres/src/any.rs @@ -4,9 +4,9 @@ use crate::{ }; use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; -use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; +use futures_util::{stream, FutureExt, StreamExt, TryFutureExt}; use sqlx_core::sql_str::SqlStr; -use std::{future, pin::pin}; +use std::future; use sqlx_core::any::{ Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, @@ -79,14 +79,14 @@ impl AnyConnectionBackend for PgConnection { Ok(self) } - fn fetch_many<'q>( - &'q mut self, + fn fetch_many( + &mut self, query: SqlStr, persistent: bool, - arguments: Option>, - ) -> BoxStream<'q, sqlx_core::Result>> { + arguments: Option, + ) -> BoxStream>> { let persistent = persistent && arguments.is_some(); - let arguments = match arguments.as_ref().map(AnyArguments::convert_to).transpose() { + let arguments = match arguments.map(AnyArguments::convert_into).transpose() { Ok(arguments) => arguments, Err(error) => { return stream::once(future::ready(Err(sqlx_core::Error::Encode(error)))).boxed() @@ -105,31 +105,6 @@ impl AnyConnectionBackend for PgConnection { ) } - fn fetch_optional<'q>( - &'q mut self, - query: SqlStr, - persistent: bool, - arguments: Option>, - ) -> BoxFuture<'q, sqlx_core::Result>> { - let persistent = persistent && arguments.is_some(); - let arguments = arguments - .as_ref() - .map(AnyArguments::convert_to) - .transpose() - .map_err(sqlx_core::Error::Encode); - - Box::pin(async move { - let arguments = arguments?; - let mut stream = pin!(self.run(query, arguments, persistent, None).await?); - - if let Some(Either::Right(row)) = stream.try_next().await? { - return Ok(Some(AnyRow::try_from(&row)?)); - } - - Ok(None) - }) - } - fn prepare_with<'c, 'q: 'c>( &'c mut self, sql: SqlStr, diff --git a/sqlx-postgres/src/arguments.rs b/sqlx-postgres/src/arguments.rs index 62a227e52d..901f768b3d 100644 --- a/sqlx-postgres/src/arguments.rs +++ b/sqlx-postgres/src/arguments.rs @@ -1,15 +1,15 @@ -use std::fmt::{self, Write}; -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - use crate::encode::{Encode, IsNull}; use crate::error::Error; use crate::ext::ustr::UStr; use crate::types::Type; use crate::{PgConnection, PgTypeInfo, Postgres}; +use std::fmt::{self, Write}; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; use crate::type_info::PgArrayOf; pub(crate) use sqlx_core::arguments::Arguments; +use sqlx_core::encode_owned::IntoEncode; use sqlx_core::error::BoxDynError; // TODO: buf.patch(|| ...) is a poor name, can we think of a better name? Maybe `buf.lazy(||)` ? @@ -138,7 +138,7 @@ impl PgArguments { } } -impl<'q> Arguments<'q> for PgArguments { +impl Arguments for PgArguments { type Database = Postgres; fn reserve(&mut self, additional: usize, size: usize) { @@ -146,11 +146,11 @@ impl<'q> Arguments<'q> for PgArguments { self.buffer.reserve(size); } - fn add(&mut self, value: T) -> Result<(), BoxDynError> + fn add<'t, T>(&mut self, value: T) -> Result<(), BoxDynError> where - T: Encode<'q, Self::Database> + Type, + T: IntoEncode + Type, { - self.add(value) + self.add(value.into_encode()) } fn format_placeholder(&self, writer: &mut W) -> fmt::Result { diff --git a/sqlx-postgres/src/bind_iter.rs b/sqlx-postgres/src/bind_iter.rs index 0f44f19e3d..00237056c1 100644 --- a/sqlx-postgres/src/bind_iter.rs +++ b/sqlx-postgres/src/bind_iter.rs @@ -1,14 +1,17 @@ use crate::{type_info::PgType, PgArgumentBuffer, PgHasArrayType, PgTypeInfo, Postgres}; -use core::cell::Cell; +use sqlx_core::encode_owned::{EncodeClone, EncodeOwned, IntoEncode}; use sqlx_core::{ database::Database, encode::{Encode, IsNull}, error::BoxDynError, types::Type, }; +use std::fmt::Debug; +use std::sync::RwLock; // not exported but pub because it is used in the extension trait -pub struct PgBindIter(Cell>); +#[derive(Debug)] +pub struct PgBindIter(RwLock>); /// Iterator extension trait enabling iterators to encode arrays in Postgres. /// @@ -58,7 +61,7 @@ pub trait PgBindIterExt: Iterator + Sized { impl PgBindIterExt for I { fn bind_iter(self) -> PgBindIter { - PgBindIter(Cell::new(Some(self))) + PgBindIter(RwLock::new(Some(self))) } } @@ -75,6 +78,32 @@ where } } +impl PgBindIter +where + I: Iterator, + I::Item: ToOwned, + ::Owned: Debug + 'static, +{ + fn into_owned( + self, + ) -> PgBindIter::Item as ToOwned>::Owned> + Debug + 'static> + { + let taken = self.0.write().unwrap().take(); + drop(self); + + let items = match taken { + None => None, + Some(it) => { + let items = it.map(|i| i.to_owned()).collect::>().into_iter(); + + Some(items) + } + }; + + PgBindIter(RwLock::new(items)) + } +} + impl<'q, I> PgBindIter where I: Iterator, @@ -132,6 +161,16 @@ where Ok(IsNull::No) } + + fn take_inner_once(&self) -> Result { + let mut guard = self.0.write().map_err(|_| "Failed to lock PgBindIter")?; + let taken = guard.take(); + drop(guard); + + let value = taken.ok_or("PgBindIter is only used once")?; + + Ok(value) + } } impl<'q, I> Encode<'q, Postgres> for PgBindIter @@ -140,15 +179,32 @@ where ::Item: Type + Encode<'q, Postgres>, { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { - Self::encode_inner(self.0.take().expect("PgBindIter is only used once"), buf) + let taken = self.take_inner_once()?; + + Self::encode_inner(taken, buf) } - fn encode(self, buf: &mut PgArgumentBuffer) -> Result +} + +impl IntoEncode for PgBindIter +where + Self: Type, + I: Iterator, + I::Item: ToOwned, + ::Owned: Debug + Send + Sync + Type + 'static, + for<'e> ::Item: Type + Encode<'e, Postgres>, + ::Owned: Encode<'static, Postgres>, + <::Item as ToOwned>::Owned: PgHasArrayType, +{ + fn into_encode<'s>(self) -> impl Encode<'s, Postgres> + Type + 's where - Self: Sized, + Self: 's, { - Self::encode_inner( - self.0.into_inner().expect("PgBindIter is only used once"), - buf, - ) + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + let owned = self.into_owned(); + + EncodeClone::from(owned) } } diff --git a/sqlx-postgres/src/connection/executor.rs b/sqlx-postgres/src/connection/executor.rs index ba4cffa647..0a223904cf 100644 --- a/sqlx-postgres/src/connection/executor.rs +++ b/sqlx-postgres/src/connection/executor.rs @@ -404,8 +404,6 @@ impl<'c> Executor<'c> for &'c mut PgConnection { 'q: 'e, E: 'q, { - // False positive: https://github.com/rust-lang/rust-clippy/issues/12560 - #[allow(clippy::map_clone)] let metadata = query.statement().map(|s| Arc::clone(&s.metadata)); let arguments = query.take_arguments().map_err(Error::Encode); let persistent = query.persistent(); diff --git a/sqlx-postgres/src/database.rs b/sqlx-postgres/src/database.rs index fbc762615b..77945b8cfe 100644 --- a/sqlx-postgres/src/database.rs +++ b/sqlx-postgres/src/database.rs @@ -27,8 +27,8 @@ impl Database for Postgres { type Value = PgValue; type ValueRef<'r> = PgValueRef<'r>; - type Arguments<'q> = PgArguments; - type ArgumentBuffer<'q> = PgArgumentBuffer; + type Arguments = PgArguments; + type ArgumentBuffer = PgArgumentBuffer; type Statement = PgStatement; diff --git a/sqlx-postgres/src/migrate.rs b/sqlx-postgres/src/migrate.rs index b96c021be2..2e6fd58e23 100644 --- a/sqlx-postgres/src/migrate.rs +++ b/sqlx-postgres/src/migrate.rs @@ -308,8 +308,8 @@ async fn execute_migration( "# ))) .bind(migration.version) - .bind(&*migration.description) - .bind(&*migration.checksum) + .bind(&migration.description) + .bind(&migration.checksum) .execute(conn) .await?; diff --git a/sqlx-postgres/src/testing/mod.rs b/sqlx-postgres/src/testing/mod.rs index 3e1cf0ddf7..7edf7af4ab 100644 --- a/sqlx-postgres/src/testing/mod.rs +++ b/sqlx-postgres/src/testing/mod.rs @@ -55,8 +55,10 @@ impl TestSupport for Postgres { let mut builder = QueryBuilder::new("drop database if exists "); - for db_name in &delete_db_names { - builder.push(db_name); + let delete_db_names_len = delete_db_names.len(); + + for db_name in delete_db_names { + builder.push(&db_name); match builder.build().execute(&mut conn).await { Ok(_deleted) => { @@ -79,7 +81,7 @@ impl TestSupport for Postgres { .await?; let _ = conn.close().await; - Ok(Some(delete_db_names.len())) + Ok(Some(delete_db_names_len)) } async fn snapshot(_conn: &mut Self::Connection) -> Result, Error> { diff --git a/sqlx-postgres/src/types/array.rs b/sqlx-postgres/src/types/array.rs index 372c2891a8..c824e00f22 100644 --- a/sqlx-postgres/src/types/array.rs +++ b/sqlx-postgres/src/types/array.rs @@ -162,6 +162,7 @@ where self.len() ) })?; + crate::PgBindIterExt::bind_iter(self.iter()).encode(buf) } } diff --git a/sqlx-postgres/src/types/bigdecimal.rs b/sqlx-postgres/src/types/bigdecimal.rs index cb9ce0a948..1b15672977 100644 --- a/sqlx-postgres/src/types/bigdecimal.rs +++ b/sqlx-postgres/src/types/bigdecimal.rs @@ -193,6 +193,8 @@ impl Encode<'_, Postgres> for BigDecimal { } } +impl_into_encode_for_db!(Postgres, BigDecimal); + /// ### Note: `NaN` /// `BigDecimal` has a greater range than `NUMERIC` (see the corresponding `Encode` impl for details) /// but cannot represent `NaN`, so decoding may return an error. diff --git a/sqlx-postgres/src/types/bit_vec.rs b/sqlx-postgres/src/types/bit_vec.rs index 92eb47b210..56c620454b 100644 --- a/sqlx-postgres/src/types/bit_vec.rs +++ b/sqlx-postgres/src/types/bit_vec.rs @@ -45,6 +45,8 @@ impl Encode<'_, Postgres> for BitVec { } } +impl_into_encode_for_db!(Postgres, BitVec); + impl Decode<'_, Postgres> for BitVec { fn decode(value: PgValueRef<'_>) -> Result { match value.format() { diff --git a/sqlx-postgres/src/types/chrono/date.rs b/sqlx-postgres/src/types/chrono/date.rs index 0327d5c45d..9f5715589f 100644 --- a/sqlx-postgres/src/types/chrono/date.rs +++ b/sqlx-postgres/src/types/chrono/date.rs @@ -38,6 +38,8 @@ impl Encode<'_, Postgres> for NaiveDate { } } +impl_into_encode_for_db!(Postgres, NaiveDate); + impl<'r> Decode<'r, Postgres> for NaiveDate { fn decode(value: PgValueRef<'r>) -> Result { Ok(match value.format() { diff --git a/sqlx-postgres/src/types/chrono/datetime.rs b/sqlx-postgres/src/types/chrono/datetime.rs index 77f900d42a..71f592e9a7 100644 --- a/sqlx-postgres/src/types/chrono/datetime.rs +++ b/sqlx-postgres/src/types/chrono/datetime.rs @@ -6,6 +6,9 @@ use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValue use chrono::{ DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, Offset, TimeZone, Utc, }; +use sqlx_core::encode_owned::EncodeClone; +use sqlx_core::encode_owned::EncodeOwned; +use sqlx_core::encode_owned::IntoEncode; use std::mem; impl Type for NaiveDateTime { @@ -47,6 +50,8 @@ impl Encode<'_, Postgres> for NaiveDateTime { } } +impl_into_encode_for_db!(Postgres, NaiveDateTime); + impl<'r> Decode<'r, Postgres> for NaiveDateTime { fn decode(value: PgValueRef<'r>) -> Result { Ok(match value.format() { @@ -84,6 +89,24 @@ impl Encode<'_, Postgres> for DateTime { } } +impl IntoEncode for DateTime +where + Tz: TimeZone, + for<'e> Self: Encode<'e, Postgres>, + Self: Send + Sync + 'static, +{ + fn into_encode<'s>(self) -> impl Encode<'s, Postgres> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self) + } +} + impl<'r> Decode<'r, Postgres> for DateTime { fn decode(value: PgValueRef<'r>) -> Result { let fixed = as Decode>::decode(value)?; diff --git a/sqlx-postgres/src/types/chrono/time.rs b/sqlx-postgres/src/types/chrono/time.rs index ca66f389fd..239e604d86 100644 --- a/sqlx-postgres/src/types/chrono/time.rs +++ b/sqlx-postgres/src/types/chrono/time.rs @@ -33,6 +33,8 @@ impl Encode<'_, Postgres> for NaiveTime { } } +impl_into_encode_for_db!(Postgres, NaiveTime); + impl<'r> Decode<'r, Postgres> for NaiveTime { fn decode(value: PgValueRef<'r>) -> Result { Ok(match value.format() { diff --git a/sqlx-postgres/src/types/citext.rs b/sqlx-postgres/src/types/citext.rs index c0316ac825..860f1129dc 100644 --- a/sqlx-postgres/src/types/citext.rs +++ b/sqlx-postgres/src/types/citext.rs @@ -104,3 +104,5 @@ impl Decode<'_, Postgres> for PgCiText { Ok(PgCiText(value.as_str()?.to_owned())) } } + +impl_into_encode_for_db!(Postgres, PgCiText); diff --git a/sqlx-postgres/src/types/hstore.rs b/sqlx-postgres/src/types/hstore.rs index a03970fb30..130fb55380 100644 --- a/sqlx-postgres/src/types/hstore.rs +++ b/sqlx-postgres/src/types/hstore.rs @@ -219,6 +219,8 @@ impl Encode<'_, Postgres> for PgHstore { } } +impl_into_encode_for_db!(Postgres, PgHstore); + fn read_length(buf: &mut &[u8]) -> Result { if buf.len() < mem::size_of::() { return Err(format!( diff --git a/sqlx-postgres/src/types/interval.rs b/sqlx-postgres/src/types/interval.rs index af4810323d..99dd9d9011 100644 --- a/sqlx-postgres/src/types/interval.rs +++ b/sqlx-postgres/src/types/interval.rs @@ -67,6 +67,8 @@ impl Encode<'_, Postgres> for PgInterval { } } +impl_into_encode_for_db!(Postgres, PgInterval); + // We then implement Encode + Type for std Duration, chrono Duration, and time Duration // This is to enable ease-of-use for encoding when its simple diff --git a/sqlx-postgres/src/types/ipnet/ipnet.rs b/sqlx-postgres/src/types/ipnet/ipnet.rs index 1f986174b8..3f41f4eb34 100644 --- a/sqlx-postgres/src/types/ipnet/ipnet.rs +++ b/sqlx-postgres/src/types/ipnet/ipnet.rs @@ -70,6 +70,8 @@ impl Encode<'_, Postgres> for IpNet { } } +impl_into_encode_for_db!(Postgres, IpNet); + impl Decode<'_, Postgres> for IpNet { fn decode(value: PgValueRef<'_>) -> Result { let bytes = match value.format() { diff --git a/sqlx-postgres/src/types/ipnetwork/ipnetwork.rs b/sqlx-postgres/src/types/ipnetwork/ipnetwork.rs index 4f619ba99c..6a2d2909f7 100644 --- a/sqlx-postgres/src/types/ipnetwork/ipnetwork.rs +++ b/sqlx-postgres/src/types/ipnetwork/ipnetwork.rs @@ -69,6 +69,8 @@ impl Encode<'_, Postgres> for IpNetwork { } } +impl_into_encode_for_db!(Postgres, IpNetwork); + impl Decode<'_, Postgres> for IpNetwork { fn decode(value: PgValueRef<'_>) -> Result { let bytes = match value.format() { diff --git a/sqlx-postgres/src/types/mac_address.rs b/sqlx-postgres/src/types/mac_address.rs index 23766e7000..a1a7761662 100644 --- a/sqlx-postgres/src/types/mac_address.rs +++ b/sqlx-postgres/src/types/mac_address.rs @@ -33,6 +33,8 @@ impl Encode<'_, Postgres> for MacAddress { } } +impl_into_encode_for_db!(Postgres, MacAddress); + impl Decode<'_, Postgres> for MacAddress { fn decode(value: PgValueRef<'_>) -> Result { let bytes = match value.format() { diff --git a/sqlx-postgres/src/types/money.rs b/sqlx-postgres/src/types/money.rs index 52fc68795a..98c5cd527c 100644 --- a/sqlx-postgres/src/types/money.rs +++ b/sqlx-postgres/src/types/money.rs @@ -172,6 +172,8 @@ impl Encode<'_, Postgres> for PgMoney { } } +impl_into_encode_for_db!(Postgres, PgMoney); + impl Decode<'_, Postgres> for PgMoney { fn decode(value: PgValueRef<'_>) -> Result { match value.format() { diff --git a/sqlx-postgres/src/types/oid.rs b/sqlx-postgres/src/types/oid.rs index 04c5ef837a..ed6077fa92 100644 --- a/sqlx-postgres/src/types/oid.rs +++ b/sqlx-postgres/src/types/oid.rs @@ -63,3 +63,5 @@ impl<'de> Deserialize<'de> for Oid { u32::deserialize(deserializer).map(Self) } } + +impl_into_encode!(Oid); diff --git a/sqlx-postgres/src/types/range.rs b/sqlx-postgres/src/types/range.rs index 0d9c14bdfd..cedec02581 100644 --- a/sqlx-postgres/src/types/range.rs +++ b/sqlx-postgres/src/types/range.rs @@ -1,15 +1,15 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; -use bitflags::bitflags; -use sqlx_core::bytes::Buf; - use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::type_info::PgTypeKind; use crate::types::Type; use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres}; +use bitflags::bitflags; +use sqlx_core::bytes::Buf; +use sqlx_core::encode_owned::{EncodeClone, EncodeOwned, IntoEncode}; // https://github.com/postgres/postgres/blob/2f48ede080f42b97b594fb14102c82ca1001b80c/src/include/utils/rangetypes.h#L35-L44 bitflags! { @@ -324,6 +324,26 @@ where } } +impl IntoEncode for PgRange +where + T: for<'e> Encode<'e, Postgres>, + PgRange: Clone, + PgRange: for<'e> Encode<'e, Postgres>, + T: Debug + Send + Sync + 'static, + Self: Type, +{ + fn into_encode<'s>(self) -> impl Encode<'s, Postgres> + Type + 's + where + Self: 's, + { + self + } + + fn into_encode_owned(self) -> impl EncodeOwned + 'static { + EncodeClone::from(self.clone()) + } +} + impl<'r, T> Decode<'r, Postgres> for PgRange where T: Type + for<'a> Decode<'a, Postgres>, diff --git a/sqlx-postgres/src/types/rust_decimal.rs b/sqlx-postgres/src/types/rust_decimal.rs index d679ae9151..06d50f17bd 100644 --- a/sqlx-postgres/src/types/rust_decimal.rs +++ b/sqlx-postgres/src/types/rust_decimal.rs @@ -179,6 +179,8 @@ impl Encode<'_, Postgres> for Decimal { } } +impl_into_encode_for_db!(Postgres, Decimal); + #[doc=include_str!("rust_decimal-range.md")] impl Decode<'_, Postgres> for Decimal { fn decode(value: PgValueRef<'_>) -> Result { diff --git a/sqlx-postgres/src/types/time/date.rs b/sqlx-postgres/src/types/time/date.rs index 2afa57ee0d..2d2944e511 100644 --- a/sqlx-postgres/src/types/time/date.rs +++ b/sqlx-postgres/src/types/time/date.rs @@ -34,6 +34,8 @@ impl Encode<'_, Postgres> for Date { } } +impl_into_encode_for_db!(Postgres, Date); + impl<'r> Decode<'r, Postgres> for Date { fn decode(value: PgValueRef<'r>) -> Result { Ok(match value.format() { diff --git a/sqlx-postgres/src/types/time/datetime.rs b/sqlx-postgres/src/types/time/datetime.rs index 3484116bd1..b6917f4696 100644 --- a/sqlx-postgres/src/types/time/datetime.rs +++ b/sqlx-postgres/src/types/time/datetime.rs @@ -51,6 +51,8 @@ impl Encode<'_, Postgres> for PrimitiveDateTime { } } +impl_into_encode_for_db!(Postgres, PrimitiveDateTime); + impl<'r> Decode<'r, Postgres> for PrimitiveDateTime { fn decode(value: PgValueRef<'r>) -> Result { Ok(match value.format() { @@ -101,6 +103,8 @@ impl Encode<'_, Postgres> for OffsetDateTime { } } +impl_into_encode_for_db!(Postgres, OffsetDateTime); + impl<'r> Decode<'r, Postgres> for OffsetDateTime { fn decode(value: PgValueRef<'r>) -> Result { Ok(>::decode(value)?.assume_utc()) diff --git a/sqlx-postgres/src/types/time/time.rs b/sqlx-postgres/src/types/time/time.rs index 635170d14b..7f7376ac1a 100644 --- a/sqlx-postgres/src/types/time/time.rs +++ b/sqlx-postgres/src/types/time/time.rs @@ -34,6 +34,8 @@ impl Encode<'_, Postgres> for Time { } } +impl_into_encode_for_db!(Postgres, Time); + impl<'r> Decode<'r, Postgres> for Time { fn decode(value: PgValueRef<'r>) -> Result { Ok(match value.format() { diff --git a/sqlx-postgres/src/types/time_tz.rs b/sqlx-postgres/src/types/time_tz.rs index e3de79ea5c..751a03b8db 100644 --- a/sqlx-postgres/src/types/time_tz.rs +++ b/sqlx-postgres/src/types/time_tz.rs @@ -64,6 +64,8 @@ mod chrono { } } + impl_into_encode_for_db!(Postgres, PgTimeTz); + impl<'r> Decode<'r, Postgres> for PgTimeTz { fn decode(value: PgValueRef<'r>) -> Result { match value.format() { @@ -146,6 +148,8 @@ mod time { } } + impl_into_encode_for_db!(Postgres, PgTimeTz); + impl<'r> Decode<'r, Postgres> for PgTimeTz { fn decode(value: PgValueRef<'r>) -> Result { match value.format() { diff --git a/sqlx-sqlite/src/any.rs b/sqlx-sqlite/src/any.rs index d33677c64e..1f7063398a 100644 --- a/sqlx-sqlite/src/any.rs +++ b/sqlx-sqlite/src/any.rs @@ -12,13 +12,14 @@ use sqlx_core::any::{ }; use sqlx_core::sql_str::SqlStr; +use crate::arguments::SqliteArgumentsBuffer; use crate::type_info::DataType; use sqlx_core::connection::{ConnectOptions, Connection}; use sqlx_core::database::Database; use sqlx_core::describe::Describe; use sqlx_core::executor::Executor; use sqlx_core::transaction::TransactionManager; -use std::pin::pin; +use std::sync::Arc; sqlx_core::declare_driver_with_optional_migrate!(DRIVER = Sqlite); @@ -78,48 +79,28 @@ impl AnyConnectionBackend for SqliteConnection { Ok(self) } - fn fetch_many<'q>( - &'q mut self, + fn fetch_many( + &mut self, query: SqlStr, persistent: bool, - arguments: Option>, - ) -> BoxStream<'q, sqlx_core::Result>> { - let persistent = persistent && arguments.is_some(); - let args = arguments.map(map_arguments); - - Box::pin( - self.worker - .execute(query, args, self.row_channel_size, persistent, None) - .map_ok(flume::Receiver::into_stream) - .try_flatten_stream() - .map( - move |res: sqlx_core::Result>| match res? { - Either::Left(result) => Ok(Either::Left(map_result(result))), - Either::Right(row) => Ok(Either::Right(AnyRow::try_from(&row)?)), - }, - ), - ) + arguments: Option, + ) -> BoxStream<'_, sqlx_core::Result>> { + self.fetch_with_limit(query, persistent, arguments, None) } - fn fetch_optional<'q>( - &'q mut self, + fn fetch_optional( + &mut self, query: SqlStr, persistent: bool, - arguments: Option>, - ) -> BoxFuture<'q, sqlx_core::Result>> { - let persistent = persistent && arguments.is_some(); - let args = arguments.map(map_arguments); + arguments: Option, + ) -> BoxFuture<'_, sqlx_core::Result>> { + let mut stream = self.fetch_with_limit(query, persistent, arguments, Some(1)); Box::pin(async move { - let mut stream = pin!( - self.worker - .execute(query, args, self.row_channel_size, persistent, Some(1)) - .map_ok(flume::Receiver::into_stream) - .await? - ); - - if let Some(Either::Right(row)) = stream.try_next().await? { - return Ok(Some(AnyRow::try_from(&row)?)); + while let Some(result) = stream.try_next().await? { + if let Either::Right(row) = result { + return Ok(Some(row)); + } } Ok(None) @@ -143,6 +124,32 @@ impl AnyConnectionBackend for SqliteConnection { } } +impl SqliteConnection { + fn fetch_with_limit( + &mut self, + query: SqlStr, + persistent: bool, + arguments: Option, + limit: Option, + ) -> BoxStream<'_, sqlx_core::Result>> { + let persistent = persistent && arguments.is_some(); + let args = arguments.map(map_arguments); + + Box::pin( + self.worker + .execute(query, args, self.row_channel_size, persistent, limit) + .map_ok(flume::Receiver::into_stream) + .try_flatten_stream() + .map( + move |res: sqlx_core::Result>| match res? { + Either::Left(result) => Ok(Either::Left(map_result(result))), + Either::Right(row) => Ok(Either::Right(AnyRow::try_from(&row)?)), + }, + ), + ) + } +} + impl<'a> TryFrom<&'a SqliteTypeInfo> for AnyTypeInfo { type Error = sqlx_core::Error; @@ -203,27 +210,29 @@ impl<'a> TryFrom<&'a AnyConnectOptions> for SqliteConnectOptions { } } -/// Instead of `AnyArguments::convert_into()`, we can do a direct mapping and preserve the lifetime. -fn map_arguments(args: AnyArguments<'_>) -> SqliteArguments<'_> { +// Infallible alternative to AnyArguments::convert_into() +fn map_arguments(args: AnyArguments) -> SqliteArguments { + let values = args + .values + .0 + .into_iter() + .map(|val| match val { + AnyValueKind::Null(_) => SqliteArgumentValue::Null, + AnyValueKind::Bool(b) => SqliteArgumentValue::Int(b as i32), + AnyValueKind::SmallInt(i) => SqliteArgumentValue::Int(i as i32), + AnyValueKind::Integer(i) => SqliteArgumentValue::Int(i), + AnyValueKind::BigInt(i) => SqliteArgumentValue::Int64(i), + AnyValueKind::Real(r) => SqliteArgumentValue::Double(r as f64), + AnyValueKind::Double(d) => SqliteArgumentValue::Double(d), + AnyValueKind::Text(t) => SqliteArgumentValue::Text(Arc::new(t.to_string())), + AnyValueKind::Blob(b) => SqliteArgumentValue::Blob(Arc::new(b.to_vec())), + // AnyValueKind is `#[non_exhaustive]` but we should have covered everything + _ => unreachable!("BUG: missing mapping for {val:?}"), + }) + .collect(); + SqliteArguments { - values: args - .values - .0 - .into_iter() - .map(|val| match val { - AnyValueKind::Null(_) => SqliteArgumentValue::Null, - AnyValueKind::Bool(b) => SqliteArgumentValue::Int(b as i32), - AnyValueKind::SmallInt(i) => SqliteArgumentValue::Int(i as i32), - AnyValueKind::Integer(i) => SqliteArgumentValue::Int(i), - AnyValueKind::BigInt(i) => SqliteArgumentValue::Int64(i), - AnyValueKind::Real(r) => SqliteArgumentValue::Double(r as f64), - AnyValueKind::Double(d) => SqliteArgumentValue::Double(d), - AnyValueKind::Text(t) => SqliteArgumentValue::Text(t), - AnyValueKind::Blob(b) => SqliteArgumentValue::Blob(b), - // AnyValueKind is `#[non_exhaustive]` but we should have covered everything - _ => unreachable!("BUG: missing mapping for {val:?}"), - }) - .collect(), + values: SqliteArgumentsBuffer::new(values), } } diff --git a/sqlx-sqlite/src/arguments.rs b/sqlx-sqlite/src/arguments.rs index 410b4caa99..0b86e3b26a 100644 --- a/sqlx-sqlite/src/arguments.rs +++ b/sqlx-sqlite/src/arguments.rs @@ -4,77 +4,73 @@ use crate::statement::StatementHandle; use crate::Sqlite; use atoi::atoi; use libsqlite3_sys::SQLITE_OK; -use std::borrow::Cow; +use std::sync::Arc; pub(crate) use sqlx_core::arguments::*; +use sqlx_core::encode_owned::IntoEncode; use sqlx_core::error::BoxDynError; +use sqlx_core::types::Type; #[derive(Debug, Clone)] -pub enum SqliteArgumentValue<'q> { +pub enum SqliteArgumentValue { Null, - Text(Cow<'q, str>), - Blob(Cow<'q, [u8]>), + Text(Arc), + TextSlice(Arc), + Blob(Arc>), Double(f64), Int(i32), Int64(i64), } #[derive(Default, Debug, Clone)] -pub struct SqliteArguments<'q> { - pub(crate) values: Vec>, +pub struct SqliteArguments { + pub(crate) values: SqliteArgumentsBuffer, } -impl<'q> SqliteArguments<'q> { - pub(crate) fn add(&mut self, value: T) -> Result<(), BoxDynError> +#[derive(Default, Debug, Clone)] +pub struct SqliteArgumentsBuffer(Vec); + +impl SqliteArguments { + pub(crate) fn add<'t, T>(&mut self, value: T) -> Result<(), BoxDynError> where - T: Encode<'q, Sqlite>, + T: Encode<'t, Sqlite>, { - let value_length_before_encoding = self.values.len(); + let value_length_before_encoding = self.values.0.len(); match value.encode(&mut self.values) { - Ok(IsNull::Yes) => self.values.push(SqliteArgumentValue::Null), + Ok(IsNull::Yes) => self.values.0.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); + self.values.0.truncate(value_length_before_encoding); return Err(error); } }; Ok(()) } - - pub(crate) fn into_static(self) -> SqliteArguments<'static> { - SqliteArguments { - values: self - .values - .into_iter() - .map(SqliteArgumentValue::into_static) - .collect(), - } - } } -impl<'q> Arguments<'q> for SqliteArguments<'q> { +impl Arguments for SqliteArguments { type Database = Sqlite; fn reserve(&mut self, len: usize, _size_hint: usize) { - self.values.reserve(len); + self.values.0.reserve(len); } - fn add(&mut self, value: T) -> Result<(), BoxDynError> + fn add<'t, T>(&mut self, value: T) -> Result<(), BoxDynError> where - T: Encode<'q, Self::Database>, + T: IntoEncode + Type, { - self.add(value) + self.add(value.into_encode()) } fn len(&self) -> usize { - self.values.len() + self.values.0.len() } } -impl SqliteArguments<'_> { +impl SqliteArguments { pub(super) fn bind(&self, handle: &mut StatementHandle, offset: usize) -> Result { let mut arg_i = offset; // for handle in &statement.handles { @@ -103,7 +99,7 @@ impl SqliteArguments<'_> { arg_i }; - if n > self.values.len() { + if n > self.values.0.len() { // SQLite treats unbound variables as NULL // we reproduce this here // If you are reading this and think this should be an error, open an issue and we can @@ -113,32 +109,31 @@ impl SqliteArguments<'_> { break; } - self.values[n - 1].bind(handle, param_i)?; + self.values.0[n - 1].bind(handle, param_i)?; } Ok(arg_i - offset) } } -impl SqliteArgumentValue<'_> { - fn into_static(self) -> SqliteArgumentValue<'static> { - use SqliteArgumentValue::*; +impl SqliteArgumentsBuffer { + #[allow(dead_code)] // clippy incorrectly reports this as unused + pub(crate) fn new(values: Vec) -> SqliteArgumentsBuffer { + Self(values) + } - match self { - Null => Null, - Text(text) => Text(text.into_owned().into()), - Blob(blob) => Blob(blob.into_owned().into()), - Int(v) => Int(v), - Int64(v) => Int64(v), - Double(v) => Double(v), - } + pub(crate) fn push(&mut self, value: SqliteArgumentValue) { + self.0.push(value); } +} +impl SqliteArgumentValue { fn bind(&self, handle: &mut StatementHandle, i: usize) -> Result<(), Error> { use SqliteArgumentValue::*; let status = match self { Text(v) => handle.bind_text(i, v), + TextSlice(v) => handle.bind_text(i, v), Blob(v) => handle.bind_blob(i, v), Int(v) => handle.bind_int(i, *v), Int64(v) => handle.bind_int64(i, *v), diff --git a/sqlx-sqlite/src/connection/execute.rs b/sqlx-sqlite/src/connection/execute.rs index 7acbc91ff8..733a1abbe6 100644 --- a/sqlx-sqlite/src/connection/execute.rs +++ b/sqlx-sqlite/src/connection/execute.rs @@ -10,7 +10,7 @@ pub struct ExecuteIter<'a> { handle: &'a mut ConnectionHandle, statement: &'a mut VirtualStatement, logger: QueryLogger, - args: Option>, + args: Option, /// since a `VirtualStatement` can encompass multiple actual statements, /// this keeps track of the number of arguments so far @@ -19,12 +19,12 @@ pub struct ExecuteIter<'a> { goto_next: bool, } -pub(crate) fn iter<'a>( - conn: &'a mut ConnectionState, +pub(crate) fn iter( + conn: &mut ConnectionState, query: impl SqlSafeStr, - args: Option>, + args: Option, persistent: bool, -) -> Result, Error> { +) -> Result, Error> { let query = query.into_sql_str(); // fetch the cached statement or allocate a new one let statement = conn.statements.get(query.as_str(), persistent)?; @@ -43,7 +43,7 @@ pub(crate) fn iter<'a>( fn bind( statement: &mut StatementHandle, - arguments: &Option>, + arguments: &Option, offset: usize, ) -> Result { let mut n = 0; @@ -56,7 +56,7 @@ fn bind( } impl ExecuteIter<'_> { - pub fn finish(&mut self) -> Result<(), Error> { + pub fn finish(self) -> Result<(), Error> { for res in self { let _ = res?; } diff --git a/sqlx-sqlite/src/connection/worker.rs b/sqlx-sqlite/src/connection/worker.rs index ae50f3e896..f1dbea3682 100644 --- a/sqlx-sqlite/src/connection/worker.rs +++ b/sqlx-sqlite/src/connection/worker.rs @@ -63,7 +63,7 @@ enum Command { }, Execute { query: SqlStr, - arguments: Option>, + arguments: Option, persistent: bool, tx: flume::Sender, Error>>, limit: Option, @@ -353,7 +353,7 @@ impl ConnectionWorker { pub(crate) async fn execute( &mut self, query: SqlStr, - args: Option>, + args: Option, chan_size: usize, persistent: bool, limit: Option, @@ -364,7 +364,7 @@ impl ConnectionWorker { .send_async(( Command::Execute { query, - arguments: args.map(SqliteArguments::into_static), + arguments: args, persistent, tx, limit, diff --git a/sqlx-sqlite/src/database.rs b/sqlx-sqlite/src/database.rs index d34d8eb8c3..ca52d1cf6b 100644 --- a/sqlx-sqlite/src/database.rs +++ b/sqlx-sqlite/src/database.rs @@ -1,9 +1,9 @@ pub(crate) use sqlx_core::database::{Database, HasStatementCache}; +use crate::arguments::SqliteArgumentsBuffer; use crate::{ - SqliteArgumentValue, SqliteArguments, SqliteColumn, SqliteConnection, SqliteQueryResult, - SqliteRow, SqliteStatement, SqliteTransactionManager, SqliteTypeInfo, SqliteValue, - SqliteValueRef, + SqliteArguments, SqliteColumn, SqliteConnection, SqliteQueryResult, SqliteRow, SqliteStatement, + SqliteTransactionManager, SqliteTypeInfo, SqliteValue, SqliteValueRef, }; /// Sqlite database driver. @@ -26,8 +26,8 @@ impl Database for Sqlite { type Value = SqliteValue; type ValueRef<'r> = SqliteValueRef<'r>; - type Arguments<'q> = SqliteArguments<'q>; - type ArgumentBuffer<'q> = Vec>; + type Arguments = SqliteArguments; + type ArgumentBuffer = SqliteArgumentsBuffer; type Statement = SqliteStatement; diff --git a/sqlx-sqlite/src/lib.rs b/sqlx-sqlite/src/lib.rs index 36d3ee7e69..17b4bed1ca 100644 --- a/sqlx-sqlite/src/lib.rs +++ b/sqlx-sqlite/src/lib.rs @@ -74,7 +74,7 @@ extern crate sqlx_core; use std::sync::atomic::AtomicBool; -pub use arguments::{SqliteArgumentValue, SqliteArguments}; +pub use arguments::{SqliteArgumentValue, SqliteArguments, SqliteArgumentsBuffer}; pub use column::SqliteColumn; #[cfg(feature = "deserialize")] #[cfg_attr(docsrs, doc(cfg(feature = "deserialize")))] @@ -147,7 +147,7 @@ impl<'c, T: Executor<'c, Database = Sqlite>> SqliteExecutor<'c> for T {} pub type SqliteTransaction<'c> = sqlx_core::transaction::Transaction<'c, Sqlite>; // NOTE: required due to the lack of lazy normalization -impl_into_arguments_for_arguments!(SqliteArguments<'q>); +impl_into_arguments_for_arguments!(SqliteArguments); impl_column_index_for_row!(SqliteRow); impl_column_index_for_statement!(SqliteStatement); impl_acquire!(Sqlite, SqliteConnection); diff --git a/sqlx-sqlite/src/migrate.rs b/sqlx-sqlite/src/migrate.rs index 6f1796d376..3625e259fa 100644 --- a/sqlx-sqlite/src/migrate.rs +++ b/sqlx-sqlite/src/migrate.rs @@ -181,8 +181,8 @@ CREATE TABLE IF NOT EXISTS {table_name} ( "# ))) .bind(migration.version) - .bind(&*migration.description) - .bind(&*migration.checksum) + .bind(&migration.description) + .bind(&migration.checksum) .execute(&mut *tx) .await?; diff --git a/sqlx-sqlite/src/statement/mod.rs b/sqlx-sqlite/src/statement/mod.rs index 407c567045..93485397d3 100644 --- a/sqlx-sqlite/src/statement/mod.rs +++ b/sqlx-sqlite/src/statement/mod.rs @@ -45,7 +45,7 @@ impl Statement for SqliteStatement { &self.columns } - impl_statement_query!(SqliteArguments<'_>); + impl_statement_query!(SqliteArguments); } impl ColumnIndex for &'_ str { @@ -57,20 +57,3 @@ impl ColumnIndex for &'_ str { .copied() } } - -// #[cfg(feature = "any")] -// impl<'q> From> for crate::any::AnyStatement<'q> { -// #[inline] -// fn from(statement: SqliteStatement<'q>) -> Self { -// crate::any::AnyStatement::<'q> { -// columns: statement -// .columns -// .iter() -// .map(|col| col.clone().into()) -// .collect(), -// column_names: statement.column_names, -// parameters: Some(Either::Right(statement.parameters)), -// sql: statement.sql, -// } -// } -// } diff --git a/sqlx-sqlite/src/types/bool.rs b/sqlx-sqlite/src/types/bool.rs index a229298ff9..9228eef236 100644 --- a/sqlx-sqlite/src/types/bool.rs +++ b/sqlx-sqlite/src/types/bool.rs @@ -1,3 +1,4 @@ +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -15,11 +16,8 @@ impl Type for bool { } } -impl<'q> Encode<'q, Sqlite> for bool { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for bool { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int((*self).into())); Ok(IsNull::No) diff --git a/sqlx-sqlite/src/types/bytes.rs b/sqlx-sqlite/src/types/bytes.rs index 2d67335a52..6453007bc9 100644 --- a/sqlx-sqlite/src/types/bytes.rs +++ b/sqlx-sqlite/src/types/bytes.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::rc::Rc; use std::sync::Arc; +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -19,12 +20,9 @@ impl Type for [u8] { } } -impl<'q> Encode<'q, Sqlite> for &'q [u8] { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Blob(Cow::Borrowed(self))); +impl Encode<'_, Sqlite> for &'_ [u8] { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self.to_vec()))); Ok(IsNull::No) } @@ -37,19 +35,14 @@ impl<'r> Decode<'r, Sqlite> for &'r [u8] { } impl Encode<'_, Sqlite> for Box<[u8]> { - fn encode(self, args: &mut Vec>) -> Result { - args.push(SqliteArgumentValue::Blob(Cow::Owned(self.into_vec()))); + fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self.into_vec()))); Ok(IsNull::No) } - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Blob(Cow::Owned( - self.clone().into_vec(), - ))); + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self.clone().into_vec()))); Ok(IsNull::No) } @@ -65,18 +58,15 @@ impl Type for Vec { } } -impl<'q> Encode<'q, Sqlite> for Vec { - fn encode(self, args: &mut Vec>) -> Result { - args.push(SqliteArgumentValue::Blob(Cow::Owned(self))); +impl Encode<'_, Sqlite> for Vec { + fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self))); Ok(IsNull::No) } - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Blob(Cow::Owned(self.clone()))); + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self.clone()))); Ok(IsNull::No) } @@ -88,37 +78,28 @@ impl<'r> Decode<'r, Sqlite> for Vec { } } -impl<'q> Encode<'q, Sqlite> for Cow<'q, [u8]> { - fn encode(self, args: &mut Vec>) -> Result { - args.push(SqliteArgumentValue::Blob(self)); +impl Encode<'_, Sqlite> for Cow<'_, [u8]> { + fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self.into()))); Ok(IsNull::No) } - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Blob(self.clone())); + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new(self.to_vec()))); Ok(IsNull::No) } } -impl<'q> Encode<'q, Sqlite> for Arc<[u8]> { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for Arc<[u8]> { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { as Encode<'_, Sqlite>>::encode(self.to_vec(), args) } } -impl<'q> Encode<'q, Sqlite> for Rc<[u8]> { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for Rc<[u8]> { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { as Encode<'_, Sqlite>>::encode(self.to_vec(), args) } } diff --git a/sqlx-sqlite/src/types/chrono.rs b/sqlx-sqlite/src/types/chrono.rs index 5c4a41caff..1a54fc3071 100644 --- a/sqlx-sqlite/src/types/chrono.rs +++ b/sqlx-sqlite/src/types/chrono.rs @@ -7,7 +7,7 @@ use crate::{ error::BoxDynError, type_info::DataType, types::Type, - Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef, + Sqlite, SqliteArgumentsBuffer, SqliteTypeInfo, SqliteValueRef, }; use chrono::FixedOffset; use chrono::{ @@ -65,25 +65,25 @@ impl Encode<'_, Sqlite> for DateTime where Tz::Offset: Display, { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.to_rfc3339_opts(SecondsFormat::AutoSi, false), buf) } } impl Encode<'_, Sqlite> for NaiveDateTime { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.format("%F %T%.f").to_string(), buf) } } impl Encode<'_, Sqlite> for NaiveDate { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.format("%F").to_string(), buf) } } impl Encode<'_, Sqlite> for NaiveTime { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.format("%T%.f").to_string(), buf) } } @@ -218,3 +218,6 @@ impl<'r> Decode<'r, Sqlite> for NaiveTime { Err(format!("invalid time: {value}").into()) } } + +impl_into_encode_for_db!(Sqlite, DateTime); +impl_into_encode_for_db!(Sqlite, NaiveDateTime); diff --git a/sqlx-sqlite/src/types/float.rs b/sqlx-sqlite/src/types/float.rs index c6e105d783..d23b3f3a08 100644 --- a/sqlx-sqlite/src/types/float.rs +++ b/sqlx-sqlite/src/types/float.rs @@ -1,3 +1,4 @@ +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -11,11 +12,8 @@ impl Type for f32 { } } -impl<'q> Encode<'q, Sqlite> for f32 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for f32 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Double((*self).into())); Ok(IsNull::No) @@ -36,11 +34,8 @@ impl Type for f64 { } } -impl<'q> Encode<'q, Sqlite> for f64 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for f64 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Double(*self)); Ok(IsNull::No) diff --git a/sqlx-sqlite/src/types/int.rs b/sqlx-sqlite/src/types/int.rs index e87025e2fb..2cf4209ad6 100644 --- a/sqlx-sqlite/src/types/int.rs +++ b/sqlx-sqlite/src/types/int.rs @@ -1,3 +1,4 @@ +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -15,11 +16,8 @@ impl Type for i8 { } } -impl<'q> Encode<'q, Sqlite> for i8 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for i8 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int(*self as i32)); Ok(IsNull::No) @@ -46,11 +44,8 @@ impl Type for i16 { } } -impl<'q> Encode<'q, Sqlite> for i16 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for i16 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int(*self as i32)); Ok(IsNull::No) @@ -73,11 +68,8 @@ impl Type for i32 { } } -impl<'q> Encode<'q, Sqlite> for i32 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for i32 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int(*self)); Ok(IsNull::No) @@ -100,11 +92,8 @@ impl Type for i64 { } } -impl<'q> Encode<'q, Sqlite> for i64 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for i64 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int64(*self)); Ok(IsNull::No) diff --git a/sqlx-sqlite/src/types/json.rs b/sqlx-sqlite/src/types/json.rs index b8b665c4d3..dafd483658 100644 --- a/sqlx-sqlite/src/types/json.rs +++ b/sqlx-sqlite/src/types/json.rs @@ -1,10 +1,11 @@ use serde::{Deserialize, Serialize}; +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::types::{Json, Type}; -use crate::{type_info::DataType, Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef}; +use crate::{type_info::DataType, Sqlite, SqliteTypeInfo, SqliteValueRef}; impl Type for Json { fn type_info() -> SqliteTypeInfo { @@ -20,7 +21,7 @@ impl Encode<'_, Sqlite> for Json where T: Serialize, { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.encode_to_string()?, buf) } } diff --git a/sqlx-sqlite/src/types/str.rs b/sqlx-sqlite/src/types/str.rs index 5392f6401a..c90f9bba55 100644 --- a/sqlx-sqlite/src/types/str.rs +++ b/sqlx-sqlite/src/types/str.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::rc::Rc; use std::sync::Arc; +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -15,12 +16,9 @@ impl Type for str { } } -impl<'q> Encode<'q, Sqlite> for &'q str { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Borrowed(*self))); +impl Encode<'_, Sqlite> for &'_ str { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.to_string()))); Ok(IsNull::No) } @@ -33,19 +31,14 @@ impl<'r> Decode<'r, Sqlite> for &'r str { } impl Encode<'_, Sqlite> for Box { - fn encode(self, args: &mut Vec>) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Owned(self.into_string()))); + fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(String::from(self)))); Ok(IsNull::No) } - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Owned( - self.clone().into_string(), - ))); + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.to_string()))); Ok(IsNull::No) } @@ -57,18 +50,15 @@ impl Type for String { } } -impl<'q> Encode<'q, Sqlite> for String { - fn encode(self, args: &mut Vec>) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Owned(self))); +impl Encode<'_, Sqlite> for String { + fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self))); Ok(IsNull::No) } - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Owned(self.clone()))); + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.clone()))); Ok(IsNull::No) } @@ -80,37 +70,28 @@ impl<'r> Decode<'r, Sqlite> for String { } } -impl<'q> Encode<'q, Sqlite> for Cow<'q, str> { - fn encode(self, args: &mut Vec>) -> Result { - args.push(SqliteArgumentValue::Text(self)); +impl Encode<'_, Sqlite> for Cow<'_, str> { + fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.into()))); Ok(IsNull::No) } - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Text(self.clone())); + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.to_string()))); Ok(IsNull::No) } } -impl<'q> Encode<'q, Sqlite> for Arc { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for Arc { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { >::encode(self.to_string(), args) } } -impl<'q> Encode<'q, Sqlite> for Rc { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for Rc { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { >::encode(self.to_string(), args) } } diff --git a/sqlx-sqlite/src/types/text.rs b/sqlx-sqlite/src/types/text.rs index 80aab0d4ba..ef03caf5a0 100644 --- a/sqlx-sqlite/src/types/text.rs +++ b/sqlx-sqlite/src/types/text.rs @@ -1,4 +1,5 @@ -use crate::{Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef}; +use crate::arguments::SqliteArgumentsBuffer; +use crate::{Sqlite, SqliteTypeInfo, SqliteValueRef}; use sqlx_core::decode::Decode; use sqlx_core::encode::{Encode, IsNull}; use sqlx_core::error::BoxDynError; @@ -16,11 +17,11 @@ impl Type for Text { } } -impl<'q, T> Encode<'q, Sqlite> for Text +impl Encode<'_, Sqlite> for Text where T: Display, { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.0.to_string(), buf) } } diff --git a/sqlx-sqlite/src/types/time.rs b/sqlx-sqlite/src/types/time.rs index c7ad3b3d05..ad2818a6f2 100644 --- a/sqlx-sqlite/src/types/time.rs +++ b/sqlx-sqlite/src/types/time.rs @@ -1,3 +1,4 @@ +use crate::arguments::SqliteArgumentsBuffer; use crate::value::ValueRef; use crate::{ decode::Decode, @@ -5,7 +6,7 @@ use crate::{ error::BoxDynError, type_info::DataType, types::Type, - Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef, + Sqlite, SqliteTypeInfo, SqliteValueRef, }; use time::format_description::{well_known::Rfc3339, BorrowedFormatItem}; use time::macros::format_description as fd; @@ -55,27 +56,27 @@ impl Type for Time { } impl Encode<'_, Sqlite> for OffsetDateTime { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { Encode::::encode(self.format(&Rfc3339)?, buf) } } impl Encode<'_, Sqlite> for PrimitiveDateTime { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { let format = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"); Encode::::encode(self.format(&format)?, buf) } } impl Encode<'_, Sqlite> for Date { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { let format = fd!("[year]-[month]-[day]"); Encode::::encode(self.format(&format)?, buf) } } impl Encode<'_, Sqlite> for Time { - fn encode_by_ref(&self, buf: &mut Vec>) -> Result { + fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result { let format = fd!("[hour]:[minute]:[second].[subsecond]"); Encode::::encode(self.format(&format)?, buf) } @@ -316,3 +317,8 @@ mod formats { ] }; } + +impl_into_encode_for_db!(Sqlite, Date); +impl_into_encode_for_db!(Sqlite, OffsetDateTime); +impl_into_encode_for_db!(Sqlite, PrimitiveDateTime); +impl_into_encode_for_db!(Sqlite, Time); diff --git a/sqlx-sqlite/src/types/uint.rs b/sqlx-sqlite/src/types/uint.rs index f3d7da8c2f..1e8b1c5392 100644 --- a/sqlx-sqlite/src/types/uint.rs +++ b/sqlx-sqlite/src/types/uint.rs @@ -1,3 +1,4 @@ +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -15,11 +16,8 @@ impl Type for u8 { } } -impl<'q> Encode<'q, Sqlite> for u8 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for u8 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int(*self as i32)); Ok(IsNull::No) @@ -46,11 +44,8 @@ impl Type for u16 { } } -impl<'q> Encode<'q, Sqlite> for u16 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for u16 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int(*self as i32)); Ok(IsNull::No) @@ -73,11 +68,8 @@ impl Type for u32 { } } -impl<'q> Encode<'q, Sqlite> for u32 { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { +impl Encode<'_, Sqlite> for u32 { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { args.push(SqliteArgumentValue::Int64(*self as i64)); Ok(IsNull::No) diff --git a/sqlx-sqlite/src/types/uuid.rs b/sqlx-sqlite/src/types/uuid.rs index 99291be86e..3a1db85f47 100644 --- a/sqlx-sqlite/src/types/uuid.rs +++ b/sqlx-sqlite/src/types/uuid.rs @@ -1,10 +1,11 @@ +use crate::arguments::SqliteArgumentsBuffer; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::type_info::DataType; use crate::types::Type; use crate::{Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef}; -use std::borrow::Cow; +use std::sync::Arc; use uuid::{ fmt::{Hyphenated, Simple}, Uuid, @@ -20,12 +21,9 @@ impl Type for Uuid { } } -impl<'q> Encode<'q, Sqlite> for Uuid { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Blob(Cow::Owned( +impl Encode<'_, Sqlite> for Uuid { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Blob(Arc::new( self.as_bytes().to_vec(), ))); @@ -46,12 +44,9 @@ impl Type for Hyphenated { } } -impl<'q> Encode<'q, Sqlite> for Hyphenated { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string()))); +impl Encode<'_, Sqlite> for Hyphenated { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.to_string()))); Ok(IsNull::No) } @@ -72,12 +67,9 @@ impl Type for Simple { } } -impl<'q> Encode<'q, Sqlite> for Simple { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string()))); +impl Encode<'_, Sqlite> for Simple { + fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result { + args.push(SqliteArgumentValue::Text(Arc::new(self.to_string()))); Ok(IsNull::No) } @@ -91,3 +83,6 @@ impl Decode<'_, Sqlite> for Simple { Ok(uuid?.simple()) } } + +impl_into_encode_for_db!(Sqlite, Hyphenated); +impl_into_encode_for_db!(Sqlite, Simple); diff --git a/src/lib.rs b/src/lib.rs index 438463210d..56eee0b0cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,6 +142,10 @@ pub mod encode { pub use self::encode::Encode; +pub mod encode_owned { + pub use sqlx_core::encode_owned::{EncodeClone, EncodeOwned, IntoEncode}; +} + /// Provides [`Decode`] for decoding values from the database. pub mod decode { pub use sqlx_core::decode::Decode; diff --git a/tests/mysql/derives.rs b/tests/mysql/derives.rs index 825c7b8e2c..41ea4e62f8 100644 --- a/tests/mysql/derives.rs +++ b/tests/mysql/derives.rs @@ -1,9 +1,10 @@ +use sqlx_core::impl_into_encode_for_db; use sqlx_mysql::MySql; use sqlx_test::new; #[sqlx::test] async fn test_derive_strong_enum() -> anyhow::Result<()> { - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "PascalCase")] enum PascalCaseEnum { FooFoo, @@ -11,7 +12,9 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, PascalCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "camelCase")] enum CamelCaseEnum { FooFoo, @@ -19,7 +22,9 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, CamelCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "snake_case")] enum SnakeCaseEnum { FooFoo, @@ -27,7 +32,9 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, SnakeCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "SCREAMING_SNAKE_CASE")] enum ScreamingSnakeCaseEnum { FooFoo, @@ -35,7 +42,9 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, ScreamingSnakeCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "kebab-case")] enum KebabCaseEnum { FooFoo, @@ -43,7 +52,9 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, KebabCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "lowercase")] enum LowerCaseEnum { FooFoo, @@ -51,7 +62,9 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, LowerCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] #[sqlx(rename_all = "UPPERCASE")] enum UpperCaseEnum { FooFoo, @@ -59,13 +72,17 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { BazBaz, } - #[derive(sqlx::Type, PartialEq, Eq, Debug)] + impl_into_encode_for_db!(MySql, UpperCaseEnum); + + #[derive(sqlx::Type, Clone, PartialEq, Eq, Debug)] enum DefaultCaseEnum { FooFoo, BarBar, BazBaz, } + impl_into_encode_for_db!(MySql, DefaultCaseEnum); + #[derive(sqlx::FromRow, PartialEq, Eq, Debug)] struct StrongEnumRow { pascal_case: PascalCaseEnum, @@ -145,7 +162,7 @@ async fn test_derive_strong_enum() -> anyhow::Result<()> { #[sqlx::test] async fn test_derive_weak_enum() -> anyhow::Result<()> { - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(i8)] enum WeakEnumI8 { Foo = i8::MIN, @@ -153,7 +170,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = i8::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumI8); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(i16)] enum WeakEnumI16 { Foo = i16::MIN, @@ -161,7 +180,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = i16::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumI16); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(i32)] enum WeakEnumI32 { Foo = i32::MIN, @@ -169,7 +190,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = i32::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumI32); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(i64)] enum WeakEnumI64 { Foo = i64::MIN, @@ -177,7 +200,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = i64::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumI64); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(u8)] enum WeakEnumU8 { Foo = 0, @@ -185,7 +210,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = u8::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumU8); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(u16)] enum WeakEnumU16 { Foo = 0, @@ -193,7 +220,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = u16::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumU16); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(u32)] enum WeakEnumU32 { Foo = 0, @@ -201,7 +230,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = u32::MAX, } - #[derive(sqlx::Type, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumU32); + + #[derive(sqlx::Type, Clone, Debug, PartialEq, Eq)] #[repr(u64)] enum WeakEnumU64 { Foo = 0, @@ -209,7 +240,9 @@ async fn test_derive_weak_enum() -> anyhow::Result<()> { Baz = u64::MAX, } - #[derive(sqlx::FromRow, Debug, PartialEq, Eq)] + impl_into_encode_for_db!(MySql, WeakEnumU64); + + #[derive(sqlx::FromRow, Clone, Debug, PartialEq, Eq)] struct WeakEnumRow { i8: WeakEnumI8, i16: WeakEnumI16, diff --git a/tests/mysql/types.rs b/tests/mysql/types.rs index 9686d41721..e8e37f06ed 100644 --- a/tests/mysql/types.rs +++ b/tests/mysql/types.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use std::sync::Arc; use sqlx::mysql::MySql; -use sqlx::{Executor, FromRow, Row}; +use sqlx::{Executor, Row}; use sqlx::types::Text; diff --git a/tests/postgres/derives.rs b/tests/postgres/derives.rs index 86eac02065..3a2cdaddd6 100644 --- a/tests/postgres/derives.rs +++ b/tests/postgres/derives.rs @@ -1,6 +1,7 @@ use futures_util::TryStreamExt; use sqlx::postgres::types::PgRange; use sqlx::{Connection, Executor, FromRow, Postgres}; +use sqlx_core::impl_into_encode_for_db; use sqlx_core::sql_str::AssertSqlSafe; use sqlx_postgres::PgHasArrayType; use sqlx_test::{new, test_type}; @@ -8,7 +9,7 @@ use std::fmt::Debug; use std::ops::Bound; // Transparent types are rust-side wrappers over DB types -#[derive(PartialEq, Debug, sqlx::Type)] +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(transparent)] struct Transparent(i32); @@ -42,8 +43,10 @@ enum Weak { Three = 4, } +impl_into_encode_for_db!(Postgres, Weak); + // "Strong" enums can map to TEXT (25) -#[derive(PartialEq, Debug, sqlx::Type)] +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "text")] #[sqlx(rename_all = "lowercase")] enum Strong { @@ -54,8 +57,10 @@ enum Strong { Three, } +impl_into_encode_for_db!(Postgres, Strong); + // rename_all variants -#[derive(PartialEq, Debug, sqlx::Type)] +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_lower")] #[sqlx(rename_all = "lowercase")] enum ColorLower { @@ -64,7 +69,9 @@ enum ColorLower { Blue, } -#[derive(PartialEq, Debug, sqlx::Type)] +impl_into_encode_for_db!(Postgres, ColorLower); + +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_snake")] #[sqlx(rename_all = "snake_case")] enum ColorSnake { @@ -72,7 +79,9 @@ enum ColorSnake { BlueBlack, } -#[derive(PartialEq, Debug, sqlx::Type)] +impl_into_encode_for_db!(Postgres, ColorSnake); + +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_upper")] #[sqlx(rename_all = "UPPERCASE")] enum ColorUpper { @@ -81,7 +90,9 @@ enum ColorUpper { Blue, } -#[derive(PartialEq, Debug, sqlx::Type)] +impl_into_encode_for_db!(Postgres, ColorUpper); + +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_screaming_snake")] #[sqlx(rename_all = "SCREAMING_SNAKE_CASE")] enum ColorScreamingSnake { @@ -89,7 +100,9 @@ enum ColorScreamingSnake { BlueBlack, } -#[derive(PartialEq, Debug, sqlx::Type)] +impl_into_encode_for_db!(Postgres, ColorScreamingSnake); + +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_kebab_case")] #[sqlx(rename_all = "kebab-case")] enum ColorKebabCase { @@ -97,7 +110,9 @@ enum ColorKebabCase { BlueBlack, } -#[derive(PartialEq, Debug, sqlx::Type)] +impl_into_encode_for_db!(Postgres, ColorKebabCase); + +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_mixed_case")] #[sqlx(rename_all = "camelCase")] enum ColorCamelCase { @@ -105,7 +120,9 @@ enum ColorCamelCase { BlueBlack, } -#[derive(PartialEq, Debug, sqlx::Type)] +impl_into_encode_for_db!(Postgres, ColorCamelCase); + +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "color_camel_case")] #[sqlx(rename_all = "PascalCase")] enum ColorPascalCase { @@ -113,8 +130,10 @@ enum ColorPascalCase { BlueBlack, } +impl_into_encode_for_db!(Postgres, ColorPascalCase); + // "Strong" enum can map to a custom type -#[derive(PartialEq, Debug, sqlx::Type)] +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "mood")] #[sqlx(rename_all = "lowercase")] enum Mood { @@ -123,9 +142,11 @@ enum Mood { Sad, } +impl_into_encode_for_db!(Postgres, Mood); + // Records must map to a custom type // Note that all types are types in Postgres -#[derive(PartialEq, Debug, sqlx::Type)] +#[derive(PartialEq, Clone, Debug, sqlx::Type)] #[sqlx(type_name = "inventory_item")] struct InventoryItem { name: String, @@ -138,6 +159,8 @@ struct InventoryItem { #[sqlx(type_name = "float_range")] struct FloatRange(PgRange); +impl_into_encode_for_db!(Postgres, FloatRange); + // Custom domain type #[derive(sqlx::Type, Debug)] #[sqlx(type_name = "int4rangeL0pC")] @@ -745,6 +768,8 @@ async fn test_enum_with_schema() -> anyhow::Result<()> { Baz, } + impl_into_encode_for_db!(Postgres, Foo); + let mut conn = new::().await?; let foo: Foo = sqlx::query_scalar("SELECT $1::foo.\"Foo\"") @@ -797,7 +822,7 @@ async fn test_from_row_hygiene() -> anyhow::Result<()> { #[sqlx_macros::test] async fn test_custom_pg_array() -> anyhow::Result<()> { - #[derive(sqlx::Type)] + #[derive(Debug, sqlx::Type)] #[sqlx(no_pg_array)] pub struct User { pub id: i32, @@ -839,13 +864,13 @@ CREATE TABLE responses ( ) .await?; - #[derive(Debug, sqlx::Type)] + #[derive(Clone, Debug, sqlx::Type)] #[sqlx(type_name = "http_response")] struct HttpResponseRecord { headers: Vec, } - #[derive(Debug, sqlx::Type)] + #[derive(Clone, Debug, sqlx::Type)] #[sqlx(type_name = "header_pair")] struct HeaderPairRecord { name: String, diff --git a/tests/postgres/macros.rs b/tests/postgres/macros.rs index 676811bc00..af9f02c5ad 100644 --- a/tests/postgres/macros.rs +++ b/tests/postgres/macros.rs @@ -445,11 +445,11 @@ async fn with_test_row<'a>( Ok(transaction) } -#[derive(PartialEq, Eq, Debug, sqlx::Type)] +#[derive(Clone, PartialEq, Eq, Debug, sqlx::Type)] #[sqlx(transparent)] struct MyInt(i64); -#[derive(PartialEq, Eq, Debug, sqlx::Type)] +#[derive(Clone, PartialEq, Eq, Debug, sqlx::Type)] #[sqlx(transparent)] struct MyInt4(i32); diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index c580bb4eed..17ad66815a 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -7,7 +7,7 @@ use sqlx::postgres::{ }; use sqlx::{Column, Connection, Executor, Row, SqlSafeStr, Statement, TypeInfo}; use sqlx_core::sql_str::AssertSqlSafe; -use sqlx_core::{bytes::Bytes, error::BoxDynError}; +use sqlx_core::{bytes::Bytes, error::BoxDynError, impl_into_encode_for_db}; use sqlx_test::{new, pool, setup_if_needed}; use std::env; use std::pin::{pin, Pin}; @@ -1320,6 +1320,9 @@ CREATE TABLE heating_bills ( Ok(sqlx::encode::IsNull::No) } } + + impl_into_encode_for_db!(Postgres, WinterYearMonth); + let mut conn = new::().await?; let result = sqlx::query("DELETE FROM heating_bills;") @@ -1612,6 +1615,8 @@ CREATE TYPE another.some_enum_type AS ENUM ('d', 'e', 'f'); #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct SomeEnumType(String); + impl_into_encode_for_db!(Postgres, SomeEnumType); + impl sqlx::Type for SomeEnumType { fn type_info() -> sqlx::postgres::PgTypeInfo { sqlx::postgres::PgTypeInfo::with_name("some_enum_type") @@ -1774,6 +1779,8 @@ async fn it_encodes_custom_array_issue_1504() -> anyhow::Result<()> { Array(Vec), } + impl_into_encode_for_db!(Postgres, Value); + impl<'r> Decode<'r, Postgres> for Value { fn decode( value: sqlx::postgres::PgValueRef<'r>, @@ -1885,7 +1892,7 @@ async fn it_encodes_custom_array_issue_1504() -> anyhow::Result<()> { #[sqlx_macros::test] async fn test_issue_1254() -> anyhow::Result<()> { - #[derive(sqlx::Type)] + #[derive(Debug, sqlx::Type)] #[sqlx(type_name = "pair")] struct Pair { one: i32, @@ -1893,10 +1900,12 @@ async fn test_issue_1254() -> anyhow::Result<()> { } // array for custom type is not supported, use wrapper - #[derive(sqlx::Type)] + #[derive(Debug, sqlx::Type)] #[sqlx(type_name = "_pair")] struct Pairs(Vec); + impl_into_encode_for_db!(Postgres, Pairs); + let mut conn = new::().await?; conn.execute( " @@ -2092,7 +2101,7 @@ async fn test_bind_iter() -> anyhow::Result<()> { let mut conn = new::().await?; - #[derive(sqlx::FromRow, PartialEq, Debug)] + #[derive(sqlx::FromRow, PartialEq, Clone, Debug)] struct Person { id: i64, name: String, @@ -2128,8 +2137,20 @@ create temporary table person( // owned value .bind(people.iter().map(|p| p.id).bind_iter()) // borrowed value - .bind(people.iter().map(|p| &p.name).bind_iter()) - .bind(people.iter().map(|p| &p.birthdate).bind_iter()) + .bind( + people + .iter() + .map(|p| &p.name) + .map(|n| n.clone()) + .collect::>(), + ) + .bind( + people + .iter() + .map(|p| &p.birthdate) + .map(|n| n.clone()) + .collect::>(), + ) .execute(&mut conn) .await? .rows_affected(); @@ -2142,6 +2163,8 @@ create temporary table person( assert_eq!(people, p_query); Ok(()) } + +#[allow(unused)] async fn test_pg_copy_chunked() -> anyhow::Result<()> { let mut conn = new::().await?; diff --git a/tests/postgres/query_builder.rs b/tests/postgres/query_builder.rs index 5b73bcff35..60be9761d6 100644 --- a/tests/postgres/query_builder.rs +++ b/tests/postgres/query_builder.rs @@ -7,13 +7,13 @@ use sqlx_test::new; #[test] fn test_new() { - let qb: QueryBuilder<'_, Postgres> = QueryBuilder::new("SELECT * FROM users"); + let qb: QueryBuilder = QueryBuilder::new("SELECT * FROM users"); assert_eq!(qb.sql(), "SELECT * FROM users"); } #[test] fn test_push() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new("SELECT * FROM users"); + let mut qb: QueryBuilder = QueryBuilder::new("SELECT * FROM users"); let second_line = " WHERE last_name LIKE '[A-N]%';"; qb.push(second_line); @@ -26,7 +26,7 @@ fn test_push() { #[test] #[should_panic] fn test_push_panics_after_build_without_reset() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new("SELECT * FROM users;"); + let mut qb: QueryBuilder = QueryBuilder::new("SELECT * FROM users;"); let _query = qb.build(); @@ -35,7 +35,7 @@ fn test_push_panics_after_build_without_reset() { #[test] fn test_push_bind() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new("SELECT * FROM users WHERE id = "); + let mut qb: QueryBuilder = QueryBuilder::new("SELECT * FROM users WHERE id = "); qb.push_bind(42i32) .push(" OR membership_level = ") @@ -49,7 +49,7 @@ fn test_push_bind() { #[test] fn test_build() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new("SELECT * FROM users"); + let mut qb: QueryBuilder = QueryBuilder::new("SELECT * FROM users"); qb.push(" WHERE id = ").push_bind(42i32); let query = qb.build(); @@ -60,7 +60,7 @@ fn test_build() { #[test] fn test_reset() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new(""); + let mut qb: QueryBuilder = QueryBuilder::new(""); { let _query = qb @@ -76,7 +76,7 @@ fn test_reset() { #[test] fn test_query_builder_reuse() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new(""); + let mut qb: QueryBuilder = QueryBuilder::new(""); let _query = qb .push("SELECT * FROM users WHERE id = ") @@ -92,7 +92,7 @@ fn test_query_builder_reuse() { #[test] fn test_query_builder_with_args() { - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new(""); + let mut qb: QueryBuilder = QueryBuilder::new(""); let mut query = qb .push("SELECT * FROM users WHERE id = ") @@ -101,8 +101,7 @@ fn test_query_builder_with_args() { let args = query.take_arguments().unwrap().unwrap(); - let mut qb: QueryBuilder<'_, Postgres> = - QueryBuilder::with_arguments(query.sql().as_str(), args); + let mut qb: QueryBuilder = QueryBuilder::with_arguments(query.sql().as_str(), args); let query = qb.push(" OR membership_level = ").push_bind(3i32).build(); assert_eq!( @@ -118,7 +117,7 @@ async fn test_max_number_of_binds() -> anyhow::Result<()> { // // https://github.com/launchbadge/sqlx/issues/3464 - let mut qb: QueryBuilder<'_, Postgres> = QueryBuilder::new("SELECT ARRAY["); + let mut qb: QueryBuilder = QueryBuilder::new("SELECT ARRAY["); let mut elements = qb.separated(','); diff --git a/tests/postgres/types.rs b/tests/postgres/types.rs index 16a165278b..fa95ed9569 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -294,6 +294,7 @@ test_type!(mac_address_vec>(Postgres, ] )); +#[allow(deprecated)] // use of deprecated method `sqlx::types::chrono::TimeZone::ymd`: use `with_ymd_and_hms()` instead #[cfg(feature = "chrono")] mod chrono { use super::*; @@ -440,7 +441,7 @@ mod json { "array['\"😎\"'::jsonb, '\"🙋‍♀️\"'::jsonb]::jsonb[]" == vec![json!("😎"), json!("🙋‍♀️")], )); - #[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq)] + #[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq)] struct Friend { name: String, age: u32, diff --git a/tests/sqlite/derives.rs b/tests/sqlite/derives.rs index d91e012b30..ac9bcb7c6f 100644 --- a/tests/sqlite/derives.rs +++ b/tests/sqlite/derives.rs @@ -1,4 +1,5 @@ use sqlx::Sqlite; +use sqlx_core::impl_into_encode_for_db; use sqlx_test::test_type; #[derive(Debug, PartialEq, sqlx::Type)] @@ -12,3 +13,5 @@ test_type!(origin_enum(Sqlite, "1" == Origin::Foo, "2" == Origin::Bar, )); + +impl_into_encode_for_db!(Sqlite, Origin); diff --git a/tests/sqlite/macros.rs b/tests/sqlite/macros.rs index 74e689260a..0ab4dd56bf 100644 --- a/tests/sqlite/macros.rs +++ b/tests/sqlite/macros.rs @@ -175,6 +175,31 @@ async fn test_query_scalar() -> anyhow::Result<()> { Ok(()) } +#[sqlx_macros::test] +async fn query_by_string() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let string = "Hello, world!".to_string(); + let ref tuple = ("Hello, world!".to_string(),); + + let result = sqlx::query!( + "SELECT 'Hello, world!' as string where 'Hello, world!' in (?, ?, ?, ?, ?, ?, ?)", + string, // make sure we don't actually take ownership here + &string[..], + Some(&string), + Some(&string[..]), + Option::::None, + string.clone(), + tuple.0 // make sure we're not trying to move out of a field expression + ) + .fetch_one(&mut conn) + .await?; + + assert_eq!(result.string, string); + + Ok(()) +} + #[sqlx_macros::test] async fn macro_select_from_view() -> anyhow::Result<()> { let mut conn = new::().await?; diff --git a/tests/sqlite/types.rs b/tests/sqlite/types.rs index 4621dcafd1..c5245d5104 100644 --- a/tests/sqlite/types.rs +++ b/tests/sqlite/types.rs @@ -3,6 +3,7 @@ extern crate time_ as time; use sqlx::sqlite::{Sqlite, SqliteRow}; use sqlx::Type; use sqlx_core::executor::Executor; +use sqlx_core::impl_into_encode_for_db; use sqlx_core::row::Row; use sqlx_core::types::Text; use sqlx_test::new; @@ -267,6 +268,8 @@ async fn it_binds_with_borrowed_data() -> anyhow::Result<()> { Closed, } + impl_into_encode_for_db!(Sqlite, Status); + let owned = Status::New; let mut conn = new::().await?;