diff --git a/README.MD b/README.MD index 16c7f98..2404504 100644 --- a/README.MD +++ b/README.MD @@ -11,4 +11,121 @@ ### note -- 展开宏:cargo expand --manifest-path .\server\Cargo.toml controller::feedback_controller \ No newline at end of file +- 展开宏:cargo expand --manifest-path .\server\Cargo.toml controller::feedback_controller + +### sql dynamic query +https://github.com/launchbadge/sqlx/blob/main/FAQ.md#how-can-i-bind-an-array-to-a-values-clause-how-can-i-do-bulk-inserts +```rust +use sqlx::{query_builder::QueryBuilder, Execute}; + +struct Search { + id: i64, + username: Option, + min_age: Option, + max_age: Option, +} + +fn search_query(search: Search) -> String { + let mut query = QueryBuilder::new("SELECT * from users where id = "); + query.push_bind(search.id); + + if let Some(username) = search.username { + query.push(" AND username = "); + query.push_bind(username); + } + + if let Some(min_age) = search.min_age { + query.push(" AND age > "); + query.push_bind(min_age); + } + + if let Some(max_age) = search.max_age { + query.push(" AND age < "); + query.push_bind(max_age); + } + + query.build().sql().into() +} + +fn main() { + dbg!(search_query(Search { + id: 12, + username: None, + min_age: None, + max_age: None, + })); // "SELECT * from users where id = $1" + dbg!(search_query(Search { + id: 12, + username: Some("Bob".into()), + min_age: None, + max_age: None, + })); // "SELECT * from users where id = $1 AND username = $2" + dbg!(search_query(Search { + id: 12, + username: Some("Bob".into()), + min_age: Some(10), + max_age: Some(70), + })); // "SELECT * from users where id = $1 AND username = $2 AND age > $3 AND age < $4" +} +``` + +```rust +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-{i}@example.com"), + password: format!("Test!User@Password#{i}"), +}); + +let mut query_builder: QueryBuilder = 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. + "INSERT INTO users(id, username, email, password) " +); + +// Note that `.into_iter()` wasn't needed here since `users` is already an iterator. +query_builder.push_values(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( + "INSERT INTO users(id, username, email, password) VALUES (?, ?, ?, ?), (?, ?, ?, ?)" +)); + +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); +``` \ No newline at end of file