1
1
use std:: {
2
+ collections:: { HashMap , HashSet } ,
2
3
fs,
3
4
io:: { BufRead , BufReader } ,
4
5
path:: Path ,
@@ -186,15 +187,16 @@ pub(crate) fn add_build_into_database(
186
187
}
187
188
188
189
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" ,
194
197
& [ & pkg. name ] ,
195
- ) ?;
196
- }
197
- Ok ( rows[ 0 ] . get ( 0 ) )
198
+ ) ?
199
+ . get ( 0 ) )
198
200
}
199
201
200
202
/// Convert dependencies into Vec<(String, String, String)>
@@ -316,28 +318,41 @@ fn add_keywords_into_database(
316
318
pkg : & MetadataPackage ,
317
319
release_id : i32 ,
318
320
) -> 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] ) ?;
339
346
}
340
347
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
+
341
356
Ok ( ( ) )
342
357
}
343
358
@@ -347,7 +362,9 @@ pub fn update_crate_data_in_database(
347
362
registry_data : & CrateData ,
348
363
) -> Result < ( ) > {
349
364
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 ) ;
351
368
352
369
update_owners_in_database ( conn, & registry_data. owners , crate_id) ?;
353
370
@@ -360,64 +377,51 @@ fn update_owners_in_database(
360
377
owners : & [ CrateOwner ] ,
361
378
crate_id : i32 ,
362
379
) -> 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" ,
372
390
) ?;
373
- let existing_owners = rows. into_iter ( ) . map ( |row| -> String { row. get ( 0 ) } ) ;
374
-
375
391
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
392
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 ] ,
401
395
) ?;
402
396
}
403
397
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
+ ) ?;
421
425
422
426
Ok ( ( ) )
423
427
}
0 commit comments