Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to use dynamic query in `sqlx::query!()"

Tags:

rust

rust-sqlx

Context: rust, library sqlx

Question: how to compose similar queries from smaller parts, without losing type check?

macro_rules! select {
  () => {"select col from tab"}
}

macro_rules! guard1 {
  () => {"where col > 1"}
}

macro_rules! guard2 {
  () => {"where col > 2"}
}

let mut conn = get_pg_connection_from_somewhere();

if some_condition() {
  sqlx::query!(select!() + guard1!()).fetch_all(&mut conn).await?;
} else {
  sqlx::query!(select!() + guard2!()).fetch_all(&mut conn).await?;
}

The documentation says that:

The query must be a string literal, or concatenation of string literals using + (useful for queries generated by macro), or else it cannot be introspected (and thus cannot be dynamic or the result of another macro).

You can see that it says that "useful for queries generated by macro" (although later "cannot be the result of another macro")

The problem I want to solve is that, depending on the condition, I want to run different but similar (e.g. they all have the same columns) queries, and I want to compose the query from smaller reusable parts[

like image 715
Incömplete Avatar asked Nov 07 '22 04:11

Incömplete


1 Answers

You can do this using a macro like below

macro_rules! fetch_all_todos_with_predicate {
    ($executor:ident, $predicate:tt) => {
        sqlx::query!(
            "SELECT id, title, completed_at FROM todos " + $predicate
        ).fetch_all($executor).await
    }
}

And provide the predicate based on some condition

let kind = QueryKind::Complete;
let mut conn = get_pg_connection_from_somewhere();

let result = match kind {
    QueryKind::Complete => fetch_all_todos_with_predicate!(conn, "WHERE completed_at IS NOT NULL"),
    QueryKind::Incomplete => fetch_all_todos_with_predicate!(conn, "WHERE completed_at IS NULL"),
    QueryKind::All => fetch_all_todos_with_predicate!(conn, ""),
};
like image 121
Tiberiu Avatar answered Dec 10 '22 02:12

Tiberiu