My function takes multiple filters, I construct them to a WHERE string, but seems I cannot pass it to tokio_postgres method
WHERE user_id = 10 AND
username = 'test' AND
created_at >= '2021-01-02' AND
created_at <= '2021-05-01'
I tried
where_condition = "user_id = 10 AND username = 'test' AND created_at >= '2021-01-02' AND created_at <= '2021-05-01'"
client.query("SELECT * from users WHERE $1", &[&where_condition])
got this error
panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: ToSql(0), cause: Some(WrongType { postgres: Bool, rust: "alloc::string::String" }) }'
seem I had to explicitly define the columns like below
client.query("SELECT * from users WHERE user_id = $1 AND username = $2", &[&user_id, &username])
That seems a rather complicated way to construct the statements if my filter list is long. I don't know if it's possible to do what I want or not.
Parameter interpolation is handled by the Postgres server, not by tokio-postgres. As @alice noted, you should dynamically construct the query string with parameters for all of the actual values.
You can still get safe queries, you just format the query string and not the parameters, as @alice and @sfackler noted.
This is how we do it in one of our applications (this is rusqlite -- which is modeled after rust-postgres). It doesn't do exactly what you're doing, but the same technique can be used:
pub(crate) fn add_peer(
conn: &mut Connection,
npi: &NewPeerInfo
) -> Result<i64, Error> {
let mut names = Vec::new();
let mut params = Vec::new();
let mut vals: Vec<&dyn ToSql> = Vec::new();
names.push("name".to_string());
params.push("?".to_string());
vals.push(&npi.name);
names.push("cert".to_string());
params.push("?".to_string());
vals.push(&npi.cert);
if let Some(addr) = npi.addr.as_ref() {
names.push("addr".to_string());
params.push("?".to_string());
vals.push(addr);
}
let q = format!(
"INSERT INTO peers ({}) VALUES ({});",
names.join(","),
params.join(",")
);
self.conn.execute(&q, &vals[..])?;
let last_id = self.conn.last_insert_rowid();
Ok(last_id)
}
Note that you don't actually need to build the params at each step unless you want to do some special handling. Instead you can construct it based on names: