Skip to content

Commit 6004f5e

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

File tree

1 file changed

+86
-79
lines changed

1 file changed

+86
-79
lines changed

src/db/add_package.rs

Lines changed: 86 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,43 @@ 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+
.collect::<Vec<(_, _)>>()
343+
{
344+
conn.execute(&insert_keyword_query, &[&name, &slug])?;
345+
}
346+
347+
let affected_rows = conn.execute(
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+
)?;
333354

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

341359
Ok(())
@@ -347,7 +365,9 @@ pub fn update_crate_data_in_database(
347365
registry_data: &CrateData,
348366
) -> Result<()> {
349367
info!("Updating crate data for {}", name);
350-
let crate_id = conn.query("SELECT id FROM crates WHERE crates.name = $1", &[&name])?[0].get(0);
368+
let crate_id = conn
369+
.query_one("SELECT id FROM crates WHERE crates.name = $1", &[&name])?
370+
.get(0);
351371

352372
update_owners_in_database(conn, &registry_data.owners, crate_id)?;
353373

@@ -360,64 +380,51 @@ fn update_owners_in_database(
360380
owners: &[CrateOwner],
361381
crate_id: i32,
362382
) -> 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],
383+
// Update any existing owner data since it is mutable and could have changed since last
384+
// time we pulled it
385+
let owner_upsert = conn.prepare(
386+
"INSERT INTO owners (login, avatar, name, email)
387+
VALUES ($1, $2, $3, $4)
388+
ON CONFLICT (login) DO UPDATE
389+
SET
390+
avatar = EXCLUDED.avatar,
391+
name = EXCLUDED.name,
392+
email = EXCLUDED.email",
372393
)?;
373-
let existing_owners = rows.into_iter().map(|row| -> String { row.get(0) });
374-
375394
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],
395+
conn.execute(
396+
&owner_upsert,
397+
&[&owner.login, &owner.avatar, &owner.name, &owner.email],
401398
)?;
402399
}
403400

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

422429
Ok(())
423430
}
@@ -427,13 +434,13 @@ fn add_compression_into_database<I>(conn: &mut Client, algorithms: I, release_id
427434
where
428435
I: Iterator<Item = CompressionAlgorithm>,
429436
{
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)?;
437+
let prepared = conn.prepare(
438+
"INSERT INTO compression_rels (release, algorithm)
439+
VALUES ($1, $2)
440+
ON CONFLICT DO NOTHING;",
441+
)?;
435442
for alg in algorithms {
436-
conn.query(&prepared, &[&release_id, &(alg as i32)])?;
443+
conn.execute(&prepared, &[&release_id, &(alg as i32)])?;
437444
}
438445
Ok(())
439446
}

0 commit comments

Comments
 (0)