1
1
use std:: {
2
+ collections:: { HashMap , HashSet } ,
2
3
fs,
3
4
io:: { BufRead , BufReader } ,
4
5
path:: Path ,
@@ -316,28 +317,41 @@ fn add_keywords_into_database(
316
317
pkg : & MetadataPackage ,
317
318
release_id : i32 ,
318
319
) -> 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] ) ?;
339
345
}
340
346
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
+
341
355
Ok ( ( ) )
342
356
}
343
357
@@ -347,7 +361,9 @@ pub fn update_crate_data_in_database(
347
361
registry_data : & CrateData ,
348
362
) -> Result < ( ) > {
349
363
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 ) ;
351
367
352
368
update_owners_in_database ( conn, & registry_data. owners , crate_id) ?;
353
369
@@ -360,64 +376,51 @@ fn update_owners_in_database(
360
376
owners : & [ CrateOwner ] ,
361
377
crate_id : i32 ,
362
378
) -> 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" ,
372
389
) ?;
373
- let existing_owners = rows. into_iter ( ) . map ( |row| -> String { row. get ( 0 ) } ) ;
374
-
375
390
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
391
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 ] ,
401
394
) ?;
402
395
}
403
396
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
+ ) ?;
421
424
422
425
Ok ( ( ) )
423
426
}
0 commit comments