|
|
@@ -238,9 +238,7 @@ class Fsck(object): |
|
|
|
try: |
|
|
|
posiset += new |
|
|
|
except IntervalError: |
|
|
|
raise FsckError("%s: overlap in file offsets:\n" |
|
|
|
"set: %s\nnew: %s", |
|
|
|
path, str(posiset), str(new)) |
|
|
|
self.fix_row_overlap(sid, path, posiset, new) |
|
|
|
|
|
|
|
try: |
|
|
|
# Check bulkdata |
|
|
@@ -268,6 +266,48 @@ class Fsck(object): |
|
|
|
path, str(e)) |
|
|
|
tab.close() |
|
|
|
|
|
|
|
|
|
|
|
def fix_row_overlap(self, sid, path, existing, new): |
|
|
|
# If the file rows (spos, epos) overlap in the interval table, |
|
|
|
# and the overlapping ranges look like this: |
|
|
|
# A --------- C |
|
|
|
# B -------- D |
|
|
|
# Then we can try changing the first interval to go from |
|
|
|
# A to B instead. |
|
|
|
msg = (f"{path}: overlap in file offests:\n" |
|
|
|
f"existing ranges: {existing}\n" |
|
|
|
f"overlapping interval: {new}") |
|
|
|
if not self.fix: |
|
|
|
raise FixableFsckError(msg) |
|
|
|
err(f"\n{msg}\nSeeing if we can truncate one of them...\n") |
|
|
|
|
|
|
|
# See if there'e exactly one interval that overlaps the |
|
|
|
# conflicting one in the right way |
|
|
|
match = None |
|
|
|
for intv in self.stream_interval[sid]: |
|
|
|
(stime, etime, spos, epos) = intv |
|
|
|
if spos < new.start and epos > new.start: |
|
|
|
if match: |
|
|
|
err(f"no, more than one interval matched:\n" |
|
|
|
f"{intv}\n{match}\n") |
|
|
|
raise FsckError(f"{path}: unfixable overlap") |
|
|
|
match = intv |
|
|
|
if match is None: |
|
|
|
err(f"no intervals overlapped in the right way\n") |
|
|
|
raise FsckError(f"{path}: unfixable overlap") |
|
|
|
|
|
|
|
# Truncate the file position |
|
|
|
err(f"truncating {match}\n") |
|
|
|
with self.sql: |
|
|
|
cur = self.sql.cursor() |
|
|
|
cur.execute("UPDATE ranges SET end_pos=? " |
|
|
|
"WHERE stream_id=? AND start_time=? AND " |
|
|
|
"end_time=? AND start_pos=? AND end_pos=?", |
|
|
|
(new.start, sid, *match)) |
|
|
|
if cur.rowcount != 1: # pragma: no cover (shouldn't fail) |
|
|
|
raise FsckError("failed to fix SQL database") |
|
|
|
raise RetryFsck |
|
|
|
|
|
|
|
### Check that bulkdata is good enough to be opened |
|
|
|
|
|
|
|
@retry_if_raised(RetryFsck) |
|
|
@@ -438,8 +478,8 @@ class Fsck(object): |
|
|
|
|
|
|
|
# Otherwise, the only hope is to delete the interval entirely. |
|
|
|
err("*** Deleting the entire interval from SQL.\n") |
|
|
|
err("This may leave stale data on disk. To fix that, copy all\n") |
|
|
|
err("data from this stream to a new stream using nilm-copy, then\n") |
|
|
|
err("This may leave stale data on disk. To fix that, copy all " |
|
|
|
"data from this stream to a new stream using nilm-copy, then\n") |
|
|
|
err("remove all data from and destroy %s.\n", path) |
|
|
|
with self.sql: |
|
|
|
cur = self.sql.cursor() |
|
|
@@ -564,7 +604,7 @@ class Fsck(object): |
|
|
|
msg = f"{path}: bad data in table, starting at row {row}: {reason}" |
|
|
|
if not self.fix: |
|
|
|
raise FixableFsckError(msg) |
|
|
|
err(f"\n{msg}\nWill try truncating table") |
|
|
|
err(f"\n{msg}\nWill try truncating table\n") |
|
|
|
(subdir, fname, offs, count) = tab._offset_from_row(row) |
|
|
|
tab._remove_or_truncate_file(subdir, fname, offs) |
|
|
|
raise RetryFsck |