Skip to content

Commit f294eee

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

File tree

1 file changed

+85
-81
lines changed

1 file changed

+85
-81
lines changed

src/db/add_package.rs

Lines changed: 85 additions & 81 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,
@@ -186,15 +187,16 @@ pub(crate) fn add_build_into_database(
186187
}
187188

188189
fn initialize_package_in_database(conn: &mut Client, pkg: &MetadataPackage) -> Result<i32> {
189-
let mut rows = conn.query("SELECT id FROM crates WHERE name = $1", &[&pkg.name])?;
190-
// insert crate into database if it is not exists
191-
if rows.is_empty() {
192-
rows = conn.query(
193-
"INSERT INTO crates (name) VALUES ($1) RETURNING id",
190+
Ok(conn
191+
.query_one(
192+
"INSERT INTO crates (name)
193+
VALUES ($1)
194+
ON CONFLICT (name) DO UPDATE
195+
SET name = EXCLUDED.name
196+
RETURNING id",
194197
&[&pkg.name],
195-
)?;
196-
}
197-
Ok(rows[0].get(0))
198+
)?
199+
.get(0))
198200
}
199201

200202
/// Convert dependencies into Vec<(String, String, String)>
@@ -316,28 +318,41 @@ fn add_keywords_into_database(
316318
pkg: &MetadataPackage,
317319
release_id: i32,
318320
) -> 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-
);
321+
let wanted_keywords: HashMap<String, String> = pkg
322+
.keywords
323+
.iter()
324+
.map(|kw| (slugify(&kw), kw.clone()))
325+
.collect();
326+
327+
let existing_keyword_slugs: HashSet<String> = conn
328+
.query(
329+
"SELECT slug FROM keywords WHERE slug = ANY($1)",
330+
&[&wanted_keywords.keys().collect::<Vec<_>>()],
331+
)?
332+
.iter()
333+
.map(|row| row.get(0))
334+
.collect();
335+
336+
// we create new keywords one-by-one, since most of the time we already have them,
337+
// and because support for multi-record inserts is a mess without adding a new
338+
// library
339+
let insert_keyword_query = conn.prepare("INSERT INTO keywords (name, slug) VALUES ($1, $2)")?;
340+
for (slug, name) in wanted_keywords
341+
.iter()
342+
.filter(|(k, _)| !(existing_keyword_slugs.contains(*k)))
343+
.collect::<Vec<(_, _)>>()
344+
{
345+
conn.query(&insert_keyword_query, &[&name, &slug])?;
339346
}
340347

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

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

352369
update_owners_in_database(conn, &registry_data.owners, crate_id)?;
353370

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

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

422426
Ok(())
423427
}

0 commit comments

Comments
 (0)