12module storage345pub struct DoctorStats {6pub mut:7 objects i648 saves i649 checkpoints i6410 cached_files i6411 file_entries i6412 object_bytes i6413 packed_bytes i6414 unused i6415 pending []string16 state map[string]string17 problems []string18}192021pub fn (s &Store) doctor_stats() !DoctorStats {22 mut out := DoctorStats{}23 mut sizes := s.db.prepare('SELECT COUNT(*), COALESCE(SUM(size), 0),24 COALESCE(SUM(length(data)), 0) FROM objects')!25 defer { sizes.finalize() }26 if sizes.step()! {27 out.objects = sizes.int(0)28 out.object_bytes = sizes.int(1)29 out.packed_bytes = sizes.int(2)30 }31 mut counts := s.db.prepare('SELECT32 (SELECT COUNT(*) FROM saves),33 (SELECT COUNT(*) FROM saves WHERE is_checkpoint = 1),34 (SELECT COUNT(*) FROM file_cache),35 (SELECT COUNT(*) FROM save_files),36 (SELECT COUNT(*) FROM objects WHERE hash NOT IN (SELECT hash FROM save_files))')!37 defer { counts.finalize() }38 if counts.step()! {39 out.saves = counts.int(0)40 out.checkpoints = counts.int(1)41 out.cached_files = counts.int(2)42 out.file_entries = counts.int(3)43 out.unused = counts.int(4)44 }45 mut foreign := s.db.prepare('PRAGMA foreign_key_check')!46 defer { foreign.finalize() }47 for foreign.step()! {48 out.problems << 'broken reference in ${foreign.text(0)} row ${foreign.int(1)} to ${foreign.text(2)}'49 }50 mut invalid := s.db.prepare("SELECT save_id, path FROM save_files51 WHERE executable NOT IN (0, 1)52 OR typeof(executable) != 'integer'53 UNION ALL54 SELECT id, 'invalid save metadata' FROM saves55 WHERE is_checkpoint NOT IN (0, 1)56 OR typeof(is_checkpoint) != 'integer'57 OR id = ''58 OR typeof(created_at) != 'integer'59 OR created_at < 060 OR typeof(size_bytes) != 'integer'61 OR size_bytes < 0")!62 defer { invalid.finalize() }63 for invalid.step()! {64 out.problems << 'invalid metadata in save ${invalid.text(0)}: ${invalid.text(1)}'65 }66 mut state := s.db.prepare('SELECT key, value FROM state')!67 defer { state.finalize() }68 for state.step()! {69 key := state.text(0)70 out.state[key] = state.text(1)71 if key.starts_with('strip_pending:') {72 out.pending << key.all_after('strip_pending:')73 }74 }75 out.pending.sort()76 return out77}