Merge pull request #806 from LukasKalbertodt/add-from-to-sql-arrays
Add `FromSql` and `ToSql` impls for arrays (guarded behind feature)
This commit is contained in:
commit
8e2e90a161
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -59,7 +59,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: sfackler/actions/rustup@master
|
||||
with:
|
||||
version: 1.46.0
|
||||
version: 1.51.0
|
||||
- run: echo "::set-output name=version::$(rustc --version)"
|
||||
id: rust-version
|
||||
- uses: actions/cache@v1
|
||||
|
@ -12,6 +12,7 @@ categories = ["database"]
|
||||
|
||||
[features]
|
||||
derive = ["postgres-derive"]
|
||||
array-impls = ["array-init"]
|
||||
with-bit-vec-0_6 = ["bit-vec-06"]
|
||||
with-chrono-0_4 = ["chrono-04"]
|
||||
with-eui48-0_4 = ["eui48-04"]
|
||||
@ -28,6 +29,7 @@ fallible-iterator = "0.2"
|
||||
postgres-protocol = { version = "0.6.1", path = "../postgres-protocol" }
|
||||
postgres-derive = { version = "0.4.0", optional = true, path = "../postgres-derive" }
|
||||
|
||||
array-init = { version = "2", optional = true }
|
||||
bit-vec-06 = { version = "0.6", package = "bit-vec", optional = true }
|
||||
chrono-04 = { version = "0.4.16", package = "chrono", default-features = false, features = ["clock"], optional = true }
|
||||
eui48-04 = { version = "0.4", package = "eui48", optional = true }
|
||||
|
@ -428,8 +428,10 @@ impl WrongType {
|
||||
///
|
||||
/// # Arrays
|
||||
///
|
||||
/// `FromSql` is implemented for `Vec<T>` where `T` implements `FromSql`, and
|
||||
/// corresponds to one-dimensional Postgres arrays.
|
||||
/// `FromSql` is implemented for `Vec<T>` and `[T; N]` where `T` implements
|
||||
/// `FromSql`, and corresponds to one-dimensional Postgres arrays. **Note:**
|
||||
/// the impl for arrays only exist when the Cargo feature `array-impls` is
|
||||
/// enabled.
|
||||
pub trait FromSql<'a>: Sized {
|
||||
/// Creates a new value of this type from a buffer of data of the specified
|
||||
/// Postgres `Type` in its binary format.
|
||||
@ -513,6 +515,47 @@ impl<'a, T: FromSql<'a>> FromSql<'a> for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "array-impls")]
|
||||
impl<'a, T: FromSql<'a>, const N: usize> FromSql<'a> for [T; N] {
|
||||
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
|
||||
let member_type = match *ty.kind() {
|
||||
Kind::Array(ref member) => member,
|
||||
_ => panic!("expected array type"),
|
||||
};
|
||||
|
||||
let array = types::array_from_sql(raw)?;
|
||||
if array.dimensions().count()? > 1 {
|
||||
return Err("array contains too many dimensions".into());
|
||||
}
|
||||
|
||||
let mut values = array.values();
|
||||
let out = array_init::try_array_init(|i| {
|
||||
let v = values
|
||||
.next()?
|
||||
.ok_or_else(|| -> Box<dyn Error + Sync + Send> {
|
||||
format!("too few elements in array (expected {}, got {})", N, i).into()
|
||||
})?;
|
||||
T::from_sql_nullable(member_type, v)
|
||||
})?;
|
||||
if values.next()?.is_some() {
|
||||
return Err(format!(
|
||||
"excess elements in array (expected {}, got more than that)",
|
||||
N,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match *ty.kind() {
|
||||
Kind::Array(ref inner) => T::accepts(inner),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromSql<'a> for Vec<u8> {
|
||||
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<Vec<u8>, Box<dyn Error + Sync + Send>> {
|
||||
Ok(types::bytea_from_sql(raw).to_owned())
|
||||
@ -691,8 +734,10 @@ pub enum IsNull {
|
||||
///
|
||||
/// # Arrays
|
||||
///
|
||||
/// `ToSql` is implemented for `Vec<T>` and `&[T]` where `T` implements `ToSql`,
|
||||
/// and corresponds to one-dimensional Postgres arrays with an index offset of 1.
|
||||
/// `ToSql` is implemented for `Vec<T>`, `&[T]` and `[T; N]` where `T`
|
||||
/// implements `ToSql`, and corresponds to one-dimensional Postgres arrays with
|
||||
/// an index offset of 1. **Note:** the impl for arrays only exist when the
|
||||
/// Cargo feature `array-impls` is enabled.
|
||||
pub trait ToSql: fmt::Debug {
|
||||
/// Converts the value of `self` into the binary format of the specified
|
||||
/// Postgres `Type`, appending it to `out`.
|
||||
@ -808,6 +853,19 @@ impl<'a> ToSql for &'a [u8] {
|
||||
to_sql_checked!();
|
||||
}
|
||||
|
||||
#[cfg(feature = "array-impls")]
|
||||
impl<T: ToSql, const N: usize> ToSql for [T; N] {
|
||||
fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
|
||||
<&[T] as ToSql>::to_sql(&&self[..], ty, w)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
<&[T] as ToSql>::accepts(ty)
|
||||
}
|
||||
|
||||
to_sql_checked!();
|
||||
}
|
||||
|
||||
impl<T: ToSql> ToSql for Vec<T> {
|
||||
fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
|
||||
<&[T] as ToSql>::to_sql(&&**self, ty, w)
|
||||
|
@ -21,6 +21,7 @@ all-features = true
|
||||
circle-ci = { repository = "sfackler/rust-postgres" }
|
||||
|
||||
[features]
|
||||
array-impls = ["tokio-postgres/array-impls"]
|
||||
with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"]
|
||||
with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"]
|
||||
with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"]
|
||||
|
@ -27,6 +27,7 @@ circle-ci = { repository = "sfackler/rust-postgres" }
|
||||
default = ["runtime"]
|
||||
runtime = ["tokio/net", "tokio/time"]
|
||||
|
||||
array-impls = ["postgres-types/array-impls"]
|
||||
with-bit-vec-0_6 = ["postgres-types/with-bit-vec-0_6"]
|
||||
with-chrono-0_4 = ["postgres-types/with-chrono-0_4"]
|
||||
with-eui48-0_4 = ["postgres-types/with-eui48-0_4"]
|
||||
|
@ -350,7 +350,7 @@ async fn test_hstore_params() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_array_params() {
|
||||
async fn test_array_vec_params() {
|
||||
test_type(
|
||||
"integer[]",
|
||||
&[
|
||||
@ -363,6 +363,18 @@ async fn test_array_params() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "array-impls")]
|
||||
#[tokio::test]
|
||||
async fn test_array_array_params() {
|
||||
test_type("integer[]", &[(Some([1i32, 2i32]), "ARRAY[1,2]")]).await;
|
||||
test_type("text[]", &[(Some(["peter".to_string()]), "ARRAY['peter']")]).await;
|
||||
test_type(
|
||||
"integer[]",
|
||||
&[(Some([] as [i32; 0]), "ARRAY[]"), (None, "NULL")],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[allow(clippy::eq_op)]
|
||||
async fn test_nan_param<T>(sql_type: &str)
|
||||
where
|
||||
|
Loading…
Reference in New Issue
Block a user