1
1
use std:: {
2
+ collections:: { HashMap , HashSet } ,
2
3
fs,
3
4
io:: { BufRead , BufReader } ,
4
5
path:: Path ,
@@ -316,26 +317,42 @@ 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
- } ;
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
+ ) ?;
333
353
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" ) ;
339
356
}
340
357
341
358
Ok ( ( ) )
@@ -347,7 +364,9 @@ pub fn update_crate_data_in_database(
347
364
registry_data : & CrateData ,
348
365
) -> Result < ( ) > {
349
366
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 ) ;
351
370
352
371
update_owners_in_database ( conn, & registry_data. owners , crate_id) ?;
353
372
@@ -360,64 +379,51 @@ fn update_owners_in_database(
360
379
owners : & [ CrateOwner ] ,
361
380
crate_id : i32 ,
362
381
) -> 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" ,
372
392
) ?;
373
- let existing_owners = rows. into_iter ( ) . map ( |row| -> String { row. get ( 0 ) } ) ;
374
-
375
393
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 ] ,
401
397
) ?;
402
398
}
403
399
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
+ ) ?;
421
427
422
428
Ok ( ( ) )
423
429
}
@@ -427,13 +433,13 @@ fn add_compression_into_database<I>(conn: &mut Client, algorithms: I, release_id
427
433
where
428
434
I : Iterator < Item = CompressionAlgorithm > ,
429
435
{
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
+ ) ?;
435
441
for alg in algorithms {
436
- conn. query ( & prepared, & [ & release_id, & ( alg as i32 ) ] ) ?;
442
+ conn. execute ( & prepared, & [ & release_id, & ( alg as i32 ) ] ) ?;
437
443
}
438
444
Ok ( ( ) )
439
445
}
0 commit comments