def buildPackIndex(self): pos = 4 # We make the initial assumption that the database has been # packed before and set unpacked to True only after seeing the # first record with a status == " ". If we get to the packtime # and unpacked is still False, we need to watch for a redundant # pack. unpacked = False while pos < self.eof: th = self._read_txn_header(pos) if th.tid > self.packtime: break self.checkTxn(th, pos) if th.status != "p": unpacked = True tpos = pos end = pos + th.tlen pos += th.headerlen() while pos < end: dh = self._read_data_header(pos) self.checkData(th, tpos, dh, pos) if dh.plen or dh.back: self.oid2curpos[dh.oid] = pos else: if dh.oid in self.oid2curpos: del self.oid2curpos[dh.oid] pos += dh.recordlen() tlen = self._read_num(pos) if tlen != th.tlen: self.fail( pos, "redundant transaction length does not " "match initial transaction length: %d != %d", tlen, th.tlen) pos += 8 self.packpos = pos if unpacked: return # check for a redundant pack. If the first record following # the newly computed packpos has status 'p', then it was # packed earlier and the current pack is redudant. try: th = self._read_txn_header(pos) except CorruptedDataError as err: if err.buf != b"": raise if th.status == 'p': # Delayed import to cope with circular imports. # TODO: put exceptions in a separate module. from ZODB.FileStorage.FileStorage import RedundantPackWarning raise RedundantPackWarning( "The database has already been packed to a later time" " or no changes have been made since the last pack")
class GC(FileStorageFormatter): def __init__(self, file, eof, packtime, gc, referencesf): self._file = file self._name = file.name self.eof = eof self.packtime = packtime self.gc = gc # packpos: position of first txn header after pack time self.packpos = None # {oid -> current data record position}: self.oid2curpos = ZODB.fsIndex.fsIndex() # The set of reachable revisions of each object. # # This set as managed using two data structures. The first is # an fsIndex mapping oids to one data record pos. Since only # a few objects will have more than one revision, we use this # efficient data structure to handle the common case. The # second is a dictionary mapping objects to lists of # positions; it is used to handle the same number of objects # for which we must keep multiple revisions. self.reachable = ZODB.fsIndex.fsIndex() self.reach_ex = {} # keep ltid for consistency checks during initial scan self.ltid = z64 self.referencesf = referencesf def isReachable(self, oid, pos): """Return 1 if revision of `oid` at `pos` is reachable.""" rpos = self.reachable.get(oid) if rpos is None: return 0 if rpos == pos: return 1 return pos in self.reach_ex.get(oid, []) def findReachable(self): self.buildPackIndex() if self.gc: self.findReachableAtPacktime([z64]) self.findReachableFromFuture() # These mappings are no longer needed and may consume a lot of # space. del self.oid2curpos else: self.reachable = self.oid2curpos def buildPackIndex(self): pos = 4L # We make the initial assumption that the database has been # packed before and set unpacked to True only after seeing the # first record with a status == " ". If we get to the packtime # and unpacked is still False, we need to watch for a redundant # pack. unpacked = False while pos < self.eof: th = self._read_txn_header(pos) if th.tid > self.packtime: break self.checkTxn(th, pos) if th.status != "p": unpacked = True tpos = pos end = pos + th.tlen pos += th.headerlen() while pos < end: dh = self._read_data_header(pos) self.checkData(th, tpos, dh, pos) if dh.plen or dh.back: self.oid2curpos[dh.oid] = pos else: if dh.oid in self.oid2curpos: del self.oid2curpos[dh.oid] pos += dh.recordlen() tlen = self._read_num(pos) if tlen != th.tlen: self.fail( pos, "redundant transaction length does not " "match initial transaction length: %d != %d", tlen, th.tlen) pos += 8 self.packpos = pos if unpacked: return # check for a redundant pack. If the first record following # the newly computed packpos has status 'p', then it was # packed earlier and the current pack is redudant. try: th = self._read_txn_header(pos) except CorruptedDataError, err: if err.buf != "": raise if th.status == 'p': # Delayed import to cope with circular imports. # TODO: put exceptions in a separate module. from ZODB.FileStorage.FileStorage import RedundantPackWarning raise RedundantPackWarning( "The database has already been packed to a later time" " or no changes have been made since the last pack")