Skip to content

Commit 12e3436

Browse files
authored
Merge pull request #9911 from Turbo87/delete-crate-derive
admin/delete_crate: Simplify SQL query code by deriving `Queryable` and `Selectable`
2 parents 5e3f0ab + 3aeff80 commit 12e3436

File tree

1 file changed

+50
-61
lines changed

1 file changed

+50
-61
lines changed

src/bin/crates-admin/delete_crate.rs

Lines changed: 50 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ use crates_io::worker::jobs;
66
use crates_io::{db, schema::crates};
77
use crates_io_worker::BackgroundJob;
88
use diesel::dsl::sql;
9+
use diesel::expression::SqlLiteral;
10+
use diesel::prelude::*;
911
use diesel::sql_types::{Array, BigInt, Text};
10-
use diesel::{ExpressionMethods, QueryDsl};
1112
use diesel_async::RunQueryDsl;
12-
use futures_util::TryStreamExt;
13-
use std::collections::HashMap;
1413
use std::fmt::Display;
1514

1615
#[derive(clap::Parser, Debug)]
@@ -40,56 +39,15 @@ pub async fn run(opts: Opts) -> anyhow::Result<()> {
4039
let existing_crates = crates::table
4140
.inner_join(crate_downloads::table)
4241
.filter(crates::name.eq_any(&crate_names))
43-
.select((
44-
crates::name,
45-
crates::id,
46-
crate_downloads::downloads,
47-
sql::<Array<Text>>(
48-
r#"
49-
ARRAY(
50-
SELECT
51-
CASE WHEN crate_owners.owner_kind = 1 THEN
52-
teams.login
53-
ELSE
54-
users.gh_login
55-
END
56-
FROM crate_owners
57-
LEFT JOIN teams ON teams.id = crate_owners.owner_id
58-
LEFT JOIN users ON users.id = crate_owners.owner_id
59-
WHERE crate_owners.crate_id = crates.id
60-
)
61-
"#,
62-
),
63-
sql::<BigInt>(
64-
// This is an incorrect reverse dependencies query, since it
65-
// includes the `dependencies` rows for all versions, not just
66-
// the "default version" per crate. However, it's good enough
67-
// for our purposes here.
68-
r#"
69-
(
70-
SELECT COUNT(*)
71-
FROM dependencies
72-
WHERE dependencies.crate_id = crates.id
73-
)
74-
"#,
75-
),
76-
))
77-
.load_stream::<(String, i32, i64, Vec<String>, i64)>(&mut conn)
42+
.select(CrateInfo::as_select())
43+
.load::<CrateInfo>(&mut conn)
7844
.await
79-
.context("Failed to look up crate name from the database")?
80-
.try_fold(
81-
HashMap::new(),
82-
|mut map, (name, id, downloads, owners, rev_deps)| {
83-
map.insert(name, CrateInfo::new(id, downloads, owners, rev_deps));
84-
futures_util::future::ready(Ok(map))
85-
},
86-
)
87-
.await?;
45+
.context("Failed to look up crate name from the database")?;
8846

8947
println!("Deleting the following crates:");
9048
println!();
9149
for name in &crate_names {
92-
match existing_crates.get(name) {
50+
match existing_crates.iter().find(|info| info.name == *name) {
9351
Some(info) => println!(" - {} ({info})", name.bold()),
9452
None => println!(" - {name} (⚠️ crate not found)"),
9553
}
@@ -103,7 +61,7 @@ pub async fn run(opts: Opts) -> anyhow::Result<()> {
10361
}
10462

10563
for name in &crate_names {
106-
if let Some(crate_info) = existing_crates.get(name) {
64+
if let Some(crate_info) = existing_crates.iter().find(|info| info.name == *name) {
10765
let id = crate_info.id;
10866

10967
info!("{name}: Deleting crate from the database…");
@@ -138,25 +96,20 @@ pub async fn run(opts: Opts) -> anyhow::Result<()> {
13896
Ok(())
13997
}
14098

141-
#[derive(Debug, Clone)]
99+
#[derive(Debug, Clone, Queryable, Selectable)]
142100
struct CrateInfo {
101+
#[diesel(select_expression = crates::columns::name)]
102+
name: String,
103+
#[diesel(select_expression = crates::columns::id)]
143104
id: i32,
105+
#[diesel(select_expression = crate_downloads::columns::downloads)]
144106
downloads: i64,
107+
#[diesel(select_expression = owners_subquery())]
145108
owners: Vec<String>,
109+
#[diesel(select_expression = rev_deps_subquery())]
146110
rev_deps: i64,
147111
}
148112

149-
impl CrateInfo {
150-
pub fn new(id: i32, downloads: i64, owners: Vec<String>, rev_deps: i64) -> Self {
151-
Self {
152-
id,
153-
downloads,
154-
owners,
155-
rev_deps,
156-
}
157-
}
158-
}
159-
160113
impl Display for CrateInfo {
161114
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162115
let id = self.id;
@@ -175,3 +128,39 @@ impl Display for CrateInfo {
175128
Ok(())
176129
}
177130
}
131+
132+
/// A subquery that returns the owners of a crate as an array of strings.
133+
#[diesel::dsl::auto_type]
134+
fn owners_subquery() -> SqlLiteral<Array<Text>> {
135+
sql(r#"
136+
ARRAY(
137+
SELECT
138+
CASE WHEN crate_owners.owner_kind = 1 THEN
139+
teams.login
140+
ELSE
141+
users.gh_login
142+
END
143+
FROM crate_owners
144+
LEFT JOIN teams ON teams.id = crate_owners.owner_id
145+
LEFT JOIN users ON users.id = crate_owners.owner_id
146+
WHERE crate_owners.crate_id = crates.id
147+
)
148+
"#)
149+
}
150+
151+
/// A subquery that returns the number of reverse dependencies of a crate.
152+
///
153+
/// **Warning:** this is an incorrect reverse dependencies query, since it
154+
/// includes the `dependencies` rows for all versions, not just the
155+
/// "default version" per crate. However, it's good enough for our
156+
/// purposes here.
157+
#[diesel::dsl::auto_type]
158+
fn rev_deps_subquery() -> SqlLiteral<BigInt> {
159+
sql(r#"
160+
(
161+
SELECT COUNT(*)
162+
FROM dependencies
163+
WHERE dependencies.crate_id = crates.id
164+
)
165+
"#)
166+
}

0 commit comments

Comments
 (0)