mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 06:24:16 +00:00
add push_tuples
for QueryBuilder (#1954)
* add `push_tuples` for QueryBuilder * update docs * fix docs
This commit is contained in:
parent
9534de3476
commit
28e22e1259
1 changed files with 107 additions and 0 deletions
|
@ -272,6 +272,113 @@ where
|
|||
separated.query_builder
|
||||
}
|
||||
|
||||
/// Creates `((a, b), (..)` statements, from `tuples`.
|
||||
///
|
||||
/// This can be used to construct a bulk `SELECT` statement like this:
|
||||
/// ```sql
|
||||
/// SELECT * FROM users WHERE (id, username) IN ((1, "test_user_1"), (2, "test_user_2"))
|
||||
/// ```
|
||||
///
|
||||
/// Although keep in mind that all
|
||||
/// databases have some practical limit on the number of bind arguments in a single query.
|
||||
/// See [`.push_bind()`][Self::push_bind] for details.
|
||||
///
|
||||
/// To be safe, you can do `tuples.into_iter().take(N)` where `N` is the limit for your database
|
||||
/// divided by the number of fields in each tuple; since integer division always rounds down,
|
||||
/// this will ensure that you don't exceed the limit.
|
||||
///
|
||||
/// ### Notes
|
||||
///
|
||||
/// If `tuples` is empty, this will likely produce a syntactically invalid query
|
||||
///
|
||||
/// ### Example (MySQL)
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "mysql")]
|
||||
/// # {
|
||||
/// use sqlx::{Execute, MySql, QueryBuilder};
|
||||
///
|
||||
/// struct User {
|
||||
/// id: i32,
|
||||
/// username: String,
|
||||
/// email: String,
|
||||
/// password: String,
|
||||
/// }
|
||||
///
|
||||
/// // The number of parameters in MySQL must fit in a `u16`.
|
||||
/// const BIND_LIMIT: usize = 65535;
|
||||
///
|
||||
/// // This would normally produce values forever!
|
||||
/// let users = (0..).map(|i| User {
|
||||
/// id: i,
|
||||
/// username: format!("test_user_{}", i),
|
||||
/// email: format!("test-user-{}@example.com", i),
|
||||
/// password: format!("Test!User@Password#{}", i),
|
||||
/// });
|
||||
///
|
||||
/// let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
|
||||
/// // Note the trailing space; most calls to `QueryBuilder` don't automatically insert
|
||||
/// // spaces as that might interfere with identifiers or quoted strings where exact
|
||||
/// // values may matter.
|
||||
/// "SELECT * FROM users WHERE (id, username, email, password) in"
|
||||
/// );
|
||||
///
|
||||
/// // Note that `.into_iter()` wasn't needed here since `users` is already an iterator.
|
||||
/// query_builder.push_tuples(users.take(BIND_LIMIT / 4), |mut b, user| {
|
||||
/// // If you wanted to bind these by-reference instead of by-value,
|
||||
/// // you'd need an iterator that yields references that live as long as `query_builder`,
|
||||
/// // e.g. collect it to a `Vec` first.
|
||||
/// b.push_bind(user.id)
|
||||
/// .push_bind(user.username)
|
||||
/// .push_bind(user.email)
|
||||
/// .push_bind(user.password);
|
||||
/// });
|
||||
///
|
||||
/// let mut query = query_builder.build();
|
||||
///
|
||||
/// // You can then call `query.execute()`, `.fetch_one()`, `.fetch_all()`, etc.
|
||||
/// // For the sake of demonstration though, we're just going to assert the contents
|
||||
/// // of the query.
|
||||
///
|
||||
/// // These are methods of the `Execute` trait, not normally meant to be called in user code.
|
||||
/// let sql = query.sql();
|
||||
/// let arguments = query.take_arguments().unwrap();
|
||||
///
|
||||
/// assert!(sql.starts_with(
|
||||
/// "SELECT * FROM users WHERE (id, username, email, password) in ((?, ?, ?, ?), (?, ?, ?, ?), "
|
||||
/// ));
|
||||
///
|
||||
/// assert!(sql.ends_with("(?, ?, ?, ?)) "));
|
||||
///
|
||||
/// // Not a normally exposed function, only used for this doctest.
|
||||
/// // 65535 / 4 = 16383 (rounded down)
|
||||
/// // 16383 * 4 = 65532
|
||||
/// assert_eq!(arguments.len(), 65532);
|
||||
/// }
|
||||
pub fn push_tuples<I, F>(&mut self, tuples: I, mut push_tuple: F) -> &mut Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
F: FnMut(Separated<'_, 'args, DB, &'static str>, I::Item),
|
||||
{
|
||||
self.sanity_check();
|
||||
|
||||
self.push(" (");
|
||||
|
||||
let mut separated = self.separated(", ");
|
||||
|
||||
for tuple in tuples {
|
||||
separated.push("(");
|
||||
|
||||
// use a `Separated` with a separate (hah) internal state
|
||||
push_tuple(separated.query_builder.separated(", "), tuple);
|
||||
|
||||
separated.push_unseparated(")");
|
||||
}
|
||||
separated.push_unseparated(") ");
|
||||
|
||||
separated.query_builder
|
||||
}
|
||||
|
||||
/// Produce an executable query from this builder.
|
||||
///
|
||||
/// ### Note: Query is not Checked
|
||||
|
|
Loading…
Reference in a new issue