|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Remove one test run from the database, all of its result tables together. |
| 4 | +
|
| 5 | + ./remove-run.py --db postgresql://om@localhost/omdb wasm-jit |
| 6 | + ./remove-run.py --db postgresql://om@localhost/omdb wasm-jit --write |
| 7 | +
|
| 8 | +The first says what it would delete, the second deletes it. Without --date or |
| 9 | +--omcversion it takes the newest run of that branch. |
| 10 | +
|
| 11 | +A run of test.py --wasmjitrunner or --fmisimulator fills several tables from one |
| 12 | +job - wasm-jit, wasm-jit-me and wasm-jit-cs share a date - and removing only the |
| 13 | +first would leave the others describing a run that no longer exists. So every |
| 14 | +table whose name is the branch or begins with it, and that has rows of that |
| 15 | +date, goes at once; --also names any further table, for the --solver runners, |
| 16 | +whose tables are named after the solver rather than the branch. |
| 17 | +
|
| 18 | +The rows of a run are in the result table, in omcversion and in libversion. Its |
| 19 | +job_claim rows are left alone: they say who tested a library last, the next run |
| 20 | +overwrites them, and a finished claim stops nobody. |
| 21 | +""" |
| 22 | + |
| 23 | +import argparse, sys |
| 24 | +from datetime import datetime, timezone |
| 25 | +import resultsdb, shared |
| 26 | + |
| 27 | + |
| 28 | +def runDate(cursor, branch, date, omcversion): |
| 29 | + """The date of the run being removed, and the omc that ran it.""" |
| 30 | + if date and omcversion: |
| 31 | + raise SystemExit("Give --date or --omcversion, not both") |
| 32 | + if omcversion: |
| 33 | + where = "branch=? AND omcversion=?" |
| 34 | + params = (branch, omcversion) |
| 35 | + elif date: |
| 36 | + where = "branch=? AND date=?" |
| 37 | + params = (branch, date) |
| 38 | + else: |
| 39 | + where = "branch=?" |
| 40 | + params = (branch,) |
| 41 | + rows = cursor.execute("SELECT date, omcversion FROM omcversion WHERE %s ORDER BY date DESC" |
| 42 | + % where, params).fetchall() |
| 43 | + if not rows: |
| 44 | + raise SystemExit("No run of %s in omcversion matching that" % branch) |
| 45 | + if omcversion and len(rows) > 1: |
| 46 | + raise SystemExit("%s ran %s %d times; name one of them with --date %s" |
| 47 | + % (branch, omcversion, len(rows), " --date ".join(str(r[0]) for r in rows))) |
| 48 | + return rows[0] |
| 49 | + |
| 50 | + |
| 51 | +def resultTables(cursor, db, branch, date, also): |
| 52 | + """The tables one job of that branch wrote: itself, whichever of its runners |
| 53 | + has rows of that date, and whatever --also names.""" |
| 54 | + candidates = [t for t in db.tables() if t not in resultsdb.NON_RESULT_TABLES |
| 55 | + and t.startswith(branch + "-")] |
| 56 | + ran = [t for t in candidates |
| 57 | + if cursor.execute("SELECT COUNT(*) FROM %s WHERE date=?" % db.quote(t), (date,)).fetchone()[0]] |
| 58 | + return sorted(set([branch] + ran + list(also))) |
| 59 | + |
| 60 | + |
| 61 | +def counts(cursor, db, tables, date): |
| 62 | + """How many rows each table holds for that run, in the order they are deleted.""" |
| 63 | + out = [] |
| 64 | + for table in tables: |
| 65 | + out.append((table, "date=?", (date,))) |
| 66 | + for table in ("omcversion", "libversion"): |
| 67 | + for name in tables: |
| 68 | + out.append((table, "branch=? AND date=?", (name, date))) |
| 69 | + return [(table, where, params, |
| 70 | + cursor.execute("SELECT COUNT(*) FROM %s WHERE %s" % (db.quote(table), where), |
| 71 | + params).fetchone()[0]) |
| 72 | + for (table, where, params) in out] |
| 73 | + |
| 74 | + |
| 75 | +def main(): |
| 76 | + parser = argparse.ArgumentParser( |
| 77 | + description="Remove one test run, all of its result tables together", |
| 78 | + formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__) |
| 79 | + parser.add_argument("branch", help="The branch whose run to remove, as it is named in the database") |
| 80 | + parser.add_argument("--date", type=int, help="The run to remove, as the epoch second in its date column") |
| 81 | + parser.add_argument("--omcversion", help="The run to remove, as the omc version that produced it") |
| 82 | + parser.add_argument("--also", action="append", default=[], |
| 83 | + help="A further table the same job wrote, for --solver runners, whose tables " |
| 84 | + "are named after the solver. Repeatable.") |
| 85 | + parser.add_argument("--write", action="store_true", help="Delete, instead of only saying what would be deleted") |
| 86 | + resultsdb.addArgument(parser) |
| 87 | + args = parser.parse_args() |
| 88 | + |
| 89 | + branch = shared.resultTable(args.branch) |
| 90 | + db = resultsdb.connect(args.db) |
| 91 | + cursor = db.cursor() |
| 92 | + |
| 93 | + (date, omcversion) = runDate(cursor, branch, args.date, args.omcversion) |
| 94 | + tables = resultTables(cursor, db, branch, date, args.also) |
| 95 | + print("%s run of %s, date %d (%s)" |
| 96 | + % (branch, omcversion, date, datetime.fromtimestamp(date, tz=timezone.utc).isoformat())) |
| 97 | + print("Result tables: %s" % ", ".join(tables)) |
| 98 | + |
| 99 | + rows = counts(cursor, db, tables, date) |
| 100 | + for (table, where, params, n) in rows: |
| 101 | + print(" %-24s %6d rows (%s)" % (table, n, " ".join(str(p) for p in params))) |
| 102 | + total = sum(n for (_, _, _, n) in rows) |
| 103 | + print(" %-24s %6d rows" % ("total", total)) |
| 104 | + if not total: |
| 105 | + raise SystemExit("Nothing to remove") |
| 106 | + |
| 107 | + if not args.write: |
| 108 | + print("\nNothing deleted. Run it again with --write to delete.") |
| 109 | + return |
| 110 | + |
| 111 | + for (table, where, params, n) in rows: |
| 112 | + cursor.execute("DELETE FROM %s WHERE %s" % (db.quote(table), where), params) |
| 113 | + db.commit() |
| 114 | + |
| 115 | + left = [(table, n) for (table, where, params, n) in counts(cursor, db, tables, date) if n] |
| 116 | + if left: |
| 117 | + print("Still there after deleting: %s" % ", ".join("%s (%d)" % t for t in left)) |
| 118 | + sys.exit(1) |
| 119 | + print("Removed %d rows." % total) |
| 120 | + db.vacuum() |
| 121 | + db.close() |
| 122 | + |
| 123 | + |
| 124 | +if __name__ == "__main__": |
| 125 | + main() |
0 commit comments