Skip to content

Commit 88cac08

Browse files
committed
Resolves rust-lang#530 - add_package logic, make database calls in for loops
1 parent 55c7386 commit 88cac08

File tree

1 file changed

+76
-73
lines changed

1 file changed

+76
-73
lines changed

src/db/add_package.rs

Lines changed: 76 additions & 73 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,28 +317,41 @@ 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-
};
333-
334-
// add releationship
335-
let _ = conn.query(
336-
"INSERT INTO keyword_rels (rid, kid) VALUES ($1, $2)",
337-
&[&release_id, &keyword_id],
338-
);
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+
.collect::<Vec<(_, _)>>()
343+
{
344+
conn.query(&insert_keyword_query, &[&name, &slug])?;
339345
}
340346

347+
conn.query(
348+
"INSERT INTO keyword_rels (rid, kid)
349+
SELECT $1 as rid, id as kid
350+
FROM keywords
351+
WHERE slug = ANY($2)",
352+
&[&release_id, &wanted_keywords.keys().collect::<Vec<_>>()],
353+
)?;
354+
341355
Ok(())
342356
}
343357

@@ -347,7 +361,9 @@ pub fn update_crate_data_in_database(
347361
registry_data: &CrateData,
348362
) -> Result<()> {
349363
info!("Updating crate data for {}", name);
350-
let crate_id = conn.query("SELECT id FROM crates WHERE crates.name = $1", &[&name])?[0].get(0);
364+
let crate_id = conn
365+
.query_one("SELECT id FROM crates WHERE crates.name = $1", &[&name])?
366+
.get(0);
351367

352368
update_owners_in_database(conn, &registry_data.owners, crate_id)?;
353369

@@ -360,64 +376,51 @@ fn update_owners_in_database(
360376
owners: &[CrateOwner],
361377
crate_id: i32,
362378
) -> 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],
379+
// Update any existing owner data since it is mutable and could have changed since last
380+
// time we pulled it
381+
let owner_upsert = conn.prepare(
382+
"INSERT INTO owners (login, avatar, name, email)
383+
VALUES ($1, $2, $3, $4)
384+
ON CONFLICT (login) DO UPDATE
385+
SET
386+
avatar = EXCLUDED.avatar,
387+
name = EXCLUDED.name,
388+
email = EXCLUDED.email",
372389
)?;
373-
let existing_owners = rows.into_iter().map(|row| -> String { row.get(0) });
374-
375390
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
398391
conn.query(
399-
"INSERT INTO owner_rels (cid, oid) VALUES ($1, $2) ON CONFLICT DO NOTHING",
400-
&[&crate_id, &owner_id],
392+
&owner_upsert,
393+
&[&owner.login, &owner.avatar, &owner.name, &owner.email],
401394
)?;
402395
}
403396

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-
}
397+
let updated_oids: Vec<i32> = conn
398+
.query(
399+
"INSERT INTO owner_rels (cid, oid)
400+
SELECT $1,id
401+
FROM owners
402+
WHERE login = ANY($2)
403+
ON CONFLICT (cid,oid)
404+
DO UPDATE -- we need this so the existing/updated records end
405+
-- up being in the returned OIDs
406+
SET oid=excluded.oid
407+
RETURNING oid",
408+
&[
409+
&crate_id,
410+
&owners.iter().map(|o| o.login.clone()).collect::<Vec<_>>(),
411+
],
412+
)?
413+
.iter()
414+
.map(|row| row.get(0))
415+
.collect();
416+
417+
conn.query(
418+
"DELETE FROM owner_rels
419+
WHERE
420+
cid = $1 AND
421+
NOT (oid = ANY($2))",
422+
&[&crate_id, &updated_oids],
423+
)?;
421424

422425
Ok(())
423426
}

0 commit comments

Comments
 (0)