@@ -705,6 +705,14 @@ func formatStringArray(s []string) string {
705705 return strings .Join (s , "','" )
706706}
707707
708+ func formatStringArrayFromUUIDs (uuids []uuid.UUID ) string {
709+ var s []string
710+ for _ , uuid := range uuids {
711+ s = append (s , uuid .String ())
712+ }
713+ return strings .Join (s , "','" )
714+ }
715+
708716func (ms * sqliteMatcherStore ) GetEnrichment (ctx context.Context , kind string , tags []string ) ([]driver.EnrichmentRecord , error ) {
709717 var query = `
710718 WITH
@@ -765,6 +773,11 @@ func (ms *sqliteMatcherStore) GetEnrichment(ctx context.Context, kind string, ta
765773 if err := rows .Err (); err != nil {
766774 return nil , err
767775 }
776+
777+ if err := tx .Commit (); err != nil {
778+ return nil , fmt .Errorf ("failed to commit tx: %v" , err )
779+ }
780+
768781 return results , nil
769782}
770783
@@ -828,7 +841,9 @@ func (ms *sqliteMatcherStore) GetUpdateOperations(ctx context.Context, kind driv
828841 q = queryVulnerability
829842 }
830843
831- rows , err := tx .QueryContext (ctx , q , formatStringArray (updater ))
844+ // FIXME: This is REALLY ugly!
845+ fmt_q := fmt .Sprintf (strings .Replace (q , "$1" , "'%s'" , 1 ), formatStringArray (updater ))
846+ rows , err := tx .QueryContext (ctx , fmt_q )
832847 switch {
833848 case err == nil :
834849 case errors .Is (err , pgx .ErrNoRows ):
@@ -855,6 +870,9 @@ func (ms *sqliteMatcherStore) GetUpdateOperations(ctx context.Context, kind driv
855870 if err := rows .Err (); err != nil {
856871 return nil , err
857872 }
873+ if err := tx .Commit (); err != nil {
874+ return nil , fmt .Errorf ("failed to commit tx: %v" , err )
875+ }
858876
859877 return out , nil
860878}
@@ -889,8 +907,22 @@ func (ms *sqliteMatcherStore) GetUpdateDiff(ctx context.Context, prev uuid.UUID,
889907//
890908// The number of UpdateOperations deleted is returned.
891909func (ms * sqliteMatcherStore ) DeleteUpdateOperations (ctx context.Context , uuids ... uuid.UUID ) (int64 , error ) {
892- zlog .Warn (ctx ).Msg ("sqliteMatcherStore.DeleteUpdateOperations is not implemented!" )
893- return 0 , nil
910+ const query = `DELETE FROM update_operation WHERE ref IN ($1);`
911+ ctx = zlog .ContextWithValues (ctx , "component" , "internal/vulnstore/sqlite/deleteUpdateOperations" )
912+
913+ if len (uuids ) == 0 {
914+ return 0 , nil
915+ }
916+
917+ // FIXME: This is REALLY ugly!
918+ fmt_q := fmt .Sprintf (strings .Replace (query , "$1" , "'%s'" , 1 ), formatStringArrayFromUUIDs (uuids ))
919+ tag , err := ms .conn .ExecContext (ctx , fmt_q )
920+ if err != nil {
921+ zlog .Error (ctx ).Err (err ).Msg ("ExecContext" )
922+ return 0 , fmt .Errorf ("failed to delete: %w" , err )
923+ }
924+
925+ return tag .RowsAffected ()
894926}
895927
896928// GC will delete any update operations for an updater which exceeds the provided keep
@@ -901,8 +933,184 @@ func (ms *sqliteMatcherStore) DeleteUpdateOperations(ctx context.Context, uuids
901933// The returned int64 value indicates the remaining number of update operations needing GC.
902934// Running this method till the returned value is 0 accomplishes a full GC of the vulnstore.
903935func (ms * sqliteMatcherStore ) GC (ctx context.Context , count int ) (int64 , error ) {
904- zlog .Warn (ctx ).Msg ("sqliteMatcherStore.GC is not implemented!" )
905- return 0 , nil
936+ ctx = zlog .ContextWithValues (ctx , "component" , "datastore/sqlite/GC" )
937+
938+ // obtain update operations which need deletin'
939+ ops , totalOps , err := eligibleUpdateOpts (ctx , ms .conn , count )
940+ if err != nil {
941+ return 0 , err
942+ }
943+
944+ deletedOps , err := ms .DeleteUpdateOperations (ctx , ops ... )
945+ if err != nil {
946+ zlog .Error (ctx ).Err (err ).Msg ("DeleteUpdateOperations" )
947+ return totalOps - deletedOps , err
948+ }
949+
950+ // get all updaters we know about.
951+ updaters , err := distinctUpdaters (ctx , ms .conn )
952+ if err != nil {
953+ return totalOps - deletedOps , err
954+ }
955+
956+ for kind , us := range updaters {
957+ var cleanup cleanupFunc
958+ switch kind {
959+ case driver .VulnerabilityKind :
960+ cleanup = vulnCleanup
961+ case driver .EnrichmentKind :
962+ cleanup = enrichmentCleanup
963+ default :
964+ zlog .Error (ctx ).Str ("kind" , string (kind )).Msg ("unknown updater kind; skipping cleanup" )
965+ continue
966+ }
967+ for _ , u := range us {
968+ err := cleanup (ctx , ms .conn , u )
969+ if err != nil {
970+ return totalOps - deletedOps , err
971+ }
972+ }
973+ }
974+
975+ return totalOps - deletedOps , nil
976+ }
977+
978+ // distinctUpdaters returns all updaters which have registered an update
979+ // operation.
980+ func distinctUpdaters (ctx context.Context , conn * sql.DB ) (map [driver.UpdateKind ][]string , error ) {
981+ const (
982+ // will always contain at least two update operations
983+ selectUpdaters = `SELECT DISTINCT(updater), kind FROM update_operation;`
984+ )
985+ rows , err := conn .QueryContext (ctx , selectUpdaters )
986+ if err != nil {
987+ return nil , fmt .Errorf ("error selecting distinct updaters: %v" , err )
988+ }
989+ defer rows .Close ()
990+
991+ updaters := make (map [driver.UpdateKind ][]string )
992+ for rows .Next () {
993+ var (
994+ updater string
995+ kind driver.UpdateKind
996+ )
997+ err := rows .Scan (& updater , & kind )
998+ switch err {
999+ case nil :
1000+ // hop out
1001+ default :
1002+ return nil , fmt .Errorf ("error scanning updater: %v" , err )
1003+ }
1004+ updaters [kind ] = append (updaters [kind ], updater )
1005+ }
1006+ if rows .Err () != nil {
1007+ return nil , rows .Err ()
1008+ }
1009+
1010+ return updaters , nil
1011+ }
1012+
1013+ // eligibleUpdateOpts returns a list of update operation refs which exceed the specified
1014+ // keep value.
1015+ func eligibleUpdateOpts (ctx context.Context , conn * sql.DB , keep int ) ([]uuid.UUID , int64 , error ) {
1016+ const (
1017+ // this query will return rows of UUID arrays.
1018+ updateOps = `SELECT updater, json_group_array(ref ORDER BY date desc) FROM update_operation GROUP BY updater;`
1019+ )
1020+
1021+ // gather any update operations exceeding our keep value.
1022+ // keep+1 is used because PG's array slicing is inclusive,
1023+ // we want to grab all items once after our keep value.
1024+ m := []uuid.UUID {}
1025+
1026+ rows , err := conn .QueryContext (ctx , updateOps )
1027+ switch err {
1028+ case nil :
1029+ default :
1030+ return nil , 0 , fmt .Errorf ("error querying for update operations: %v" , err )
1031+ }
1032+
1033+ defer rows .Close ()
1034+ for rows .Next () {
1035+ var uuids_json string
1036+ var updater string
1037+ err := rows .Scan (& updater , & uuids_json )
1038+ if err != nil {
1039+ return nil , 0 , fmt .Errorf ("error scanning update operations: %w" , err )
1040+ }
1041+ var uuids []uuid.UUID
1042+ json .Unmarshal ([]byte (uuids_json ), & uuids )
1043+ m = append (m , uuids [keep :]... )
1044+ }
1045+ if rows .Err () != nil {
1046+ return nil , 0 , rows .Err ()
1047+ }
1048+
1049+ return m , int64 (len (m )), nil
1050+ }
1051+
1052+ type cleanupFunc func (context.Context , * sql.DB , string ) error
1053+
1054+ func vulnCleanup (ctx context.Context , conn * sql.DB , updater string ) error {
1055+ const (
1056+ deleteOrphanedVulns = `
1057+ DELETE FROM vuln
1058+ WHERE id IN (
1059+ SELECT v2.id
1060+ FROM vuln v2
1061+ LEFT JOIN uo_vuln uvl
1062+ ON v2.id = uvl.vuln
1063+ WHERE uvl.vuln IS NULL
1064+ AND v2.updater = $1
1065+ );
1066+ `
1067+ )
1068+
1069+ ctx = zlog .ContextWithValues (ctx , "updater" , updater )
1070+ zlog .Debug (ctx ).
1071+ Msg ("starting vuln clean up" )
1072+ res , err := conn .ExecContext (ctx , deleteOrphanedVulns , updater )
1073+ if err != nil {
1074+ return fmt .Errorf ("failed while exec'ing vuln delete: %w" , err )
1075+ }
1076+ rows , err := res .RowsAffected ()
1077+ if err != nil {
1078+ return fmt .Errorf ("failed while exec'ing vuln delete (affected): %w" , err )
1079+ }
1080+ zlog .Debug (ctx ).Int64 ("rows affected" , rows ).Msg ("vulns deleted" )
1081+
1082+ return nil
1083+ }
1084+
1085+ func enrichmentCleanup (ctx context.Context , conn * sql.DB , updater string ) error {
1086+ const (
1087+ deleteOrphanedEnrichments = `
1088+ DELETE FROM enrichment
1089+ WHERE id IN (
1090+ SELECT e2.id
1091+ FROM enrichment e2
1092+ LEFT JOIN uo_enrich uen
1093+ ON e2.id = uen.enrich
1094+ WHERE uen.enrich IS NULL
1095+ AND e2.updater = $1
1096+ );
1097+ `
1098+ )
1099+
1100+ ctx = zlog .ContextWithValues (ctx , "updater" , updater )
1101+ zlog .Debug (ctx ).
1102+ Msg ("starting enrichment clean up" )
1103+ res , err := conn .ExecContext (ctx , deleteOrphanedEnrichments , updater )
1104+ if err != nil {
1105+ return fmt .Errorf ("failed while exec'ing enrichment delete: %w" , err )
1106+ }
1107+ rows , err := res .RowsAffected ()
1108+ if err != nil {
1109+ return fmt .Errorf ("failed while exec'ing enrichment delete (affected): %w" , err )
1110+ }
1111+ zlog .Debug (ctx ).Int64 ("rows affected" , rows ).Msg ("enrichments deleted" )
1112+
1113+ return nil
9061114}
9071115
9081116// RecordUpdaterStatus records that an updater is up to date with vulnerabilities at this time
@@ -1002,8 +1210,7 @@ func (ms *sqliteMatcherStore) RecordUpdaterSetStatus(ctx context.Context, update
10021210 WHERE updater_name LIKE $2 || '%';`
10031211 )
10041212
1005- ctx = zlog .ContextWithValues (ctx ,
1006- "component" , "internal/vulnstore/postgres/recordUpdaterSetStatus" )
1213+ ctx = zlog .ContextWithValues (ctx , "component" , "internal/vulnstore/sqlite/recordUpdaterSetStatus" )
10071214
10081215 tx , err := ms .conn .BeginTx (ctx , nil )
10091216 if err != nil {
0 commit comments