I have the following schema in a Postgres database:
Table A {
ID
Name
}
Table B {
ID FOREIGN KEY (A.ID)
}
I'm trying to write the following query in Diesel:
DELETE FROM B
WHERE B.ID in (SELECT ID from A WHERE A.Name = $VAR)
$VAR
is a variable passed by my application.
My first attempt to write this was the following:
fn deleteB(conn: &PgConnection, n: &str) {
use schema::A::dsl::*;
use schema::A;
use schema::B;
let res = A::table
.filter(Name.eq(n))
.select(ID);
.load(conn)
.unwrap();
assert!(res.len() < 2);
let b_id: i32 = *res.iter().nth(1).unwrap_or(&0);
let _rows = diesel::delete(
B::table
.filter(ID.eq(n_id))
)
.execute(conn)
.unwrap();
}
This compiles but it doesn't work: the SELECT
statement for the ID always returns 0. It doesn't match any of the inserted records in A even though I checked by hand they are there. I'm sure I have an error in the way the match is being done (&str
vs &String
maybe?), but I decided to try a different solution since I didn't like this one anyway since it has to execute two separate statements against the database.
My second attempt looks like this:
fn deleteB(conn: &PgConnection, n: &str) {
use schema::A::dsl::*;
use schema::A;
use schema::B;
let source = B::table.inner_join(A::table)
.filter(Name.eq(n));
let s = delete(source)
.execute(conn)
.unwrap();
}
This looks more Diesel-idiomatic to me (spoiler alert, I barely know the framework) but sure enough, it doesn't compile:
|
410 | let s = delete(source)
| ^^^^^^ the trait `diesel::query_builder::IntoUpdateTarget` is not implemented for `diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<mobility2::schema::B::table...` (very long error)
I was looking into the aforementioned trait when I realised this seems like a trivial operation (the delete itself) and I would probably be better off asking for help.
How do I properly write the idiomatic Diesel code to execute the delete statement I want to execute?
As Diesel statements map quite literally to SQL, the query you have written would result in:
DELETE FROM TABLE B INNER JOIN TABLE A ON … WHERE name = $1
That's not valid SQL, so it results in a compile time error.
To get the query you want to write, you need to do something like:
#[macro_use]
extern crate diesel;
use diesel::delete;
use diesel::prelude::*;
mod schema {
table! {
A(ID) {
ID -> Integer,
name -> Text,
}
}
table! {
B(ID) {
ID -> Integer,
}
}
allow_tables_to_appear_in_same_query!(A, B);
}
fn deleteB(conn: &PgConnection, n: &str) {
use schema::A;
use schema::B;
let s = delete(B::table)
.filter(B::ID.eq_any(A::table.filter(A::name.eq(n)).select(A::ID)))
.execute(conn)
.unwrap();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With