11//! Core database repository for installed packages.
22
3- use diesel:: prelude:: * ;
3+ use diesel:: { prelude:: * , sql_types :: Bool , sqlite :: Sqlite } ;
44
55use crate :: {
66 models:: {
@@ -366,6 +366,7 @@ impl CoreRepository {
366366 }
367367
368368 /// Updates an installed package after successful installation.
369+ /// Only updates the record with is_installed=false (the newly created one).
369370 #[ allow( clippy:: too_many_arguments) ]
370371 pub fn record_installation (
371372 conn : & mut SqliteConnection ,
@@ -378,14 +379,16 @@ impl CoreRepository {
378379 with_pkg_id : bool ,
379380 checksum : Option < & str > ,
380381 installed_date : & str ,
382+ installed_path : & str ,
381383 ) -> QueryResult < Option < i32 > > {
382384 let provides = provides. map ( |v| serde_json:: to_value ( v) . unwrap_or_default ( ) ) ;
383385 diesel:: update (
384386 packages:: table
385387 . filter ( packages:: repo_name. eq ( repo_name) )
386388 . filter ( packages:: pkg_name. eq ( pkg_name) )
387389 . filter ( packages:: pkg_id. eq ( pkg_id) )
388- . filter ( packages:: version. eq ( version) ) ,
390+ . filter ( packages:: version. eq ( version) )
391+ . filter ( packages:: is_installed. eq ( false ) ) ,
389392 )
390393 . set ( (
391394 packages:: size. eq ( size) ,
@@ -394,6 +397,7 @@ impl CoreRepository {
394397 packages:: provides. eq ( provides) ,
395398 packages:: with_pkg_id. eq ( with_pkg_id) ,
396399 packages:: checksum. eq ( checksum) ,
400+ packages:: installed_path. eq ( installed_path) ,
397401 ) )
398402 . returning ( packages:: id)
399403 . get_result ( conn)
@@ -459,6 +463,54 @@ impl CoreRepository {
459463 diesel:: delete ( packages:: table. filter ( packages:: id. eq ( id) ) ) . execute ( conn)
460464 }
461465
466+ /// Checks if a pending install (is_installed=false) exists for a specific package version.
467+ /// Used to check if we can resume a partial install.
468+ pub fn has_pending_install (
469+ conn : & mut SqliteConnection ,
470+ pkg_id : & str ,
471+ pkg_name : & str ,
472+ repo_name : & str ,
473+ version : & str ,
474+ ) -> QueryResult < bool > {
475+ let count: i64 = packages:: table
476+ . filter ( packages:: pkg_id. eq ( pkg_id) )
477+ . filter ( packages:: pkg_name. eq ( pkg_name) )
478+ . filter ( packages:: repo_name. eq ( repo_name) )
479+ . filter ( packages:: version. eq ( version) )
480+ . filter ( packages:: is_installed. eq ( false ) )
481+ . count ( )
482+ . get_result ( conn) ?;
483+ Ok ( count > 0 )
484+ }
485+
486+ /// Deletes pending (is_installed=false) records for a package and returns their paths.
487+ /// Used to clean up orphaned partial installs before starting a new install.
488+ pub fn delete_pending_installs (
489+ conn : & mut SqliteConnection ,
490+ pkg_id : & str ,
491+ pkg_name : & str ,
492+ repo_name : & str ,
493+ ) -> QueryResult < Vec < String > > {
494+ let paths: Vec < String > = packages:: table
495+ . filter ( packages:: pkg_id. eq ( pkg_id) )
496+ . filter ( packages:: pkg_name. eq ( pkg_name) )
497+ . filter ( packages:: repo_name. eq ( repo_name) )
498+ . filter ( packages:: is_installed. eq ( false ) )
499+ . select ( packages:: installed_path)
500+ . load ( conn) ?;
501+
502+ diesel:: delete (
503+ packages:: table
504+ . filter ( packages:: pkg_id. eq ( pkg_id) )
505+ . filter ( packages:: pkg_name. eq ( pkg_name) )
506+ . filter ( packages:: repo_name. eq ( repo_name) )
507+ . filter ( packages:: is_installed. eq ( false ) ) ,
508+ )
509+ . execute ( conn) ?;
510+
511+ Ok ( paths)
512+ }
513+
462514 /// Gets the portable package configuration for a package.
463515 pub fn get_portable (
464516 conn : & mut SqliteConnection ,
@@ -525,13 +577,15 @@ impl CoreRepository {
525577 . execute ( conn)
526578 }
527579
528- /// Gets old package versions (all except the newest unpinned one) for cleanup.
580+ /// Gets old package versions (all except the newest one) for cleanup.
529581 /// Returns the installed paths of packages to remove.
582+ /// If `force` is true, includes pinned packages. Otherwise only unpinned packages.
530583 pub fn get_old_package_paths (
531584 conn : & mut SqliteConnection ,
532585 pkg_id : & str ,
533586 pkg_name : & str ,
534587 repo_name : & str ,
588+ force : bool ,
535589 ) -> QueryResult < Vec < ( i32 , String ) > > {
536590 let latest: Option < ( i32 , String ) > = packages:: table
537591 . filter ( packages:: pkg_id. eq ( pkg_id) )
@@ -546,23 +600,33 @@ impl CoreRepository {
546600 return Ok ( Vec :: new ( ) ) ;
547601 } ;
548602
549- packages:: table
603+ let query = packages:: table
550604 . filter ( packages:: pkg_id. eq ( pkg_id) )
551605 . filter ( packages:: pkg_name. eq ( pkg_name) )
552606 . filter ( packages:: repo_name. eq ( repo_name) )
553- . filter ( packages:: pinned. eq ( false ) )
554607 . filter ( packages:: id. ne ( latest_id) )
555608 . filter ( packages:: installed_path. ne ( & latest_path) )
609+ . into_boxed ( ) ;
610+
611+ let query = if force {
612+ query
613+ } else {
614+ query. filter ( packages:: pinned. eq ( false ) )
615+ } ;
616+
617+ query
556618 . select ( ( packages:: id, packages:: installed_path) )
557619 . load ( conn)
558620 }
559621
560- /// Deletes old package versions (all except the newest unpinned one).
622+ /// Deletes old package versions (all except the newest one).
623+ /// If `force` is true, deletes pinned packages too. Otherwise only unpinned packages.
561624 pub fn delete_old_packages (
562625 conn : & mut SqliteConnection ,
563626 pkg_id : & str ,
564627 pkg_name : & str ,
565628 repo_name : & str ,
629+ force : bool ,
566630 ) -> QueryResult < usize > {
567631 let latest_id: Option < i32 > = packages:: table
568632 . filter ( packages:: pkg_id. eq ( pkg_id) )
@@ -577,15 +641,21 @@ impl CoreRepository {
577641 return Ok ( 0 ) ;
578642 } ;
579643
580- diesel:: delete (
581- packages:: table
582- . filter ( packages:: pkg_id. eq ( pkg_id) )
583- . filter ( packages:: pkg_name. eq ( pkg_name) )
584- . filter ( packages:: repo_name. eq ( repo_name) )
585- . filter ( packages:: pinned. eq ( false ) )
586- . filter ( packages:: id. ne ( latest_id) ) ,
587- )
588- . execute ( conn)
644+ let pinned_filter: Box < dyn BoxableExpression < packages:: table , Sqlite , SqlType = Bool > > =
645+ if force {
646+ Box :: new ( diesel:: dsl:: sql :: < Bool > ( "TRUE" ) )
647+ } else {
648+ Box :: new ( packages:: pinned. eq ( false ) )
649+ } ;
650+
651+ let query = packages:: table
652+ . filter ( packages:: pkg_id. eq ( pkg_id) )
653+ . filter ( packages:: pkg_name. eq ( pkg_name) )
654+ . filter ( packages:: repo_name. eq ( repo_name) )
655+ . filter ( packages:: id. ne ( latest_id) )
656+ . filter ( pinned_filter) ;
657+
658+ diesel:: delete ( query) . execute ( conn)
589659 }
590660
591661 /// Unlinks all packages with a given name except those matching pkg_id and checksum.
0 commit comments