1use noxu_dbi::DatabaseImpl;
6use noxu_tree::NodeRwLock as RwLock;
7use noxu_tree::Tree;
8use noxu_tree::tree::{BinStub, InNodeStub, TreeNode};
9use noxu_util::NULL_LSN;
10use std::fmt;
11use std::sync::Arc;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct VerifyResult {
16 pub errors: Vec<VerifyError>,
18 pub warnings: Vec<String>,
20 pub databases_verified: u32,
22 pub records_verified: u64,
24 pub passed: bool,
26}
27
28impl VerifyResult {
29 pub fn new() -> Self {
31 Self {
32 errors: Vec::new(),
33 warnings: Vec::new(),
34 databases_verified: 0,
35 records_verified: 0,
36 passed: true,
37 }
38 }
39
40 pub fn with_errors(errors: Vec<VerifyError>) -> Self {
42 Self {
43 passed: errors.is_empty(),
44 errors,
45 warnings: Vec::new(),
46 databases_verified: 0,
47 records_verified: 0,
48 }
49 }
50
51 pub fn add_error(&mut self, error: VerifyError) {
53 self.errors.push(error);
54 self.passed = false;
55 }
56
57 pub fn add_warning(&mut self, warning: String) {
59 self.warnings.push(warning);
60 }
61
62 pub fn is_passed(&self) -> bool {
64 self.passed
65 }
66
67 pub fn error_count(&self) -> usize {
69 self.errors.len()
70 }
71
72 pub fn warning_count(&self) -> usize {
74 self.warnings.len()
75 }
76}
77
78impl Default for VerifyResult {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl fmt::Display for VerifyResult {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 writeln!(f, "Verification Result")?;
87 writeln!(f, "===================")?;
88 writeln!(
89 f,
90 "Status: {}",
91 if self.passed { "PASSED" } else { "FAILED" }
92 )?;
93 writeln!(f, "Databases verified: {}", self.databases_verified)?;
94 writeln!(f, "Records verified: {}", self.records_verified)?;
95 writeln!(f)?;
96
97 if !self.errors.is_empty() {
98 writeln!(f, "Errors ({}):", self.errors.len())?;
99 for error in &self.errors {
100 writeln!(f, " - {}", error)?;
101 }
102 writeln!(f)?;
103 }
104
105 if !self.warnings.is_empty() {
106 writeln!(f, "Warnings ({}):", self.warnings.len())?;
107 for warning in &self.warnings {
108 writeln!(f, " - {}", warning)?;
109 }
110 writeln!(f)?;
111 }
112
113 if self.errors.is_empty() && self.warnings.is_empty() {
114 writeln!(f, "No errors or warnings found.")?;
115 }
116
117 Ok(())
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
123pub enum VerifyError {
124 BtreeError { db_name: String, description: String },
126 LogError { file_number: u32, description: String },
128 DataInconsistency { description: String },
130 ChecksumError { location: String, description: String },
132 InvalidNodeReference { node_id: u64, description: String },
134 MetadataError { db_name: String, description: String },
136}
137
138impl fmt::Display for VerifyError {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 match self {
141 VerifyError::BtreeError { db_name, description } => {
142 write!(f, "B-tree error in '{}': {}", db_name, description)
143 }
144 VerifyError::LogError { file_number, description } => {
145 write!(
146 f,
147 "Log file {:08x}.ndb error: {}",
148 file_number, description
149 )
150 }
151 VerifyError::DataInconsistency { description } => {
152 write!(f, "Data inconsistency: {}", description)
153 }
154 VerifyError::ChecksumError { location, description } => {
155 write!(f, "Checksum error at {}: {}", location, description)
156 }
157 VerifyError::InvalidNodeReference { node_id, description } => {
158 write!(
159 f,
160 "Invalid node reference (ID {}): {}",
161 node_id, description
162 )
163 }
164 VerifyError::MetadataError { db_name, description } => {
165 write!(f, "Metadata error in '{}': {}", db_name, description)
166 }
167 }
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct VerifyConfig {
174 pub verify_btree: bool,
176 pub verify_log: bool,
178 pub verify_data_checksums: bool,
180 pub repair: bool,
182 pub max_errors: u32,
184 pub verbose: bool,
186 pub database_name: Option<String>,
188}
189
190impl VerifyConfig {
191 pub fn new() -> Self {
193 Self::default()
194 }
195
196 pub fn with_btree_verification(mut self, enabled: bool) -> Self {
198 self.verify_btree = enabled;
199 self
200 }
201
202 pub fn with_log_verification(mut self, enabled: bool) -> Self {
204 self.verify_log = enabled;
205 self
206 }
207
208 pub fn with_checksum_verification(mut self, enabled: bool) -> Self {
210 self.verify_data_checksums = enabled;
211 self
212 }
213
214 pub fn with_repair(mut self, enabled: bool) -> Self {
216 self.repair = enabled;
217 self
218 }
219
220 pub fn with_max_errors(mut self, max: u32) -> Self {
222 self.max_errors = max;
223 self
224 }
225
226 pub fn with_verbose(mut self, enabled: bool) -> Self {
228 self.verbose = enabled;
229 self
230 }
231
232 pub fn for_database(mut self, name: String) -> Self {
234 self.database_name = Some(name);
235 self
236 }
237}
238
239impl Default for VerifyConfig {
240 fn default() -> Self {
241 VerifyConfig {
242 verify_btree: true,
243 verify_log: true,
244 verify_data_checksums: true,
245 repair: false,
246 max_errors: 100,
247 verbose: false,
248 database_name: None,
249 }
250 }
251}
252
253pub fn verify_tree(
271 tree: &Tree,
272 db_name: &str,
273 config: &VerifyConfig,
274) -> VerifyResult {
275 let mut result = VerifyResult::new();
276
277 if !config.verify_btree {
278 return result;
279 }
280
281 let root = match tree.get_root() {
282 Some(r) => r,
283 None => {
284 result.databases_verified = 1;
286 return result;
287 }
288 };
289
290 let mut records: u64 = 0;
291 verify_node(&root, None, db_name, config, &mut result, &mut records);
292 result.records_verified = records;
293 result.databases_verified = 1;
294 result
295}
296
297fn verify_node(
299 node_arc: &Arc<RwLock<TreeNode>>,
300 parent_key: Option<&[u8]>,
301 db_name: &str,
302 config: &VerifyConfig,
303 result: &mut VerifyResult,
304 records: &mut u64,
305) {
306 let guard = node_arc.read();
307
308 match &*guard {
309 TreeNode::Internal(in_node) => {
310 verify_internal_node(
311 in_node, parent_key, db_name, config, result, records,
312 );
313 }
314 TreeNode::Bottom(bin_stub) => {
315 verify_bin_stub(
316 bin_stub, parent_key, db_name, config, result, records,
317 );
318 }
319 }
320}
321
322fn verify_internal_node(
327 in_node: &InNodeStub,
328 _parent_key: Option<&[u8]>,
329 db_name: &str,
330 config: &VerifyConfig,
331 result: &mut VerifyResult,
332 records: &mut u64,
333) {
334 if in_node.entries.is_empty() {
335 return;
338 }
339
340 for (i, entry) in in_node.entries.iter().enumerate() {
342 let child_arc = match &entry.child {
343 Some(c) => c,
344 None => {
345 result.add_error(VerifyError::BtreeError {
346 db_name: db_name.to_string(),
347 description: format!(
348 "IN node (id={}) entry {} has null child reference",
349 in_node.node_id, i
350 ),
351 });
352 if result.error_count() >= config.max_errors as usize {
353 return;
354 }
355 continue;
356 }
357 };
358
359 let expected_parent_key: Option<&[u8]> =
363 if i == 0 { None } else { Some(entry.key.as_slice()) };
364
365 verify_node(
366 child_arc,
367 expected_parent_key,
368 db_name,
369 config,
370 result,
371 records,
372 );
373
374 if result.error_count() >= config.max_errors as usize {
375 return;
376 }
377 }
378}
379
380fn verify_bin_stub(
386 bin: &BinStub,
387 parent_key: Option<&[u8]>,
388 db_name: &str,
389 config: &VerifyConfig,
390 result: &mut VerifyResult,
391 records: &mut u64,
392) {
393 if let Some(pk) = parent_key
395 && !bin.entries.is_empty()
396 {
397 let first_full = bin.get_full_key(0);
398 if let Some(ref first_key) = first_full
399 && first_key.as_slice() < pk
400 {
401 result.add_error(VerifyError::BtreeError {
402 db_name: db_name.to_string(),
403 description: format!(
404 "BIN (id={}) first key {:?} is less than parent routing key {:?}",
405 bin.node_id, first_key, pk
406 ),
407 });
408 }
409 }
410
411 for (i, entry) in bin.entries.iter().enumerate() {
413 if !entry.known_deleted && entry.lsn == NULL_LSN {
415 result.add_error(VerifyError::BtreeError {
416 db_name: db_name.to_string(),
417 description: format!(
418 "BIN (id={}) slot {} has NULL LSN but is not known-deleted",
419 bin.node_id, i
420 ),
421 });
422 if result.error_count() >= config.max_errors as usize {
423 return;
424 }
425 }
426
427 if !entry.known_deleted {
428 *records += 1;
429 }
430 }
431}
432
433pub fn verify_environment(config: &VerifyConfig) -> VerifyResult {
470 log::warn!(
471 "verify_environment called but verification is not yet implemented; \
472 the result does not reflect a real integrity check"
473 );
474 if config.verbose {
475 log::info!("Starting environment verification");
476 log::info!(" B-tree: {}", config.verify_btree);
477 log::info!(" Log: {}", config.verify_log);
478 log::info!(" Checksums: {}", config.verify_data_checksums);
479 log::info!(" Repair: {}", config.repair);
480 }
481
482 VerifyResult {
483 errors: Vec::new(),
484 warnings: Vec::new(),
485 databases_verified: 0,
486 records_verified: 0,
487 passed: true,
488 }
489}
490
491pub fn verify_database(db_name: &str, config: &VerifyConfig) -> VerifyResult {
512 log::warn!(
513 "verify_database({db_name}) called but verification is not yet implemented; \
514 the result does not reflect a real integrity check"
515 );
516 if config.verbose {
517 log::info!("Verifying database: {}", db_name);
518 }
519
520 VerifyResult {
521 errors: Vec::new(),
522 warnings: Vec::new(),
523 databases_verified: 1,
524 records_verified: 0,
525 passed: true,
526 }
527}
528
529pub fn verify_database_impl(
547 db_impl: &DatabaseImpl,
548 config: &VerifyConfig,
549) -> VerifyResult {
550 let db_name = db_impl.get_name();
551 match db_impl.get_real_tree() {
552 Some(tree) => verify_tree(&tree, db_name, config),
553 None => {
554 VerifyResult {
556 errors: Vec::new(),
557 warnings: Vec::new(),
558 databases_verified: 1,
559 records_verified: 0,
560 passed: true,
561 }
562 }
563 }
564}
565
566#[cfg(test)]
567mod tests {
568 use super::*;
569
570 #[test]
571 fn test_verify_result_new() {
572 let result = VerifyResult::new();
573 assert!(result.passed);
574 assert_eq!(result.errors.len(), 0);
575 assert_eq!(result.warnings.len(), 0);
576 assert_eq!(result.databases_verified, 0);
577 assert_eq!(result.records_verified, 0);
578 }
579
580 #[test]
581 fn test_verify_result_default() {
582 let result = VerifyResult::default();
583 assert!(result.passed);
584 assert!(result.errors.is_empty());
585 }
586
587 #[test]
588 fn test_verify_result_with_errors() {
589 let errors = vec![VerifyError::BtreeError {
590 db_name: "test".to_string(),
591 description: "Invalid node".to_string(),
592 }];
593 let result = VerifyResult::with_errors(errors);
594 assert!(!result.passed);
595 assert_eq!(result.errors.len(), 1);
596 }
597
598 #[test]
599 fn test_verify_result_with_no_errors() {
600 let errors = vec![];
601 let result = VerifyResult::with_errors(errors);
602 assert!(result.passed);
603 assert_eq!(result.errors.len(), 0);
604 }
605
606 #[test]
607 fn test_add_error() {
608 let mut result = VerifyResult::new();
609 assert!(result.passed);
610
611 result.add_error(VerifyError::DataInconsistency {
612 description: "Test error".to_string(),
613 });
614
615 assert!(!result.passed);
616 assert_eq!(result.errors.len(), 1);
617 }
618
619 #[test]
620 fn test_add_warning() {
621 let mut result = VerifyResult::new();
622 result.add_warning("Test warning".to_string());
623
624 assert!(result.passed); assert_eq!(result.warnings.len(), 1);
626 }
627
628 #[test]
629 fn test_error_count() {
630 let mut result = VerifyResult::new();
631 assert_eq!(result.error_count(), 0);
632
633 result.add_error(VerifyError::DataInconsistency {
634 description: "Error 1".to_string(),
635 });
636 result.add_error(VerifyError::DataInconsistency {
637 description: "Error 2".to_string(),
638 });
639
640 assert_eq!(result.error_count(), 2);
641 }
642
643 #[test]
644 fn test_warning_count() {
645 let mut result = VerifyResult::new();
646 assert_eq!(result.warning_count(), 0);
647
648 result.add_warning("Warning 1".to_string());
649 result.add_warning("Warning 2".to_string());
650
651 assert_eq!(result.warning_count(), 2);
652 }
653
654 #[test]
655 fn test_is_passed() {
656 let result = VerifyResult::new();
657 assert!(result.is_passed());
658
659 let mut failed_result = VerifyResult::new();
660 failed_result.add_error(VerifyError::DataInconsistency {
661 description: "Error".to_string(),
662 });
663 assert!(!failed_result.is_passed());
664 }
665
666 #[test]
667 fn test_verify_error_btree() {
668 let error = VerifyError::BtreeError {
669 db_name: "mydb".to_string(),
670 description: "Invalid child reference".to_string(),
671 };
672 let s = format!("{}", error);
673 assert!(s.contains("B-tree error"));
674 assert!(s.contains("mydb"));
675 assert!(s.contains("Invalid child reference"));
676 }
677
678 #[test]
679 fn test_verify_error_log() {
680 let error = VerifyError::LogError {
681 file_number: 42,
682 description: "Corrupted entry".to_string(),
683 };
684 let s = format!("{}", error);
685 assert!(s.contains("Log file"));
686 assert!(s.contains("0000002a.ndb"));
687 assert!(s.contains("Corrupted entry"));
688 }
689
690 #[test]
691 fn test_verify_error_data_inconsistency() {
692 let error = VerifyError::DataInconsistency {
693 description: "Mismatched LSN".to_string(),
694 };
695 let s = format!("{}", error);
696 assert!(s.contains("Data inconsistency"));
697 assert!(s.contains("Mismatched LSN"));
698 }
699
700 #[test]
701 fn test_verify_error_checksum() {
702 let error = VerifyError::ChecksumError {
703 location: "file 10, offset 1024".to_string(),
704 description: "CRC mismatch".to_string(),
705 };
706 let s = format!("{}", error);
707 assert!(s.contains("Checksum error"));
708 assert!(s.contains("file 10, offset 1024"));
709 assert!(s.contains("CRC mismatch"));
710 }
711
712 #[test]
713 fn test_verify_error_invalid_node_reference() {
714 let error = VerifyError::InvalidNodeReference {
715 node_id: 12345,
716 description: "Node not found".to_string(),
717 };
718 let s = format!("{}", error);
719 assert!(s.contains("Invalid node reference"));
720 assert!(s.contains("12345"));
721 assert!(s.contains("Node not found"));
722 }
723
724 #[test]
725 fn test_verify_error_metadata() {
726 let error = VerifyError::MetadataError {
727 db_name: "testdb".to_string(),
728 description: "Invalid format version".to_string(),
729 };
730 let s = format!("{}", error);
731 assert!(s.contains("Metadata error"));
732 assert!(s.contains("testdb"));
733 assert!(s.contains("Invalid format version"));
734 }
735
736 #[test]
737 fn test_verify_config_default() {
738 let config = VerifyConfig::default();
739 assert!(config.verify_btree);
740 assert!(config.verify_log);
741 assert!(config.verify_data_checksums);
742 assert!(!config.repair);
743 assert_eq!(config.max_errors, 100);
744 assert!(!config.verbose);
745 assert!(config.database_name.is_none());
746 }
747
748 #[test]
749 fn test_verify_config_new() {
750 let config = VerifyConfig::new();
751 assert_eq!(config, VerifyConfig::default());
752 }
753
754 #[test]
755 fn test_verify_config_builder() {
756 let config = VerifyConfig::new()
757 .with_btree_verification(false)
758 .with_log_verification(true)
759 .with_checksum_verification(false)
760 .with_repair(true)
761 .with_max_errors(50)
762 .with_verbose(true)
763 .for_database("mydb".to_string());
764
765 assert!(!config.verify_btree);
766 assert!(config.verify_log);
767 assert!(!config.verify_data_checksums);
768 assert!(config.repair);
769 assert_eq!(config.max_errors, 50);
770 assert!(config.verbose);
771 assert_eq!(config.database_name, Some("mydb".to_string()));
772 }
773
774 #[test]
775 fn test_verify_environment_stub() {
776 let config = VerifyConfig::default();
777 let result = verify_environment(&config);
778 assert!(result.passed);
779 assert_eq!(result.errors.len(), 0);
780 }
781
782 #[test]
783 fn test_verify_environment_with_custom_config() {
784 let config = VerifyConfig::new().with_repair(true).with_max_errors(10);
785 let result = verify_environment(&config);
786 assert!(result.passed);
787 }
788
789 #[test]
790 fn test_verify_database_stub() {
791 let config = VerifyConfig::default();
792 let result = verify_database("testdb", &config);
793 assert!(result.passed);
794 assert_eq!(result.databases_verified, 1);
795 }
796
797 #[test]
798 fn test_verify_result_display_passed() {
799 let result = VerifyResult {
800 errors: Vec::new(),
801 warnings: Vec::new(),
802 databases_verified: 5,
803 records_verified: 1000,
804 passed: true,
805 };
806
807 let output = format!("{}", result);
808 assert!(output.contains("PASSED"));
809 assert!(output.contains("Databases verified: 5"));
810 assert!(output.contains("Records verified: 1000"));
811 assert!(output.contains("No errors or warnings"));
812 }
813
814 #[test]
815 fn test_verify_result_display_with_errors() {
816 let mut result = VerifyResult::new();
817 result.add_error(VerifyError::BtreeError {
818 db_name: "test".to_string(),
819 description: "Bad node".to_string(),
820 });
821 result.databases_verified = 2;
822 result.records_verified = 500;
823
824 let output = format!("{}", result);
825 assert!(output.contains("FAILED"));
826 assert!(output.contains("Errors (1)"));
827 assert!(output.contains("B-tree error"));
828 }
829
830 #[test]
831 fn test_verify_result_display_with_warnings() {
832 let mut result = VerifyResult::new();
833 result.add_warning("Low cache utilization".to_string());
834 result.databases_verified = 3;
835
836 let output = format!("{}", result);
837 assert!(output.contains("PASSED"));
838 assert!(output.contains("Warnings (1)"));
839 assert!(output.contains("Low cache utilization"));
840 }
841
842 #[test]
843 fn test_verify_result_clone() {
844 let mut result = VerifyResult::new();
845 result.add_error(VerifyError::DataInconsistency {
846 description: "Test".to_string(),
847 });
848
849 let cloned = result.clone();
850 assert_eq!(cloned.errors.len(), result.errors.len());
851 assert_eq!(cloned.passed, result.passed);
852 }
853
854 #[test]
855 fn test_verify_error_equality() {
856 let error1 = VerifyError::BtreeError {
857 db_name: "db1".to_string(),
858 description: "error".to_string(),
859 };
860 let error2 = VerifyError::BtreeError {
861 db_name: "db1".to_string(),
862 description: "error".to_string(),
863 };
864 let error3 = VerifyError::BtreeError {
865 db_name: "db2".to_string(),
866 description: "error".to_string(),
867 };
868
869 assert_eq!(error1, error2);
870 assert_ne!(error1, error3);
871 }
872
873 #[test]
874 fn test_verify_config_equality() {
875 let config1 = VerifyConfig::default();
876 let config2 = VerifyConfig::default();
877 let config3 = VerifyConfig::new().with_repair(true);
878
879 assert_eq!(config1, config2);
880 assert_ne!(config1, config3);
881 }
882
883 #[test]
887 fn test_verify_tree_empty() {
888 use noxu_dbi::{DatabaseConfig, DatabaseId, DatabaseImpl, DbType};
889 use noxu_sync::RwLock;
890 use std::sync::Arc;
891
892 let db_id = DatabaseId::new(1);
893 let config = DatabaseConfig::default();
894 let db_impl = DatabaseImpl::new(
895 db_id,
896 "verify_test".to_string(),
897 DbType::User,
898 &config,
899 );
900 let db = Arc::new(RwLock::new(db_impl));
901 let guard = db.read();
902 let cfg = VerifyConfig::default();
903
904 if let Some(t) = guard.get_real_tree() {
905 let result = verify_tree(&t, "verify_test", &cfg);
906 assert!(
907 result.passed,
908 "empty tree should pass: {:?}",
909 result.errors
910 );
911 assert_eq!(result.databases_verified, 1);
912 }
913 }
915
916 #[test]
921 fn test_verify_tree_populated() {
922 use noxu_dbi::{
923 CursorImpl, DatabaseConfig, DatabaseId, DatabaseImpl, DbType,
924 PutMode,
925 };
926 use noxu_log::{FileManager, LogManager};
927 use noxu_sync::RwLock;
928 use std::sync::Arc;
929 use tempfile::TempDir;
930
931 let dir = TempDir::new().unwrap();
932 let fm = Arc::new(
933 FileManager::new(dir.path(), false, 64 * 1024 * 1024, 100).unwrap(),
934 );
935 let lm =
936 Arc::new(LogManager::new(Arc::clone(&fm), 3, 1024 * 1024, 65536));
937
938 let db_id = DatabaseId::new(2);
939 let config = DatabaseConfig::default();
940 let db_impl = DatabaseImpl::new(
941 db_id,
942 "pop_test".to_string(),
943 DbType::User,
944 &config,
945 );
946 let db = Arc::new(RwLock::new(db_impl));
947
948 {
949 let mut cursor = CursorImpl::with_log_manager(
950 Arc::clone(&db),
951 1,
952 Arc::clone(&lm),
953 );
954 cursor.put(b"alpha", b"1", PutMode::Overwrite).unwrap();
955 cursor.put(b"beta", b"2", PutMode::Overwrite).unwrap();
956 cursor.put(b"gamma", b"3", PutMode::Overwrite).unwrap();
957 }
958
959 let guard = db.read();
960 let cfg = VerifyConfig::default();
961
962 if let Some(t) = guard.get_real_tree() {
963 let result = verify_tree(&t, "pop_test", &cfg);
964 assert!(
965 result.passed,
966 "populated tree should pass: {:?}",
967 result.errors
968 );
969 assert_eq!(result.databases_verified, 1);
970 }
971 }
972}