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: actions/checkout@v2
|
||||||
- uses: sfackler/actions/rustup@master
|
- uses: sfackler/actions/rustup@master
|
||||||
with:
|
with:
|
||||||
version: 1.46.0
|
version: 1.51.0
|
||||||
- run: echo "::set-output name=version::$(rustc --version)"
|
- run: echo "::set-output name=version::$(rustc --version)"
|
||||||
id: rust-version
|
id: rust-version
|
||||||
- uses: actions/cache@v1
|
- uses: actions/cache@v1
|
||||||
|
@ -12,6 +12,7 @@ categories = ["database"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
derive = ["postgres-derive"]
|
derive = ["postgres-derive"]
|
||||||
|
array-impls = ["array-init"]
|
||||||
with-bit-vec-0_6 = ["bit-vec-06"]
|
with-bit-vec-0_6 = ["bit-vec-06"]
|
||||||
with-chrono-0_4 = ["chrono-04"]
|
with-chrono-0_4 = ["chrono-04"]
|
||||||
with-eui48-0_4 = ["eui48-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-protocol = { version = "0.6.1", path = "../postgres-protocol" }
|
||||||
postgres-derive = { version = "0.4.0", optional = true, path = "../postgres-derive" }
|
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 }
|
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 }
|
chrono-04 = { version = "0.4.16", package = "chrono", default-features = false, features = ["clock"], optional = true }
|
||||||
eui48-04 = { version = "0.4", package = "eui48", optional = true }
|
eui48-04 = { version = "0.4", package = "eui48", optional = true }
|
||||||
|
@ -428,8 +428,10 @@ impl WrongType {
|
|||||||
///
|
///
|
||||||
/// # Arrays
|
/// # Arrays
|
||||||
///
|
///
|
||||||
/// `FromSql` is implemented for `Vec<T>` where `T` implements `FromSql`, and
|
/// `FromSql` is implemented for `Vec<T>` and `[T; N]` where `T` implements
|
||||||
/// corresponds to one-dimensional Postgres arrays.
|
/// `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 {
|
pub trait FromSql<'a>: Sized {
|
||||||
/// Creates a new value of this type from a buffer of data of the specified
|
/// Creates a new value of this type from a buffer of data of the specified
|
||||||
/// Postgres `Type` in its binary format.
|
/// 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> {
|
impl<'a> FromSql<'a> for Vec<u8> {
|
||||||
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<Vec<u8>, Box<dyn Error + Sync + Send>> {
|
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<Vec<u8>, Box<dyn Error + Sync + Send>> {
|
||||||
Ok(types::bytea_from_sql(raw).to_owned())
|
Ok(types::bytea_from_sql(raw).to_owned())
|
||||||
@ -691,8 +734,10 @@ pub enum IsNull {
|
|||||||
///
|
///
|
||||||
/// # Arrays
|
/// # Arrays
|
||||||
///
|
///
|
||||||
/// `ToSql` is implemented for `Vec<T>` and `&[T]` where `T` implements `ToSql`,
|
/// `ToSql` is implemented for `Vec<T>`, `&[T]` and `[T; N]` where `T`
|
||||||
/// and corresponds to one-dimensional Postgres arrays with an index offset of 1.
|
/// 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 {
|
pub trait ToSql: fmt::Debug {
|
||||||
/// Converts the value of `self` into the binary format of the specified
|
/// Converts the value of `self` into the binary format of the specified
|
||||||
/// Postgres `Type`, appending it to `out`.
|
/// Postgres `Type`, appending it to `out`.
|
||||||
@ -808,6 +853,19 @@ impl<'a> ToSql for &'a [u8] {
|
|||||||
to_sql_checked!();
|
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> {
|
impl<T: ToSql> ToSql for Vec<T> {
|
||||||
fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
|
fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
|
||||||
<&[T] as ToSql>::to_sql(&&**self, ty, w)
|
<&[T] as ToSql>::to_sql(&&**self, ty, w)
|
||||||
|
@ -21,6 +21,7 @@ all-features = true
|
|||||||
circle-ci = { repository = "sfackler/rust-postgres" }
|
circle-ci = { repository = "sfackler/rust-postgres" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
array-impls = ["tokio-postgres/array-impls"]
|
||||||
with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"]
|
with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"]
|
||||||
with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"]
|
with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"]
|
||||||
with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"]
|
with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"]
|
||||||
|
@ -27,6 +27,7 @@ circle-ci = { repository = "sfackler/rust-postgres" }
|
|||||||
default = ["runtime"]
|
default = ["runtime"]
|
||||||
runtime = ["tokio/net", "tokio/time"]
|
runtime = ["tokio/net", "tokio/time"]
|
||||||
|
|
||||||
|
array-impls = ["postgres-types/array-impls"]
|
||||||
with-bit-vec-0_6 = ["postgres-types/with-bit-vec-0_6"]
|
with-bit-vec-0_6 = ["postgres-types/with-bit-vec-0_6"]
|
||||||
with-chrono-0_4 = ["postgres-types/with-chrono-0_4"]
|
with-chrono-0_4 = ["postgres-types/with-chrono-0_4"]
|
||||||
with-eui48-0_4 = ["postgres-types/with-eui48-0_4"]
|
with-eui48-0_4 = ["postgres-types/with-eui48-0_4"]
|
||||||
|
@ -350,7 +350,7 @@ async fn test_hstore_params() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_array_params() {
|
async fn test_array_vec_params() {
|
||||||
test_type(
|
test_type(
|
||||||
"integer[]",
|
"integer[]",
|
||||||
&[
|
&[
|
||||||
@ -363,6 +363,18 @@ async fn test_array_params() {
|
|||||||
.await;
|
.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)]
|
#[allow(clippy::eq_op)]
|
||||||
async fn test_nan_param<T>(sql_type: &str)
|
async fn test_nan_param<T>(sql_type: &str)
|
||||||
where
|
where
|
||||||
|
Loading…
Reference in New Issue
Block a user