Skip to content

Commit ccadd33

Browse files
authored
Merge pull request #859 from sgrif/sg-port-delete-crate
Port `bin/delete-crate` over to Diesel
2 parents cb8a8a0 + 8817eed commit ccadd33

File tree

8 files changed

+151
-80
lines changed

8 files changed

+151
-80
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
ALTER TABLE "crate_downloads"
2+
DROP CONSTRAINT "fk_crate_downloads_crate_id",
3+
ADD CONSTRAINT "fk_crate_downloads_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
4+
ALTER TABLE "crate_owners"
5+
DROP CONSTRAINT "fk_crate_owners_crate_id",
6+
ADD CONSTRAINT "fk_crate_owners_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
7+
ALTER TABLE "crates_categories"
8+
DROP CONSTRAINT "fk_crates_categories_crate_id",
9+
ADD CONSTRAINT "fk_crates_categories_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
10+
ALTER TABLE "crates_keywords"
11+
DROP CONSTRAINT "fk_crates_keywords_crate_id",
12+
ADD CONSTRAINT "fk_crates_keywords_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
13+
ALTER TABLE "dependencies"
14+
DROP CONSTRAINT "fk_dependencies_crate_id",
15+
ADD CONSTRAINT "fk_dependencies_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
16+
ALTER TABLE "follows"
17+
DROP CONSTRAINT "fk_follows_crate_id",
18+
ADD CONSTRAINT "fk_follows_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
19+
ALTER TABLE "versions"
20+
DROP CONSTRAINT "fk_versions_crate_id",
21+
ADD CONSTRAINT "fk_versions_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
ALTER TABLE "crate_downloads"
2+
DROP CONSTRAINT "fk_crate_downloads_crate_id",
3+
ADD CONSTRAINT "fk_crate_downloads_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
4+
ALTER TABLE "crate_owners"
5+
DROP CONSTRAINT "fk_crate_owners_crate_id",
6+
ADD CONSTRAINT "fk_crate_owners_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
7+
ALTER TABLE "crates_categories"
8+
DROP CONSTRAINT "fk_crates_categories_crate_id",
9+
ADD CONSTRAINT "fk_crates_categories_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
10+
ALTER TABLE "crates_keywords"
11+
DROP CONSTRAINT "fk_crates_keywords_crate_id",
12+
ADD CONSTRAINT "fk_crates_keywords_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
13+
ALTER TABLE "dependencies"
14+
DROP CONSTRAINT "fk_dependencies_crate_id",
15+
ADD CONSTRAINT "fk_dependencies_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
16+
ALTER TABLE "follows"
17+
DROP CONSTRAINT "fk_follows_crate_id",
18+
ADD CONSTRAINT "fk_follows_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
19+
ALTER TABLE "versions"
20+
DROP CONSTRAINT "fk_versions_crate_id",
21+
ADD CONSTRAINT "fk_versions_crate_id" FOREIGN KEY (crate_id) REFERENCES crates(id) ON DELETE CASCADE;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
ALTER TABLE "dependencies"
2+
DROP CONSTRAINT "fk_dependencies_version_id",
3+
ADD CONSTRAINT "fk_dependencies_version_id" FOREIGN KEY (version_id) REFERENCES versions(id);
4+
ALTER TABLE "version_authors"
5+
DROP CONSTRAINT "fk_version_authors_version_id",
6+
ADD CONSTRAINT "fk_version_authors_version_id" FOREIGN KEY (version_id) REFERENCES versions(id);
7+
ALTER TABLE "version_downloads"
8+
DROP CONSTRAINT "fk_version_downloads_version_id",
9+
ADD CONSTRAINT "fk_version_downloads_version_id" FOREIGN KEY (version_id) REFERENCES versions(id);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
ALTER TABLE "dependencies"
2+
DROP CONSTRAINT "fk_dependencies_version_id",
3+
ADD CONSTRAINT "fk_dependencies_version_id" FOREIGN KEY (version_id) REFERENCES versions(id) ON DELETE CASCADE;
4+
ALTER TABLE "version_authors"
5+
DROP CONSTRAINT "fk_version_authors_version_id",
6+
ADD CONSTRAINT "fk_version_authors_version_id" FOREIGN KEY (version_id) REFERENCES versions(id) ON DELETE CASCADE;
7+
ALTER TABLE "version_downloads"
8+
DROP CONSTRAINT "fk_version_downloads_version_id",
9+
ADD CONSTRAINT "fk_version_downloads_version_id" FOREIGN KEY (version_id) REFERENCES versions(id) ON DELETE CASCADE;

src/bin/delete-crate.rs

Lines changed: 13 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,28 @@
88
#![deny(warnings)]
99

1010
extern crate cargo_registry;
11-
extern crate postgres;
11+
extern crate diesel;
1212
extern crate time;
1313

14+
use diesel::prelude::*;
15+
use diesel::pg::PgConnection;
1416
use std::env;
1517
use std::io;
1618
use std::io::prelude::*;
1719

1820
use cargo_registry::Crate;
21+
use cargo_registry::schema::crates;
1922

2023
#[allow(dead_code)]
2124
fn main() {
22-
let conn = cargo_registry::db::connect_now_old();
23-
{
24-
let tx = conn.transaction().unwrap();
25-
delete(&tx);
26-
tx.set_commit();
27-
tx.finish().unwrap();
28-
}
25+
let conn = cargo_registry::db::connect_now().unwrap();
26+
conn.transaction::<_, diesel::result::Error, _>(|| {
27+
delete(&conn);
28+
Ok(())
29+
}).unwrap()
2930
}
3031

31-
fn delete(tx: &postgres::transaction::Transaction) {
32+
fn delete(conn: &PgConnection) {
3233
let name = match env::args().nth(1) {
3334
None => {
3435
println!("needs a crate-name argument");
@@ -37,7 +38,7 @@ fn delete(tx: &postgres::transaction::Transaction) {
3738
Some(s) => s,
3839
};
3940

40-
let krate = Crate::find_by_name(tx, &name).unwrap();
41+
let krate = Crate::by_name(&name).first::<Crate>(conn).unwrap();
4142
print!(
4243
"Are you sure you want to delete {} ({}) [y/N]: ",
4344
name,
@@ -50,77 +51,9 @@ fn delete(tx: &postgres::transaction::Transaction) {
5051
return;
5152
}
5253

53-
let versions = krate.versions(tx).unwrap();
54-
55-
for v in versions.iter() {
56-
println!("deleting version {} ({})", v.num, v.id);
57-
let n = tx.execute(
58-
"DELETE FROM version_downloads WHERE version_id = $1",
59-
&[&v.id],
60-
).unwrap();
61-
println!(" {} download records deleted", n);
62-
let n = tx.execute(
63-
"DELETE FROM version_authors WHERE version_id = $1",
64-
&[&v.id],
65-
).unwrap();
66-
println!(" {} author records deleted", n);
67-
let n = tx.execute("DELETE FROM dependencies WHERE version_id = $1", &[&v.id])
68-
.unwrap();
69-
println!(" {} dependencies deleted", n);
70-
tx.execute("DELETE FROM versions WHERE id = $1", &[&v.id])
71-
.unwrap();
72-
}
73-
74-
println!("deleting follows");
75-
let n = tx.execute("DELETE FROM follows WHERE crate_id = $1", &[&krate.id])
76-
.unwrap();
77-
println!(" {} deleted", n);
78-
79-
println!("deleting crate download records");
80-
let n = tx.execute(
81-
"DELETE FROM crate_downloads WHERE crate_id = $1",
82-
&[&krate.id],
83-
).unwrap();
84-
println!(" {} deleted", n);
85-
86-
println!("deleting crate owners");
87-
let n = tx.execute("DELETE FROM crate_owners WHERE crate_id = $1", &[&krate.id])
88-
.unwrap();
89-
println!(" {} deleted", n);
90-
91-
println!("disabling reserved crate name trigger");
92-
let _ = tx.execute(
93-
"ALTER TABLE crates DISABLE TRIGGER trigger_ensure_crate_name_not_reserved;",
94-
&[],
95-
).unwrap();
96-
97-
println!("deleting crate keyword connections");
98-
let n = tx.execute(
99-
"DELETE FROM crates_keywords WHERE crate_id = $1",
100-
&[&krate.id],
101-
).unwrap();
102-
println!(" {} deleted", n);
103-
104-
println!("deleting crate category connections");
105-
let n = tx.execute(
106-
"DELETE FROM crates_categories WHERE crate_id = $1",
107-
&[&krate.id],
108-
).unwrap();
109-
println!(" {} deleted", n);
110-
111-
println!("enabling reserved crate name trigger");
112-
let _ = tx.execute(
113-
"ALTER TABLE crates ENABLE TRIGGER trigger_ensure_crate_name_not_reserved;",
114-
&[],
115-
).unwrap();
116-
117-
println!("deleting crate badges");
118-
let n = tx.execute("DELETE FROM badges WHERE crate_id = $1", &[&krate.id])
119-
.unwrap();
120-
println!(" {} deleted", n);
121-
12254
println!("deleting the crate");
123-
let n = tx.execute("DELETE FROM crates WHERE id = $1", &[&krate.id])
55+
let n = diesel::delete(crates::table.find(krate.id))
56+
.execute(conn)
12457
.unwrap();
12558
println!(" {} deleted", n);
12659

src/tests/all.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#[macro_use]
44
extern crate serde_derive;
55
extern crate diesel;
6+
#[macro_use]
7+
extern crate diesel_codegen;
68
extern crate bufstream;
79
extern crate cargo_registry;
810
extern crate conduit;
@@ -89,6 +91,7 @@ mod git;
8991
mod keyword;
9092
mod krate;
9193
mod record;
94+
mod schema_details;
9295
mod team;
9396
mod token;
9497
mod user;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- This can't quite use Diesel's query builder yet, because diesel doesn't
2+
-- support custom ON clauses yet. We need the attnum comparison in the join
3+
-- to make sure that we get NULL if no constraint is present
4+
SELECT relname, conname, pg_get_constraintdef(pg_constraint.oid, true)
5+
FROM pg_attribute
6+
INNER JOIN pg_class ON pg_class.oid = attrelid
7+
LEFT JOIN pg_constraint ON pg_class.oid = conrelid AND ARRAY[attnum] = conkey
8+
WHERE attname = $1
9+
AND contype = 'f'

src/tests/schema_details.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use diesel::prelude::*;
2+
3+
#[test]
4+
fn all_columns_called_crate_id_have_a_cascading_foreign_key() {
5+
for (table_name, constraint) in get_fk_constraint_definitions("crate_id") {
6+
let constraint = match constraint {
7+
Some(c) => c,
8+
None => {
9+
panic!(
10+
"Column called crate_id on {} has no foreign key",
11+
table_name
12+
)
13+
}
14+
};
15+
if !constraint.definition.contains("ON DELETE CASCADE") {
16+
panic!(
17+
"Foreign key {} on table {} should have `ON DELETE CASCADE` \
18+
but it doesn't.",
19+
constraint.name,
20+
table_name
21+
);
22+
}
23+
}
24+
}
25+
26+
#[test]
27+
fn all_columns_called_version_id_have_a_cascading_foreign_key() {
28+
for (table_name, constraint) in get_fk_constraint_definitions("version_id") {
29+
let constraint = match constraint {
30+
Some(c) => c,
31+
None => {
32+
panic!(
33+
"Column called version_id on {} has no foreign key",
34+
table_name
35+
)
36+
}
37+
};
38+
if !constraint.definition.contains("ON DELETE CASCADE") {
39+
panic!(
40+
"Foreign key {} on table {} should have `ON DELETE CASCADE` \
41+
but it doesn't.",
42+
constraint.name,
43+
table_name
44+
);
45+
}
46+
}
47+
}
48+
49+
#[derive(Queryable)]
50+
struct FkConstraint {
51+
name: String,
52+
definition: String,
53+
}
54+
55+
fn get_fk_constraint_definitions(column_name: &str) -> Vec<(String, Option<FkConstraint>)> {
56+
use diesel::expression::dsl::sql;
57+
use diesel::types::Text;
58+
59+
let (_r, app, _) = ::app();
60+
let conn = app.diesel_database.get().unwrap();
61+
62+
sql(include_str!("load_foreign_key_constraints.sql"))
63+
.bind::<Text, _>(column_name)
64+
.load(&*conn)
65+
.unwrap()
66+
}

0 commit comments

Comments
 (0)