Skip to content

Commit

Permalink
Add docs to PgBindIter and test to ensure it works for owned and borr…
Browse files Browse the repository at this point in the history
…owed types
  • Loading branch information
tylerhawkes committed Dec 23, 2024
1 parent ba7525f commit afe6b19
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
36 changes: 36 additions & 0 deletions sqlx-postgres/src/bind_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ use sqlx_core::{

use crate::{type_info::PgType, PgArgumentBuffer, PgHasArrayType, PgTypeInfo, Postgres};

/// A wrapper enabling iterators to encode arrays in Postgres.
///
/// Because of the blanket impl of `PgHasArrayType` for all references
/// we can borrow instead of needing to clone or copy in the iterators
/// and it still works
///
/// Previously, 3 separate arrays would be needed in this example which
/// requires iterating 3 times to collect items into the array and then
/// iterating over them again to encode.
///
/// This now requires only iterating over the array once for each field
/// while using less memory giving both speed and memory usage improvements.
///
/// ```rust,ignore
/// # use sqlx::types::chrono::{DateTime, Utc}
/// # fn people() -> &'static [Person] {
/// # &[]
/// # }
/// #[derive(sqlx::FromRow)]
/// struct Person {
/// id: i64,
/// name: String,
/// birthdate: DateTime<Utc>,
/// }
///
/// let people: &[Person] = people();
///
/// sqlx::query(
/// "insert into person(id, name, birthdate) select * from unnest($1, $2, $3)"
/// )
/// .bind(PgBindIter::from(people.iter().map(|p|p.id)))
/// .bind(PgBindIter::from(people.iter().map(|p|&p.name)))
/// .bind(PgBindIter::from(people.iter().map(|p|&p.birthdate)))
/// .execute(pool)
/// .await?;
/// ```
pub struct PgBindIter<I>(I);

impl<I> PgBindIter<I> {
Expand Down
58 changes: 58 additions & 0 deletions tests/postgres/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2042,3 +2042,61 @@ async fn test_issue_3052() {
"expected encode error, got {too_large_error:?}",
);
}

#[sqlx_macros::test]
async fn test_bind_iter() -> anyhow::Result<()> {
use sqlx::postgres::PgBindIter;
use sqlx::types::chrono::{DateTime, Utc};

let mut conn = new::<Postgres>().await?;

#[derive(sqlx::FromRow, PartialEq, Debug)]
struct Person {
id: i64,
name: String,
birthdate: DateTime<Utc>,
}

let people: Vec<Person> = vec![
Person {
id: 1,
name: "Alice".into(),
birthdate: "1984-01-01T00:00:00Z".parse().unwrap(),
},
Person {
id: 2,
name: "Bob".into(),
birthdate: "2000-01-01T00:00:00Z".parse().unwrap(),
},
];

sqlx::query(
r#"
create temporary table person(
id int8 primary key,
name text not null,
birthdate timestamptz not null
)"#,
)
.execute(&mut conn)
.await?;

let rows_affected =
sqlx::query("insert into person(id, name, birthdate) select * from unnest($1, $2, $3)")
// owned value
.bind(PgBindIter::from(people.iter().map(|p| p.id)))
// borrowed value
.bind(PgBindIter::from(people.iter().map(|p| &p.name)))
.bind(PgBindIter::from(people.iter().map(|p| &p.birthdate)))
.execute(&mut conn)
.await?
.rows_affected();
assert_eq!(rows_affected, 2);

let p_query = sqlx::query_as::<_, Person>("select * from person order by id")
.fetch_all(&mut conn)
.await?;

assert_eq!(people, p_query);
Ok(())
}

0 comments on commit afe6b19

Please sign in to comment.