diff --git a/scripts/Modules/Mars.pm b/scripts/Modules/Mars.pm index 38ce83f..922a6c7 100644 --- a/scripts/Modules/Mars.pm +++ b/scripts/Modules/Mars.pm @@ -166,6 +166,18 @@ sub db_do { $db->do(@cmd) or die "Database command failed (@cmd): " . $db->errstr; } +# db_do only reports affected rows, this one also reads the value back +sub db_selectrow { + my (@cmd) = @_; + + my $db = db_connect(); + + my @row = $db->selectrow_array(@cmd); + die "Database query failed (@cmd): " . $db->errstr if $db->err; + + return @row; +} + sub db_create { my ($db_name) = @_; say " - Creating database $db_name..."; diff --git a/scripts/menu.pl b/scripts/menu.pl index d45d101..d219a98 100644 --- a/scripts/menu.pl +++ b/scripts/menu.pl @@ -848,6 +848,136 @@ my %handlers = ( '<', Util::shell_quote($dump_file) ); }, + '33. Repair fspgroup Write Groups (Mars)' => sub { + + # Retroactively fixes (hopefully) the missing equivalence class + # mappings (EC <-> Pilot) the BasicPruner misses (because it maps + # multiple classes to a single pilot, but uses the pilot as the + # primary key) + my @dbs = Mars::db_list(); + my @dbs_with_notes; + foreach my $db (@dbs) { + my $info = + Util::read_experiment_info( $db =~ s/smchurla_//r =~ s/:/-/gr ); + + push @dbs_with_notes, + ( defined $info && length($info) > 0 ) + ? sprintf( "%-60s (%s)", $db, $info ) + : $db; + } + + my @selected_dbs = TUI::select_from_list( "Select Databases to Repair", + 1, @dbs_with_notes ); + die "No database selected" unless @selected_dbs; + + @selected_dbs = + map { s/(.*?)\s+\(.+\)$/$1/r } @selected_dbs; + + # The PRIMARY KEY gets dropped by the repair and replaced with a KEY. + # To not run this shit on already repaired DBs, check for the PRIMARY + # index as a determinant + my $index_exists = sub { + my ($index) = @_; + my ($count) = Mars::db_selectrow( + "SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'fspgroup' + AND INDEX_NAME = '$index'" + ); + return $count; + }; + + foreach my $db (@selected_dbs) { + say "Repairing $db..."; + + # Select the DB so index_exists works + Mars::db_do("use `$db`"); + + # Update the variant names, so the queries still work + my $expected_variant = $db =~ s/^smchurla_//r; + my ($variant_count) = + Mars::db_selectrow("SELECT COUNT(DISTINCT variant) FROM variant"); + my ($current_variant) = + Mars::db_selectrow("SELECT DISTINCT variant FROM variant"); + + if ( !defined $variant_count || $variant_count != 1 ) { + say " - WARNING: $variant_count distinct variants!"; + } + elsif ( $current_variant ne $expected_variant ) { + say " - Variant is '$current_variant' but queries look up" + . " '$expected_variant' (from the database name), renaming..."; + Mars::db_do("UPDATE variant SET variant = '$expected_variant'"); + } + + # Drop the primary key + if ( $index_exists->('PRIMARY') ) { + say " - Dropping PRIMARY KEY (pilot_id)..."; + Mars::db_do("ALTER TABLE fspgroup DROP PRIMARY KEY"); + } + + # Add an index for the columns we actually join on (mostly) + unless ( $index_exists->('eq_class') ) { + say " - Adding eq_class index..."; + Mars::db_do( + "ALTER TABLE fspgroup +ADD KEY eq_class (variant_id, instr2, data_physical_address)" + ); + } + + # I'm currently only using BasicPruner, but don't touch other + # pruning methods for now, for safety + my ($fspmethod_id) = + Mars::db_selectrow( + "SELECT id FROM fspmethod WHERE method = 'basic'"); + die "No 'basic' fspmethod in $db" unless defined $fspmethod_id; + + # The first row succeeded before the duplicate key error, remove it + Mars::db_do( + "DELETE g +FROM fspgroup g +JOIN fsppilot p ON p.id = g.pilot_id +WHERE p.known_outcome = 1 AND p.fspmethod_id = $fspmethod_id" + ); + + my ($expected) = + Mars::db_selectrow( + "SELECT COUNT(*) FROM trace WHERE accesstype = 'W'"); + + # Query from BasicPruner.cc, now ran against the updated DB + say " - Inserting $expected write groups..."; + Mars::db_do( + "INSERT INTO +fspgroup (variant_id, instr2, data_physical_address, bit_pos, fspmethod_id, pilot_id) +SELECT STRAIGHT_JOIN t.variant_id, t.instr2, t.data_physical_address, p.bit_pos, p.fspmethod_id, p.id +FROM fsppilot p +JOIN trace t +ON t.variant_id = p.variant_id AND p.fspmethod_id = $fspmethod_id AND p.known_outcome = 1 +WHERE t.accesstype = 'W'" + ); + + # We expect one row per write group/EC. + # Otherwise no idea what's going on :O + my ($actual) = Mars::db_selectrow( + "SELECT COUNT(*) +FROM fspgroup g +JOIN fsppilot p ON p.id = g.pilot_id +WHERE p.known_outcome = 1 AND p.fspmethod_id = $fspmethod_id" + ); + + if ( $actual == $expected ) { + say " - OK: $actual write groups match the write ECs in trace"; + } + else { + say " - WARNING: inserted $actual, expected $expected." + . " Check for duplicate known_outcome pilots:" + . " SELECT variant_id, COUNT(*) FROM fsppilot" + . " WHERE known_outcome = 1 GROUP BY variant_id;"; + } + } + + say "Queries have to be re-run."; + }, + '95. Delete Builds' => sub { # Delete old build files