diff --git a/README.md b/README.md index 620491a900..df8a0e056d 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ sqlx = { version = "0.5", features = [ "runtime-async-std-native-tls" ] } - `chrono`: Add support for date and time types from `chrono`. -- `time`: Add support for date and time types from `time` crate (alternative to `chrono`, prefered by `query!` macro, if both enabled) +- `time`: Add support for date and time types from `time` crate (alternative to `chrono`, which is preferred by `query!` macro, if both enabled) - `bstr`: Add support for `bstr::BString`. diff --git a/sqlx-core/src/any/types.rs b/sqlx-core/src/any/types.rs index 26eeb88915..9f5d11f256 100644 --- a/sqlx-core/src/any/types.rs +++ b/sqlx-core/src/any/types.rs @@ -55,3 +55,83 @@ impl_any_decode!(f64); impl_any_decode!(&'r str); impl_any_decode!(String); + +// Conversions for Time SQL types +// Type +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_type!(chrono::NaiveDate); +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_type!(chrono::NaiveTime); + +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_type!(chrono::DateTime); +#[cfg(all( + feature = "chrono", + any(feature = "sqlite", feature = "postgres"), + not(any(feature = "mysql", feature = "mssql")) +))] +impl_any_type!(chrono::DateTime); + +// Encode +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_encode!(chrono::NaiveDate); +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_encode!(chrono::NaiveTime); +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_encode!(chrono::DateTime); +#[cfg(all( + feature = "chrono", + any(feature = "sqlite", feature = "postgres"), + not(any(feature = "mysql", feature = "mssql")) +))] +impl_any_encode!(chrono::DateTime); + +// Decode +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_decode!(chrono::NaiveDate); +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_decode!(chrono::NaiveTime); +#[cfg(all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") +))] +impl_any_decode!(chrono::DateTime); +#[cfg(all( + feature = "chrono", + any(feature = "sqlite", feature = "postgres"), + not(any(feature = "mysql", feature = "mssql")) +))] +impl_any_decode!(chrono::DateTime); diff --git a/sqlx-core/src/migrate/migrator.rs b/sqlx-core/src/migrate/migrator.rs index 63bd7a3bfc..9f23fe679b 100644 --- a/sqlx-core/src/migrate/migrator.rs +++ b/sqlx-core/src/migrate/migrator.rs @@ -27,6 +27,7 @@ impl Migrator { /// # }) /// # } /// ``` + /// See [MigrationSource] for details on structure of the `./migrations` directory. pub async fn new<'s, S>(source: S) -> Result where S: MigrationSource<'s>, diff --git a/sqlx-core/src/migrate/source.rs b/sqlx-core/src/migrate/source.rs index 7e06dd8c5f..4fde9f09a6 100644 --- a/sqlx-core/src/migrate/source.rs +++ b/sqlx-core/src/migrate/source.rs @@ -11,6 +11,12 @@ pub trait MigrationSource<'s>: Debug { fn resolve(self) -> BoxFuture<'s, Result, BoxDynError>>; } +/// Implementation of the `MigrationSource` for [std::path::Path]. +/// +/// The path has to point to a directory, which contains the migration SQL scripts. All these +/// scripts must be stored in files with names using the format `_.sql`, +/// where `` is a string that can be parsed into `i64` and its value is greater than zero, +/// and `` is a string. impl<'s> MigrationSource<'s> for &'s Path { fn resolve(self) -> BoxFuture<'s, Result, BoxDynError>> { Box::pin(async move { diff --git a/sqlx-core/src/pool/connection.rs b/sqlx-core/src/pool/connection.rs index 9bde08ca29..052b59f200 100644 --- a/sqlx-core/src/pool/connection.rs +++ b/sqlx-core/src/pool/connection.rs @@ -71,26 +71,29 @@ impl Drop for PoolConnection { fn drop(&mut self) { if let Some(mut live) = self.live.take() { let pool = self.pool.clone(); - - if live.raw.should_flush() { - spawn(async move { - // flush the connection (will immediately return if not needed) before - // we fully release to the pool - if let Err(e) = live.raw.flush().await { - log::error!("error occurred while flushing the connection: {}", e); - - // we now consider the connection to be broken - // close the connection and drop from the pool - let _ = live.float(&pool).into_idle().close().await; - } else { - // after we have flushed successfully, release to the pool - pool.release(live.float(&pool)); - } - }); - } else { - // nothing to flush, release immediately outside of a spawn - pool.release(live.float(&pool)); - } + spawn(async move { + let mut floating = live.float(&pool); + + // test the connection on-release to ensure it is still viable + // if an Executor future/stream is dropped during an `.await` call, the connection + // is likely to be left in an inconsistent state, in which case it should not be + // returned to the pool; also of course, if it was dropped due to an error + // this is simply a band-aid as SQLx-next (0.6) connections should be able + // to recover from cancellations + if let Err(e) = floating.raw.ping().await { + log::warn!( + "error occurred while testing the connection on-release: {}", + e + ); + + // we now consider the connection to be broken; just drop it to close + // trying to close gracefully might cause something weird to happen + drop(floating); + } else { + // if the connection is still viable, release it to th epool + pool.release(floating); + } + }); } } } diff --git a/sqlx-core/src/pool/inner.rs b/sqlx-core/src/pool/inner.rs index 7e911460f4..46f37d9156 100644 --- a/sqlx-core/src/pool/inner.rs +++ b/sqlx-core/src/pool/inner.rs @@ -113,13 +113,13 @@ impl SharedPool { let mut size = self.size(); while size < self.options.max_connections { - let new_size = self.size.compare_and_swap(size, size + 1, Ordering::AcqRel); - - if new_size == size { - return Some(DecrementSizeGuard::new(self)); + match self + .size + .compare_exchange(size, size + 1, Ordering::AcqRel, Ordering::Acquire) + { + Ok(_) => return Some(DecrementSizeGuard::new(self)), + Err(new_size) => size = new_size, } - - size = new_size; } None diff --git a/sqlx-core/src/types/bstr.rs b/sqlx-core/src/types/bstr.rs index 638e6f85c2..bb3a3b883d 100644 --- a/sqlx-core/src/types/bstr.rs +++ b/sqlx-core/src/types/bstr.rs @@ -5,6 +5,7 @@ use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::types::Type; +#[doc(no_inline)] pub use bstr::{BStr, BString, ByteSlice}; impl Type for BString diff --git a/sqlx-core/src/types/git2.rs b/sqlx-core/src/types/git2.rs index 389641034a..ca40598243 100644 --- a/sqlx-core/src/types/git2.rs +++ b/sqlx-core/src/types/git2.rs @@ -5,6 +5,7 @@ use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::types::Type; +#[doc(no_inline)] pub use git2::Oid; impl Type for Oid diff --git a/sqlx-core/src/types/mod.rs b/sqlx-core/src/types/mod.rs index 20350b9780..09c4de851b 100644 --- a/sqlx-core/src/types/mod.rs +++ b/sqlx-core/src/types/mod.rs @@ -34,11 +34,13 @@ mod json; #[cfg(feature = "uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] +#[doc(no_inline)] pub use uuid::{self, Uuid}; #[cfg(feature = "chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub mod chrono { + #[doc(no_inline)] pub use chrono::{ DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc, }; @@ -46,25 +48,30 @@ pub mod chrono { #[cfg(feature = "bit-vec")] #[cfg_attr(docsrs, doc(cfg(feature = "bit-vec")))] +#[doc(no_inline)] pub use bit_vec::BitVec; #[cfg(feature = "time")] #[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub mod time { + #[doc(no_inline)] pub use time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset}; } #[cfg(feature = "bigdecimal")] #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] +#[doc(no_inline)] pub use bigdecimal::BigDecimal; #[cfg(feature = "decimal")] #[cfg_attr(docsrs, doc(cfg(feature = "decimal")))] +#[doc(no_inline)] pub use rust_decimal::Decimal; #[cfg(feature = "ipnetwork")] #[cfg_attr(docsrs, doc(cfg(feature = "ipnetwork")))] pub mod ipnetwork { + #[doc(no_inline)] pub use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; } diff --git a/sqlx-macros/src/derives/decode.rs b/sqlx-macros/src/derives/decode.rs index 5f647ef166..f926718f6c 100644 --- a/sqlx-macros/src/derives/decode.rs +++ b/sqlx-macros/src/derives/decode.rs @@ -73,6 +73,7 @@ fn expand_derive_decode_transparent( let (impl_generics, _, where_clause) = generics.split_for_impl(); let tts = quote!( + #[automatically_derived] impl #impl_generics ::sqlx::decode::Decode<'r, DB> for #ident #ty_generics #where_clause { fn decode( value: >::ValueRef, @@ -111,6 +112,7 @@ fn expand_derive_decode_weak_enum( .collect::>(); Ok(quote!( + #[automatically_derived] impl<'r, DB: ::sqlx::Database> ::sqlx::decode::Decode<'r, DB> for #ident where #repr: ::sqlx::decode::Decode<'r, DB>, @@ -173,6 +175,7 @@ fn expand_derive_decode_strong_enum( if cfg!(feature = "mysql") { tts.extend(quote!( + #[automatically_derived] impl<'r> ::sqlx::decode::Decode<'r, ::sqlx::mysql::MySql> for #ident { fn decode( value: ::sqlx::mysql::MySqlValueRef<'r>, @@ -198,6 +201,7 @@ fn expand_derive_decode_strong_enum( if cfg!(feature = "postgres") { tts.extend(quote!( + #[automatically_derived] impl<'r> ::sqlx::decode::Decode<'r, ::sqlx::postgres::Postgres> for #ident { fn decode( value: ::sqlx::postgres::PgValueRef<'r>, @@ -223,6 +227,7 @@ fn expand_derive_decode_strong_enum( if cfg!(feature = "sqlite") { tts.extend(quote!( + #[automatically_derived] impl<'r> ::sqlx::decode::Decode<'r, ::sqlx::sqlite::Sqlite> for #ident { fn decode( value: ::sqlx::sqlite::SqliteValueRef<'r>, @@ -291,6 +296,7 @@ fn expand_derive_decode_struct( let names = fields.iter().map(|field| &field.ident); tts.extend(quote!( + #[automatically_derived] impl #impl_generics ::sqlx::decode::Decode<'r, ::sqlx::Postgres> for #ident #ty_generics #where_clause { diff --git a/sqlx-macros/src/derives/encode.rs b/sqlx-macros/src/derives/encode.rs index 10f0f818df..7bb568210f 100644 --- a/sqlx-macros/src/derives/encode.rs +++ b/sqlx-macros/src/derives/encode.rs @@ -78,6 +78,7 @@ fn expand_derive_encode_transparent( let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(quote!( + #[automatically_derived] impl #impl_generics ::sqlx::encode::Encode<#lifetime, DB> for #ident #ty_generics #where_clause { @@ -115,6 +116,7 @@ fn expand_derive_encode_weak_enum( } Ok(quote!( + #[automatically_derived] impl<'q, DB: ::sqlx::Database> ::sqlx::encode::Encode<'q, DB> for #ident where #repr: ::sqlx::encode::Encode<'q, DB>, @@ -164,6 +166,7 @@ fn expand_derive_encode_strong_enum( } Ok(quote!( + #[automatically_derived] impl<'q, DB: ::sqlx::Database> ::sqlx::encode::Encode<'q, DB> for #ident where &'q ::std::primitive::str: ::sqlx::encode::Encode<'q, DB>, @@ -239,6 +242,7 @@ fn expand_derive_encode_struct( }); tts.extend(quote!( + #[automatically_derived] impl #impl_generics ::sqlx::encode::Encode<'_, ::sqlx::Postgres> for #ident #ty_generics #where_clause { diff --git a/sqlx-macros/src/derives/row.rs b/sqlx-macros/src/derives/row.rs index f50ce8fd4b..da77c87469 100644 --- a/sqlx-macros/src/derives/row.rs +++ b/sqlx-macros/src/derives/row.rs @@ -148,6 +148,7 @@ fn expand_derive_from_row_struct( let mut extend_block = |exprpath: ExprPath| { block.extend(quote!( + #[automatically_derived] impl #impl_generics ::sqlx::FromRow<#lifetime, R> for #ident #ty_generics #where_clause,R: ::sqlx::Row, { fn from_row(row: &#lifetime R) -> ::sqlx::Result { #(#reads)* @@ -222,6 +223,7 @@ fn expand_derive_from_row_struct_unnamed( .map(|(idx, _)| quote!(row.try_get(#idx)?)); Ok(quote!( + #[automatically_derived] impl #impl_generics ::sqlx::FromRow<#lifetime, R> for #ident #ty_generics #where_clause { fn from_row(row: &#lifetime R) -> ::sqlx::Result { ::std::result::Result::Ok(#ident ( diff --git a/sqlx-macros/src/derives/type.rs b/sqlx-macros/src/derives/type.rs index 905b252c8c..20ab90e943 100644 --- a/sqlx-macros/src/derives/type.rs +++ b/sqlx-macros/src/derives/type.rs @@ -71,6 +71,7 @@ fn expand_derive_has_sql_type_transparent( let (impl_generics, _, where_clause) = generics.split_for_impl(); return Ok(quote!( + #[automatically_derived] impl #impl_generics ::sqlx::Type< DB > for #ident #ty_generics #where_clause { fn type_info() -> DB::TypeInfo { <#ty as ::sqlx::Type>::type_info() @@ -89,6 +90,7 @@ fn expand_derive_has_sql_type_transparent( let ty_name = type_name(ident, attr.type_name.as_ref()); tts.extend(quote!( + #[automatically_derived] impl ::sqlx::Type<::sqlx::postgres::Postgres> for #ident #ty_generics { fn type_info() -> ::sqlx::postgres::PgTypeInfo { ::sqlx::postgres::PgTypeInfo::with_name(#ty_name) @@ -108,6 +110,7 @@ fn expand_derive_has_sql_type_weak_enum( let repr = attr.repr.unwrap(); let ident = &input.ident; let ts = quote!( + #[automatically_derived] impl ::sqlx::Type for #ident where #repr: ::sqlx::Type, @@ -132,6 +135,7 @@ fn expand_derive_has_sql_type_strong_enum( if cfg!(feature = "mysql") { tts.extend(quote!( + #[automatically_derived] impl ::sqlx::Type<::sqlx::MySql> for #ident { fn type_info() -> ::sqlx::mysql::MySqlTypeInfo { ::sqlx::mysql::MySqlTypeInfo::__enum() @@ -148,6 +152,7 @@ fn expand_derive_has_sql_type_strong_enum( let ty_name = type_name(ident, attributes.type_name.as_ref()); tts.extend(quote!( + #[automatically_derived] impl ::sqlx::Type<::sqlx::Postgres> for #ident { fn type_info() -> ::sqlx::postgres::PgTypeInfo { ::sqlx::postgres::PgTypeInfo::with_name(#ty_name) @@ -158,6 +163,7 @@ fn expand_derive_has_sql_type_strong_enum( if cfg!(feature = "sqlite") { tts.extend(quote!( + #[automatically_derived] impl sqlx::Type<::sqlx::Sqlite> for #ident { fn type_info() -> ::sqlx::sqlite::SqliteTypeInfo { <::std::primitive::str as ::sqlx::Type>::type_info() @@ -186,6 +192,7 @@ fn expand_derive_has_sql_type_struct( let ty_name = type_name(ident, attributes.type_name.as_ref()); tts.extend(quote!( + #[automatically_derived] impl ::sqlx::Type<::sqlx::Postgres> for #ident { fn type_info() -> ::sqlx::postgres::PgTypeInfo { ::sqlx::postgres::PgTypeInfo::with_name(#ty_name) diff --git a/tests/any/pool.rs b/tests/any/pool.rs index 057beaddae..91b97978bf 100644 --- a/tests/any/pool.rs +++ b/tests/any/pool.rs @@ -29,7 +29,9 @@ async fn pool_should_invoke_after_connect() -> anyhow::Result<()> { let _ = pool.acquire().await?; let _ = pool.acquire().await?; - assert_eq!(counter.load(Ordering::SeqCst), 1); + // since connections are released asynchronously, + // `.after_connect()` may be called more than once + assert!(counter.load(Ordering::SeqCst) >= 1); Ok(()) }