# # # patch "ChangeLog" # from [e39bd1212f2157b8a76625880e0c3c1d90a43333] # to [ee26a5038726b27a08703697327af44e6d9dd3bc] # # patch "database_check.cc" # from [de926284bde8ed4c7fd6b075fbb8bd72a9135ba5] # to [08c81eb4ad5e70f47be3920a41ef7a2a33480a14] # ============================================================ --- ChangeLog e39bd1212f2157b8a76625880e0c3c1d90a43333 +++ ChangeLog ee26a5038726b27a08703697327af44e6d9dd3bc @@ -1,3 +1,9 @@ +2006-10-20 Thomas Moschny + + * database_check.cc: Add checks for the heights. It is checked + that every revision has a unique height that is greater than that + of all it's parents. + 2006-10-09 Nathaniel Smith * automate.cc: Remove spurious #include of rev_height.hh. ============================================================ --- database_check.cc de926284bde8ed4c7fd6b075fbb8bd72a9135ba5 +++ database_check.cc 08c81eb4ad5e70f47be3920a41ef7a2a33480a14 @@ -19,6 +19,7 @@ #include "vocab.hh" #include "transforms.hh" #include "cert.hh" +#include "rev_height.hh" // the database has roughly the following structure // @@ -118,6 +119,13 @@ struct checked_revision { cert_refs(0), parseable(false), normalized(false) {} }; +struct checked_height { + bool found; // found in db + bool unique; // not identical to any height retrieved earlier + bool sensible; // greater than all parent heights + checked_height(): found(false), unique(false), sensible(true) {} +}; + /* * check integrity of the SQLite database */ @@ -473,7 +481,104 @@ check_certs(app_state & app, } } +// - check that every rev has a height +// - check that no two revs have the same height static void +check_heights(app_state & app, + map & checked_heights) +{ + set heights; + app.db.get_revision_ids(heights); + + // add revision [], it is the (imaginary) root of all revisions, and + // should have a height, too + { + revision_id null_id; + heights.insert(null_id); + } + + L(FL("checking %d heights") % heights.size()); + + set seen; + + ticker ticks(_("heights"), "h", heights.size()/70+1); + + for (set::const_iterator i = heights.begin(); + i != heights.end(); ++i) + { + L(FL("checking height for %s") % *i); + + rev_height h; + try + { + app.db.get_rev_height(*i, h); + } + catch (std::exception & e) + { + L(FL("error loading height: %s") % e.what()); + continue; + } + checked_heights[*i].found = true; // defaults to false + + if (seen.find(h) != seen.end()) + { + L(FL("error: height not unique: %s") % h()); + continue; + } + checked_heights[*i].unique = true; // defaults to false + seen.insert(h); + + ++ticks; + } +} + +// check that every rev's height is a sensible height to assign, given its +// parents +static void +check_heights_relation(app_state & app, + map & checked_heights) +{ + set heights; + + multimap graph; // parent, child + app.db.get_revision_ancestry(graph); + + L(FL("checking heights for %d edges") % graph.size()); + + ticker ticks(_("height relations"), "h", graph.size()/70+1); + + typedef multimap::const_iterator gi; + for (gi i = graph.begin(); i != graph.end(); ++i) + { + revision_id const & p_id = i->first; + revision_id const & c_id = i->second; + + if (!checked_heights[p_id].found || !checked_heights[c_id].found) + { + L(FL("missing height(s), skipping edge %s -> %s") % p_id % c_id); + continue; + } + + L(FL("checking heights for edges %s -> %s") % + p_id % c_id); + + rev_height parent, child; + app.db.get_rev_height(p_id, parent); + app.db.get_rev_height(c_id, child); + + if (!(child > parent)) + { + L(FL("error: height %s of child %s not greater than height %s of parent %s") + % child % c_id % parent % p_id); + checked_heights[c_id].sensible = false; // defaults to true + continue; + } + + ++ticks; + } +} + +static void report_files(map const & checked_files, size_t & missing_files, size_t & unreferenced_files) @@ -724,6 +829,38 @@ report_certs(map const & checked_heights, + size_t & missing_heights, + size_t & duplicate_heights, + size_t & incorrect_heights) +{ + for (map::const_iterator + i = checked_heights.begin(); i != checked_heights.end(); ++i) + { + checked_height height = i->second; + + if (!height.found) + { + missing_heights++; + P(F("height missing for revision %s") % i->first); + continue; + } + + if (!height.unique) + { + duplicate_heights++; + P(F("duplicate height for revision %s") % i->first); + } + + if (!height.sensible) + { + incorrect_heights++; + P(F("height of revision %s not greater than that of parent") % i->first); + } + } +} + void check_db(app_state & app) { @@ -732,6 +869,7 @@ check_db(app_state & app) map checked_rosters; map checked_revisions; map checked_keys; + map checked_heights; size_t missing_files = 0; size_t unreferenced_files = 0; @@ -757,6 +895,10 @@ check_db(app_state & app) size_t unchecked_sigs = 0; size_t bad_sigs = 0; + size_t missing_heights = 0; + size_t duplicate_heights = 0; + size_t incorrect_heights = 0; + check_db_integrity_check(app); check_files(app, checked_files); check_rosters_manifest(app, checked_rosters, checked_revisions, @@ -767,6 +909,8 @@ check_db(app_state & app) check_ancestry(app, checked_revisions); check_keys(app, checked_keys); check_certs(app, checked_revisions, checked_keys, total_certs); + check_heights(app, checked_heights); + check_heights_relation(app, checked_heights); report_files(checked_files, missing_files, unreferenced_files); @@ -787,6 +931,9 @@ check_db(app_state & app) missing_certs, mismatched_certs, unchecked_sigs, bad_sigs); + report_heights(checked_heights, + missing_heights, duplicate_heights, incorrect_heights); + // NOTE: any new sorts of problems need to have added: // -- a message here, that tells the use about them // -- entries in one _or both_ of the sums calculated at the end @@ -836,6 +983,13 @@ check_db(app_state & app) if (bad_sigs > 0) W(F("%d bad signatures") % bad_sigs); + if (missing_heights > 0) + W(F("%d missing heights") % missing_heights); + if (duplicate_heights > 0) + W(F("%d duplicate heights") % duplicate_heights); + if (incorrect_heights > 0) + W(F("%d incorrect heights") % incorrect_heights); + size_t total = missing_files + unreferenced_files + unreferenced_rosters + incomplete_rosters + missing_revisions + incomplete_revisions + @@ -845,7 +999,8 @@ check_db(app_state & app) missing_rosters + missing_certs + mismatched_certs + unchecked_sigs + bad_sigs + - missing_keys; + missing_keys + + missing_heights + duplicate_heights + incorrect_heights; // unreferenced files and rosters and mismatched certs are not actually // serious errors; odd, but nothing will break. size_t serious = missing_files + @@ -856,14 +1011,16 @@ check_db(app_state & app) bad_history + missing_certs + unchecked_sigs + bad_sigs + - missing_keys; + missing_keys + + missing_heights + duplicate_heights + incorrect_heights; - P(F("check complete: %d files; %d rosters; %d revisions; %d keys; %d certs") + P(F("check complete: %d files; %d rosters; %d revisions; %d keys; %d certs; %d heights") % checked_files.size() % checked_rosters.size() % checked_revisions.size() % checked_keys.size() - % total_certs); + % total_certs + % checked_heights.size()); P(F("total problems detected: %d (%d serious)") % total % serious); if (serious) E(false, F("serious problems detected"));