Skip to content

Commit f79b97f

Browse files
committed
Resolves rust-lang#530 - makes less database calls in add_package logic
1 parent 84a3db0 commit f79b97f

File tree

1 file changed

+85
-79
lines changed

1 file changed

+85
-79
lines changed

src/db/add_package.rs

Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
collections::{HashMap, HashSet},
23
fs,
34
io::{BufRead, BufReader},
45
path::Path,
@@ -316,26 +317,42 @@ fn add_keywords_into_database(
316317
pkg: &MetadataPackage,
317318
release_id: i32,
318319
) -> Result<()> {
319-
for keyword in &pkg.keywords {
320-
let slug = slugify(&keyword);
321-
let keyword_id: i32 = {
322-
let rows = conn.query("SELECT id FROM keywords WHERE slug = $1", &[&slug])?;
323-
if !rows.is_empty() {
324-
rows[0].get(0)
325-
} else {
326-
conn.query(
327-
"INSERT INTO keywords (name, slug) VALUES ($1, $2) RETURNING id",
328-
&[&keyword, &slug],
329-
)?[0]
330-
.get(0)
331-
}
332-
};
320+
let wanted_keywords: HashMap<String, String> = pkg
321+
.keywords
322+
.iter()
323+
.map(|kw| (slugify(&kw), kw.clone()))
324+
.collect();
325+
326+
let existing_keyword_slugs: HashSet<String> = conn
327+
.query(
328+
"SELECT slug FROM keywords WHERE slug = ANY($1)",
329+
&[&wanted_keywords.keys().collect::<Vec<_>>()],
330+
)?
331+
.iter()
332+
.map(|row| row.get(0))
333+
.collect();
334+
335+
// we create new keywords one-by-one, since most of the time we already have them,
336+
// and because support for multi-record inserts is a mess without adding a new
337+
// library
338+
let insert_keyword_query = conn.prepare("INSERT INTO keywords (name, slug) VALUES ($1, $2)")?;
339+
for (slug, name) in wanted_keywords
340+
.iter()
341+
.filter(|(k, _)| !(existing_keyword_slugs.contains(*k)))
342+
{
343+
conn.execute(&insert_keyword_query, &[&name, &slug])?;
344+
}
345+
346+
let affected_rows = conn.execute(
347+
"INSERT INTO keyword_rels (rid, kid)
348+
SELECT $1 as rid, id as kid
349+
FROM keywords
350+
WHERE slug = ANY($2)",
351+
&[&release_id, &wanted_keywords.keys().collect::<Vec<_>>()],
352+
)?;
333353

334-
// add releationship
335-
let _ = conn.query(
336-
"INSERT INTO keyword_rels (rid, kid) VALUES ($1, $2)",
337-
&[&release_id, &keyword_id],
338-
);
354+
if affected_rows != pkg.keywords.len() as u64 {
355+
failure::bail!("not all keywords added to database");
339356
}
340357

341358
Ok(())
@@ -347,7 +364,9 @@ pub fn update_crate_data_in_database(
347364
registry_data: &CrateData,
348365
) -> Result<()> {
349366
info!("Updating crate data for {}", name);
350-
let crate_id = conn.query("SELECT id FROM crates WHERE crates.name = $1", &[&name])?[0].get(0);
367+
let crate_id = conn
368+
.query_one("SELECT id FROM crates WHERE crates.name = $1", &[&name])?
369+
.get(0);
351370

352371
update_owners_in_database(conn, &registry_data.owners, crate_id)?;
353372

@@ -360,64 +379,51 @@ fn update_owners_in_database(
360379
owners: &[CrateOwner],
361380
crate_id: i32,
362381
) -> Result<()> {
363-
let rows = conn.query(
364-
"
365-
SELECT login
366-
FROM owners
367-
INNER JOIN owner_rels
368-
ON owner_rels.oid = owners.id
369-
WHERE owner_rels.cid = $1
370-
",
371-
&[&crate_id],
382+
// Update any existing owner data since it is mutable and could have changed since last
383+
// time we pulled it
384+
let owner_upsert = conn.prepare(
385+
"INSERT INTO owners (login, avatar, name, email)
386+
VALUES ($1, $2, $3, $4)
387+
ON CONFLICT (login) DO UPDATE
388+
SET
389+
avatar = EXCLUDED.avatar,
390+
name = EXCLUDED.name,
391+
email = EXCLUDED.email",
372392
)?;
373-
let existing_owners = rows.into_iter().map(|row| -> String { row.get(0) });
374-
375393
for owner in owners {
376-
debug!("Updating owner data for {}: {:?}", owner.login, owner);
377-
378-
// Update any existing owner data since it is mutable and could have changed since last
379-
// time we pulled it
380-
let owner_id: i32 = {
381-
conn.query(
382-
"
383-
INSERT INTO owners (login, avatar, name, email)
384-
VALUES ($1, $2, $3, $4)
385-
ON CONFLICT (login) DO UPDATE
386-
SET
387-
avatar = $2,
388-
name = $3,
389-
email = $4
390-
RETURNING id
391-
",
392-
&[&owner.login, &owner.avatar, &owner.name, &owner.email],
393-
)?[0]
394-
.get(0)
395-
};
396-
397-
// add relationship
398-
conn.query(
399-
"INSERT INTO owner_rels (cid, oid) VALUES ($1, $2) ON CONFLICT DO NOTHING",
400-
&[&crate_id, &owner_id],
394+
conn.execute(
395+
&owner_upsert,
396+
&[&owner.login, &owner.avatar, &owner.name, &owner.email],
401397
)?;
402398
}
403399

404-
let to_remove =
405-
existing_owners.filter(|login| !owners.iter().any(|owner| &owner.login == login));
406-
407-
for login in to_remove {
408-
debug!("Removing owner relationship {}", login);
409-
// remove relationship
410-
conn.query(
411-
"
412-
DELETE FROM owner_rels
413-
USING owners
414-
WHERE owner_rels.cid = $1
415-
AND owner_rels.oid = owners.id
416-
AND owners.login = $2
417-
",
418-
&[&crate_id, &login],
419-
)?;
420-
}
400+
let updated_oids: Vec<i32> = conn
401+
.query(
402+
"INSERT INTO owner_rels (cid, oid)
403+
SELECT $1,id
404+
FROM owners
405+
WHERE login = ANY($2)
406+
ON CONFLICT (cid,oid)
407+
DO UPDATE -- we need this so the existing/updated records end
408+
-- up being in the returned OIDs
409+
SET oid=excluded.oid
410+
RETURNING oid",
411+
&[
412+
&crate_id,
413+
&owners.iter().map(|o| o.login.clone()).collect::<Vec<_>>(),
414+
],
415+
)?
416+
.iter()
417+
.map(|row| row.get(0))
418+
.collect();
419+
420+
conn.execute(
421+
"DELETE FROM owner_rels
422+
WHERE
423+
cid = $1 AND
424+
NOT (oid = ANY($2))",
425+
&[&crate_id, &updated_oids],
426+
)?;
421427

422428
Ok(())
423429
}
@@ -427,13 +433,13 @@ fn add_compression_into_database<I>(conn: &mut Client, algorithms: I, release_id
427433
where
428434
I: Iterator<Item = CompressionAlgorithm>,
429435
{
430-
let sql = "
431-
INSERT INTO compression_rels (release, algorithm)
432-
VALUES ($1, $2)
433-
ON CONFLICT DO NOTHING;";
434-
let prepared = conn.prepare(sql)?;
436+
let prepared = conn.prepare(
437+
"INSERT INTO compression_rels (release, algorithm)
438+
VALUES ($1, $2)
439+
ON CONFLICT DO NOTHING;",
440+
)?;
435441
for alg in algorithms {
436-
conn.query(&prepared, &[&release_id, &(alg as i32)])?;
442+
conn.execute(&prepared, &[&release_id, &(alg as i32)])?;
437443
}
438444
Ok(())
439445
}

0 commit comments

Comments
 (0)