Commit bc1f63a1 authored by Barry Warsaw's avatar Barry Warsaw

InternalInconsistencyError => POSException.StorageSystemError

_finish(): In delete-a-version clause, use next_dup() instead of
next() to iterate over the currentVersions table with cursor set to
the vid of the version we're deleting.

abortVersion(): When cruising over the currentVersions table (and the
key set to vid of the version we're aborting), use next_dup() instead
of next().  Also, when curvid <> vid, raise StorageSystemError instead
of VersionError.  Same later on when we check curvid <> zero.

commitVersion(): Add sanity checking based on model documentation: if
the source version is the empty string, or src == dest, raise a
VersionCommitError.  Make the same changes we made previously to
abortVersion() to keep track of the oids in a set/dictionary instead
of a list to avoid duplicates.  Other fixes include, writing revid
instead of tid==self._serial to write_moved_object() and adding a
write_discard_version(svid) once the source version's been committed.

commitVersion() now works!

load(): Add another check to avoid finding an object revision in a
committed or aborted version if we can guess that version name.  It
now raises a VersionError if vid==zero and version is true.

Also, although we still raise VersionError later on, elaborate the
actual exception better in the error message.
parent 5abae7de
...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support ...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support
undo or versioning. undo or versioning.
""" """
# $Revision: 1.9 $ # $Revision: 1.10 $
__version__ = '0.1' __version__ = '0.1'
import struct import struct
...@@ -36,11 +36,6 @@ PROTECTED_TRANSACTION = 'N' ...@@ -36,11 +36,6 @@ PROTECTED_TRANSACTION = 'N'
zero = '\0'*8 zero = '\0'*8
class InternalInconsistencyError(POSException.POSError, AssertionError):
"""Raised when we detect an internal inconsistency in our tables."""
class Full(BerkeleyBase): class Full(BerkeleyBase):
# #
...@@ -297,7 +292,7 @@ class Full(BerkeleyBase): ...@@ -297,7 +292,7 @@ class Full(BerkeleyBase):
rec = c.set(vid) rec = c.set(vid)
while rec: while rec:
c.delete() c.delete()
rec = c.next() rec = c.next_dup()
finally: finally:
c.close() c.close()
...@@ -345,29 +340,28 @@ class Full(BerkeleyBase): ...@@ -345,29 +340,28 @@ class Full(BerkeleyBase):
# information for undo. # information for undo.
while rec: while rec:
oid = rec[1] # ignore the key oid = rec[1] # ignore the key
rec = c.next() rec = c.next_dup()
if oids.has_key(oid): if oids.has_key(oid):
# We've already dealt with this oid... # We've already dealt with this oid...
continue continue
revid = self._serials[oid] revid = self._serials[oid]
meta = self._metadata[oid+revid] meta = self._metadata[oid+revid]
curvid, nvrevid = struct.unpack('8s8s', meta[:16]) curvid, nvrevid = struct.unpack('>8s8s', meta[:16])
# Make sure that the vid in the metadata record is the same as # Make sure that the vid in the metadata record is the same as
# the vid we sucked out of the vids table. # the vid we sucked out of the vids table.
if curvid <> vid: if curvid <> vid:
raise POSException.VersionError( raise POSException.StorageSystemError
'aborting a non-current version')
if nvrevid == zero: if nvrevid == zero:
# This object was created in the version, so we don't need # This object was created in the version, so we don't need
# to do anything about it. # to do anything about it.
continue continue
# Get the non-version data for the object # Get the non-version data for the object
nvmeta = self._metadata[oid+nvrevid] nvmeta = self._metadata[oid+nvrevid]
curvid, nvrevid, lrevid = struct.unpack('8s8s8s', nvmeta[:24]) curvid, nvrevid, lrevid = struct.unpack('>8s8s8s', nvmeta[:24])
# We expect curvid to be zero because we just got the # We expect curvid to be zero because we just got the
# non-version entry. # non-version entry.
if curvid <> zero: if curvid <> zero:
raise InternalInconsistencyError raise POSException.StorageSystemError
# Write the object id, live revision id, the current revision # Write the object id, live revision id, the current revision
# id (which serves as the previous revid to this transaction) # id (which serves as the previous revid to this transaction)
# to the commit log. # to the commit log.
...@@ -393,11 +387,13 @@ class Full(BerkeleyBase): ...@@ -393,11 +387,13 @@ class Full(BerkeleyBase):
if transaction is not self._transaction: if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction) raise POSException.StorageTransactionError(self, transaction)
# Sanity checks
if not src or src == dest:
raise POSException.VersionCommitError
c = None # the currentVersions cursor c = None # the currentVersions cursor
self._lock_acquire() self._lock_acquire()
try: try:
# The transaction id for this commit
tid = self._serial
# Get the version ids associated with the source and destination # Get the version ids associated with the source and destination
# version strings. # version strings.
svid = self._vids[src] svid = self._vids[src]
...@@ -407,34 +403,38 @@ class Full(BerkeleyBase): ...@@ -407,34 +403,38 @@ class Full(BerkeleyBase):
# Find the vid for the destination version, or create one if # Find the vid for the destination version, or create one if
# necessary. # necessary.
dvid = self.__findcreatevid(dest) dvid = self.__findcreatevid(dest)
# Keep track of the oids affected by this commit. # Keep track of the oids affected by this commit. For why this is
oids = [] # a dictionary (really a set), see abortVersion() above.
oids = {}
c = self._currentVersions.cursor() c = self._currentVersions.cursor()
rec = c.set(vid) rec = c.set(svid)
# Now cruise through all the records for this version, writing to # Now cruise through all the records for this version, writing to
# the commit log all the objects changed in this version. # the commit log all the objects changed in this version.
while rec: while rec:
oid = rec[1] # ignore the key oid = rec[1] # ignore the key
rec = c.next_dup()
if oids.has_key(oid):
continue
revid = self._serials[oid] revid = self._serials[oid]
meta = self._metadata[oid+revid] meta = self._metadata[oid+revid]
curvid, nvrevid, lrevid = struct.unpack('8s8s8s', meta[:24]) curvid, nvrevid, lrevid = struct.unpack('>8s8s8s', meta[:24])
# Our database better be consistent. # Our database better be consistent.
if curvid <> svid: if curvid <> svid:
raise InternalInconsistencyError raise POSException.StorageSystemError(
'oid: %s, moving from v%s to v%s, but lives in v%s' %
tuple(map(utils.U64, (oid, svid, dvid, curvid))))
# If we're committing to a non-version, then the non-version # If we're committing to a non-version, then the non-version
# revision id ought to be zero also, regardless of what it was # revision id ought to be zero also, regardless of what it was
# for the source version. # for the source version.
if not dest: if not dest:
nvrevid = zero nvrevid = zero
self._commitlog.write_moved_object( self._commitlog.write_moved_object(
oid, dvid, nvrevid, lrevid, tid) oid, dvid, nvrevid, lrevid, revid)
# Remember to return the oid... # Remember to return the oid...
oids.append(oid) oids[oid] = 1
# ...and get the next record for this vid
rec = c.next()
# Now that we're done, we can discard this version # Now that we're done, we can discard this version
self._commitlog.write_discard_version(vid) self._commitlog.write_discard_version(svid)
return oids return oids.keys()
finally: finally:
if c: if c:
c.close() c.close()
...@@ -487,6 +487,9 @@ class Full(BerkeleyBase): ...@@ -487,6 +487,9 @@ class Full(BerkeleyBase):
# object is living in is equal to the version that's being # object is living in is equal to the version that's being
# requested, then we can simply return the pickle referenced by # requested, then we can simply return the pickle referenced by
# the revid. # the revid.
if vid == zero and version:
raise POSException.VersionError(
'Object not found in version: %s' % version)
if vid == zero or self._versions[vid] == version: if vid == zero or self._versions[vid] == version:
return self._pickles[oid+lrevid], revid return self._pickles[oid+lrevid], revid
# Otherwise, we recognize that an object cannot be stored in more # Otherwise, we recognize that an object cannot be stored in more
...@@ -494,8 +497,11 @@ class Full(BerkeleyBase): ...@@ -494,8 +497,11 @@ class Full(BerkeleyBase):
# "Unlocked" versions are added). So we return the non-version # "Unlocked" versions are added). So we return the non-version
# revision of the object. Make sure the version is empty though. # revision of the object. Make sure the version is empty though.
if version: if version:
raise POSException.VersionError( if not self._vids.has_key(version):
'Undefined version: %s' % version) errmsg = 'Undefined version: %s' % version
else:
errmsg = 'Object not found in version: %s' % version
raise POSException.VersionError(errmsg)
lrevid = self._metadata[oid+nvrevid][16:24] lrevid = self._metadata[oid+nvrevid][16:24]
return self._pickles[oid+lrevid], nvrevid return self._pickles[oid+lrevid], nvrevid
finally: finally:
...@@ -654,7 +660,7 @@ class Full(BerkeleyBase): ...@@ -654,7 +660,7 @@ class Full(BerkeleyBase):
oids = [] oids = []
for oid, rec in newrevs: for oid, rec in newrevs:
vid, nvrevid, lrevid, prevrevid = struct.unpack( vid, nvrevid, lrevid, prevrevid = struct.unpack(
'8s8s8s8s', rec) '>8s8s8s8s', rec)
self._commitlog.write_moved_object(oid, vid, nvrevid, lrevid, self._commitlog.write_moved_object(oid, vid, nvrevid, lrevid,
prevrevid) prevrevid)
oids.append(oid) oids.append(oid)
......
...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support ...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support
undo or versioning. undo or versioning.
""" """
# $Revision: 1.9 $ # $Revision: 1.10 $
__version__ = '0.1' __version__ = '0.1'
import struct import struct
...@@ -36,11 +36,6 @@ PROTECTED_TRANSACTION = 'N' ...@@ -36,11 +36,6 @@ PROTECTED_TRANSACTION = 'N'
zero = '\0'*8 zero = '\0'*8
class InternalInconsistencyError(POSException.POSError, AssertionError):
"""Raised when we detect an internal inconsistency in our tables."""
class Full(BerkeleyBase): class Full(BerkeleyBase):
# #
...@@ -297,7 +292,7 @@ class Full(BerkeleyBase): ...@@ -297,7 +292,7 @@ class Full(BerkeleyBase):
rec = c.set(vid) rec = c.set(vid)
while rec: while rec:
c.delete() c.delete()
rec = c.next() rec = c.next_dup()
finally: finally:
c.close() c.close()
...@@ -345,29 +340,28 @@ class Full(BerkeleyBase): ...@@ -345,29 +340,28 @@ class Full(BerkeleyBase):
# information for undo. # information for undo.
while rec: while rec:
oid = rec[1] # ignore the key oid = rec[1] # ignore the key
rec = c.next() rec = c.next_dup()
if oids.has_key(oid): if oids.has_key(oid):
# We've already dealt with this oid... # We've already dealt with this oid...
continue continue
revid = self._serials[oid] revid = self._serials[oid]
meta = self._metadata[oid+revid] meta = self._metadata[oid+revid]
curvid, nvrevid = struct.unpack('8s8s', meta[:16]) curvid, nvrevid = struct.unpack('>8s8s', meta[:16])
# Make sure that the vid in the metadata record is the same as # Make sure that the vid in the metadata record is the same as
# the vid we sucked out of the vids table. # the vid we sucked out of the vids table.
if curvid <> vid: if curvid <> vid:
raise POSException.VersionError( raise POSException.StorageSystemError
'aborting a non-current version')
if nvrevid == zero: if nvrevid == zero:
# This object was created in the version, so we don't need # This object was created in the version, so we don't need
# to do anything about it. # to do anything about it.
continue continue
# Get the non-version data for the object # Get the non-version data for the object
nvmeta = self._metadata[oid+nvrevid] nvmeta = self._metadata[oid+nvrevid]
curvid, nvrevid, lrevid = struct.unpack('8s8s8s', nvmeta[:24]) curvid, nvrevid, lrevid = struct.unpack('>8s8s8s', nvmeta[:24])
# We expect curvid to be zero because we just got the # We expect curvid to be zero because we just got the
# non-version entry. # non-version entry.
if curvid <> zero: if curvid <> zero:
raise InternalInconsistencyError raise POSException.StorageSystemError
# Write the object id, live revision id, the current revision # Write the object id, live revision id, the current revision
# id (which serves as the previous revid to this transaction) # id (which serves as the previous revid to this transaction)
# to the commit log. # to the commit log.
...@@ -393,11 +387,13 @@ class Full(BerkeleyBase): ...@@ -393,11 +387,13 @@ class Full(BerkeleyBase):
if transaction is not self._transaction: if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction) raise POSException.StorageTransactionError(self, transaction)
# Sanity checks
if not src or src == dest:
raise POSException.VersionCommitError
c = None # the currentVersions cursor c = None # the currentVersions cursor
self._lock_acquire() self._lock_acquire()
try: try:
# The transaction id for this commit
tid = self._serial
# Get the version ids associated with the source and destination # Get the version ids associated with the source and destination
# version strings. # version strings.
svid = self._vids[src] svid = self._vids[src]
...@@ -407,34 +403,38 @@ class Full(BerkeleyBase): ...@@ -407,34 +403,38 @@ class Full(BerkeleyBase):
# Find the vid for the destination version, or create one if # Find the vid for the destination version, or create one if
# necessary. # necessary.
dvid = self.__findcreatevid(dest) dvid = self.__findcreatevid(dest)
# Keep track of the oids affected by this commit. # Keep track of the oids affected by this commit. For why this is
oids = [] # a dictionary (really a set), see abortVersion() above.
oids = {}
c = self._currentVersions.cursor() c = self._currentVersions.cursor()
rec = c.set(vid) rec = c.set(svid)
# Now cruise through all the records for this version, writing to # Now cruise through all the records for this version, writing to
# the commit log all the objects changed in this version. # the commit log all the objects changed in this version.
while rec: while rec:
oid = rec[1] # ignore the key oid = rec[1] # ignore the key
rec = c.next_dup()
if oids.has_key(oid):
continue
revid = self._serials[oid] revid = self._serials[oid]
meta = self._metadata[oid+revid] meta = self._metadata[oid+revid]
curvid, nvrevid, lrevid = struct.unpack('8s8s8s', meta[:24]) curvid, nvrevid, lrevid = struct.unpack('>8s8s8s', meta[:24])
# Our database better be consistent. # Our database better be consistent.
if curvid <> svid: if curvid <> svid:
raise InternalInconsistencyError raise POSException.StorageSystemError(
'oid: %s, moving from v%s to v%s, but lives in v%s' %
tuple(map(utils.U64, (oid, svid, dvid, curvid))))
# If we're committing to a non-version, then the non-version # If we're committing to a non-version, then the non-version
# revision id ought to be zero also, regardless of what it was # revision id ought to be zero also, regardless of what it was
# for the source version. # for the source version.
if not dest: if not dest:
nvrevid = zero nvrevid = zero
self._commitlog.write_moved_object( self._commitlog.write_moved_object(
oid, dvid, nvrevid, lrevid, tid) oid, dvid, nvrevid, lrevid, revid)
# Remember to return the oid... # Remember to return the oid...
oids.append(oid) oids[oid] = 1
# ...and get the next record for this vid
rec = c.next()
# Now that we're done, we can discard this version # Now that we're done, we can discard this version
self._commitlog.write_discard_version(vid) self._commitlog.write_discard_version(svid)
return oids return oids.keys()
finally: finally:
if c: if c:
c.close() c.close()
...@@ -487,6 +487,9 @@ class Full(BerkeleyBase): ...@@ -487,6 +487,9 @@ class Full(BerkeleyBase):
# object is living in is equal to the version that's being # object is living in is equal to the version that's being
# requested, then we can simply return the pickle referenced by # requested, then we can simply return the pickle referenced by
# the revid. # the revid.
if vid == zero and version:
raise POSException.VersionError(
'Object not found in version: %s' % version)
if vid == zero or self._versions[vid] == version: if vid == zero or self._versions[vid] == version:
return self._pickles[oid+lrevid], revid return self._pickles[oid+lrevid], revid
# Otherwise, we recognize that an object cannot be stored in more # Otherwise, we recognize that an object cannot be stored in more
...@@ -494,8 +497,11 @@ class Full(BerkeleyBase): ...@@ -494,8 +497,11 @@ class Full(BerkeleyBase):
# "Unlocked" versions are added). So we return the non-version # "Unlocked" versions are added). So we return the non-version
# revision of the object. Make sure the version is empty though. # revision of the object. Make sure the version is empty though.
if version: if version:
raise POSException.VersionError( if not self._vids.has_key(version):
'Undefined version: %s' % version) errmsg = 'Undefined version: %s' % version
else:
errmsg = 'Object not found in version: %s' % version
raise POSException.VersionError(errmsg)
lrevid = self._metadata[oid+nvrevid][16:24] lrevid = self._metadata[oid+nvrevid][16:24]
return self._pickles[oid+lrevid], nvrevid return self._pickles[oid+lrevid], nvrevid
finally: finally:
...@@ -654,7 +660,7 @@ class Full(BerkeleyBase): ...@@ -654,7 +660,7 @@ class Full(BerkeleyBase):
oids = [] oids = []
for oid, rec in newrevs: for oid, rec in newrevs:
vid, nvrevid, lrevid, prevrevid = struct.unpack( vid, nvrevid, lrevid, prevrevid = struct.unpack(
'8s8s8s8s', rec) '>8s8s8s8s', rec)
self._commitlog.write_moved_object(oid, vid, nvrevid, lrevid, self._commitlog.write_moved_object(oid, vid, nvrevid, lrevid,
prevrevid) prevrevid)
oids.append(oid) oids.append(oid)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment