Commit c2eed46c authored by Jeremy Hylton's avatar Jeremy Hylton

Merge Standby-branch to trunk (mostly).

The Standby-branch was branched from the StandaloneZODB-1_0-branch,
which includes the BTrees-fsIndex code.  I didn't include that change
in the merge, but everything else.  Terse summary follows:

BaseStorage.py:
    Add read-only storage feature.
    Add TransactionRecord and DataRecord marker classes for iteration.
    Reformat some lines.

FileStorage.py:
    Add read-only storage feature.
    Greg Ward's ConflictError patch
    Reformat some lines.
    Add lastTransaction(), lastSerialno().
    Add bounds support to iterator().
    Use TransactionRecord and DataRecord.

Connection.py:
DemoStorage.py:
MappingStorage.py:
    Greg Ward's ConflictError patch

POSException.py:
    Greg Ward's ConflictError patch
    Add ReadOnlyError.
parent 9e154d03
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
""" """
# Do this portably in the face of checking out with -kv # Do this portably in the face of checking out with -kv
import string import string
__version__ = string.split('$Revision: 1.16 $')[-2:][0] __version__ = string.split('$Revision: 1.17 $')[-2:][0]
import ThreadLock, bpthread import ThreadLock, bpthread
import time, UndoLogCompatible import time, UndoLogCompatible
...@@ -26,6 +26,7 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible): ...@@ -26,6 +26,7 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
_transaction=None # Transaction that is being committed _transaction=None # Transaction that is being committed
_serial=z64 # Transaction serial number _serial=z64 # Transaction serial number
_tstatus=' ' # Transaction status, used for copying data _tstatus=' ' # Transaction status, used for copying data
_is_read_only = 0
def __init__(self, name, base=None): def __init__(self, name, base=None):
...@@ -42,8 +43,10 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible): ...@@ -42,8 +43,10 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
t=time.time() t=time.time()
t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,))) t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
self._serial=`t` self._serial=`t`
if base is None: self._oid='\0\0\0\0\0\0\0\0' if base is None:
else: self._oid=base._oid self._oid='\0\0\0\0\0\0\0\0'
else:
self._oid=base._oid
def abortVersion(self, src, transaction): def abortVersion(self, src, transaction):
if transaction is not self._transaction: if transaction is not self._transaction:
...@@ -55,15 +58,24 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible): ...@@ -55,15 +58,24 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
raise POSException.StorageTransactionError(self, transaction) raise POSException.StorageTransactionError(self, transaction)
return [] return []
def close(self): pass def close(self):
pass
def getName(self): return self.__name__ def getName(self):
def getSize(self): return len(self)*300 # WAG! return self.__name__
def history(self, oid, version, length=1): pass
def getSize(self):
return len(self)*300 # WAG!
def history(self, oid, version, length=1):
pass
def modifiedInVersion(self, oid): return '' def modifiedInVersion(self, oid):
return ''
def new_oid(self, last=None): def new_oid(self, last=None):
if self._is_read_only:
raise POSException.ReadOnlyError()
if last is None: if last is None:
self._lock_acquire() self._lock_acquire()
try: try:
...@@ -79,10 +91,17 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible): ...@@ -79,10 +91,17 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
if d < 255: return last[:-1]+chr(d+1)+'\0'*(8-len(last)) if d < 255: return last[:-1]+chr(d+1)+'\0'*(8-len(last))
else: return self.new_oid(last[:-1]) else: return self.new_oid(last[:-1])
def registerDB(self, db, limit): pass # we don't care def registerDB(self, db, limit):
pass # we don't care
def supportsUndo(self): return 0
def supportsVersions(self): return 0 def isReadOnly(self):
return self._is_read_only
def supportsUndo(self):
return 0
def supportsVersions(self):
return 0
def tpc_abort(self, transaction): def tpc_abort(self, transaction):
self._lock_acquire() self._lock_acquire()
...@@ -171,15 +190,22 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible): ...@@ -171,15 +190,22 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
pass pass
def undo(self, transaction_id): def undo(self, transaction_id):
if self._is_read_only:
raise POSException.ReadOnlyError()
raise POSException.UndoError, 'non-undoable transaction' raise POSException.UndoError, 'non-undoable transaction'
def undoLog(self, first, last, filter=None): return () def undoLog(self, first, last, filter=None):
return ()
def versionEmpty(self, version): return 1 def versionEmpty(self, version):
return 1
def versions(self, max=None): return () def versions(self, max=None):
return ()
def pack(self, t, referencesf): pass def pack(self, t, referencesf):
if self._is_read_only:
raise POSException.ReadOnlyError()
def getSerial(self, oid): def getSerial(self, oid):
self._lock_acquire() self._lock_acquire()
...@@ -232,3 +258,9 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible): ...@@ -232,3 +258,9 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
self.tpc_vote(transaction) self.tpc_vote(transaction)
self.tpc_finish(transaction) self.tpc_finish(transaction)
class TransactionRecord:
"""Abstract base class for iterator protocol"""
class DataRecord:
"""Abstract base class for iterator protocol"""
...@@ -12,11 +12,11 @@ ...@@ -12,11 +12,11 @@
############################################################################## ##############################################################################
"""Database connection support """Database connection support
$Id: Connection.py,v 1.61 2001/11/28 15:51:18 matt Exp $""" $Id: Connection.py,v 1.62 2002/01/17 17:34:33 jeremy Exp $"""
__version__='$Revision: 1.61 $'[11:-2] __version__='$Revision: 1.62 $'[11:-2]
from cPickleCache import PickleCache from cPickleCache import PickleCache
from POSException import ConflictError from POSException import ConflictError, ReadConflictError
from ExtensionClass import Base from ExtensionClass import Base
import ExportImport, TmpStore import ExportImport, TmpStore
from zLOG import LOG, ERROR, BLATHER from zLOG import LOG, ERROR, BLATHER
...@@ -248,7 +248,7 @@ class Connection(ExportImport.ExportImport): ...@@ -248,7 +248,7 @@ class Connection(ExportImport.ExportImport):
or or
invalid(None) invalid(None)
): ):
raise ConflictError, `oid` raise ConflictError(object=object)
self._invalidating.append(oid) self._invalidating.append(oid)
else: else:
...@@ -315,7 +315,7 @@ class Connection(ExportImport.ExportImport): ...@@ -315,7 +315,7 @@ class Connection(ExportImport.ExportImport):
or or
invalid(None) invalid(None)
): ):
raise ConflictError, `oid` raise ConflictError(object=object)
self._invalidating.append(oid) self._invalidating.append(oid)
klass = object.__class__ klass = object.__class__
...@@ -459,7 +459,7 @@ class Connection(ExportImport.ExportImport): ...@@ -459,7 +459,7 @@ class Connection(ExportImport.ExportImport):
if invalid(oid) or invalid(None): if invalid(oid) or invalid(None):
if not hasattr(object.__class__, '_p_independent'): if not hasattr(object.__class__, '_p_independent'):
get_transaction().register(self) get_transaction().register(self)
raise ConflictError(`oid`, `object.__class__`) raise ReadConflictError(object=object)
invalid=1 invalid=1
else: else:
invalid=0 invalid=0
...@@ -484,7 +484,7 @@ class Connection(ExportImport.ExportImport): ...@@ -484,7 +484,7 @@ class Connection(ExportImport.ExportImport):
except KeyError: pass except KeyError: pass
else: else:
get_transaction().register(self) get_transaction().register(self)
raise ConflictError(`oid`, `object.__class__`) raise ConflictError(object=object)
except ConflictError: except ConflictError:
raise raise
...@@ -544,7 +544,7 @@ class Connection(ExportImport.ExportImport): ...@@ -544,7 +544,7 @@ class Connection(ExportImport.ExportImport):
def tpc_begin(self, transaction, sub=None): def tpc_begin(self, transaction, sub=None):
if self._invalid(None): # Some nitwit invalidated everything! if self._invalid(None): # Some nitwit invalidated everything!
raise ConflictError, "transaction already invalidated" raise ConflictError("transaction already invalidated")
self._invalidating=[] self._invalidating=[]
self._creating=[] self._creating=[]
......
...@@ -78,7 +78,7 @@ method:: ...@@ -78,7 +78,7 @@ method::
and call it to monitor the storage. and call it to monitor the storage.
""" """
__version__='$Revision: 1.8 $'[11:-2] __version__='$Revision: 1.9 $'[11:-2]
import base64, time, string import base64, time, string
from ZODB import POSException, BaseStorage, utils from ZODB import POSException, BaseStorage, utils
...@@ -244,7 +244,8 @@ class DemoStorage(BaseStorage.BaseStorage): ...@@ -244,7 +244,8 @@ class DemoStorage(BaseStorage.BaseStorage):
else: else:
nv=old nv=old
if serial != oserial: raise POSException.ConflictError if serial != oserial:
raise POSException.ConflictError(serials=(oserial, serial))
serial=self._serial serial=self._serial
r=[oid, serial, old, version and (version, nv) or None, data] r=[oid, serial, old, version and (version, nv) or None, data]
......
This diff is collapsed.
...@@ -86,7 +86,7 @@ method:: ...@@ -86,7 +86,7 @@ method::
and call it to minotor the storage. and call it to minotor the storage.
""" """
__version__='$Revision: 1.4 $'[11:-2] __version__='$Revision: 1.5 $'[11:-2]
import POSException, BaseStorage, string, utils import POSException, BaseStorage, string, utils
from TimeStamp import TimeStamp from TimeStamp import TimeStamp
...@@ -136,7 +136,8 @@ class MappingStorage(BaseStorage.BaseStorage): ...@@ -136,7 +136,8 @@ class MappingStorage(BaseStorage.BaseStorage):
if self._index.has_key(oid): if self._index.has_key(oid):
old=self._index[oid] old=self._index[oid]
oserial=old[:8] oserial=old[:8]
if serial != oserial: raise POSException.ConflictError if serial != oserial:
raise POSException.ConflictError(serials=(oserial, serial))
serial=self._serial serial=self._serial
self._tindex.append((oid,serial+data)) self._tindex.append((oid,serial+data))
......
...@@ -10,14 +10,14 @@ ...@@ -10,14 +10,14 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
'''BoboPOS-defined exceptions """BoboPOS-defined exceptions
$Id: POSException.py,v 1.8 2001/11/28 15:51:20 matt Exp $''' $Id: POSException.py,v 1.9 2002/01/17 17:34:33 jeremy Exp $"""
__version__='$Revision: 1.8 $'[11:-2] __version__ = '$Revision: 1.9 $'.split()[-2:][0]
from string import join from string import join
StringType=type('') from types import StringType, DictType
DictType=type({}) from ZODB import utils
class POSError(Exception): class POSError(Exception):
"""Persistent object system error """Persistent object system error
...@@ -28,10 +28,94 @@ class TransactionError(POSError): ...@@ -28,10 +28,94 @@ class TransactionError(POSError):
""" """
class ConflictError(TransactionError): class ConflictError(TransactionError):
"""Two transactions tried to modify the same object at once """Two transactions tried to modify the same object at once. This
transaction should be resubmitted.
Instance attributes:
oid : string
the OID (8-byte packed string) of the object in conflict
class_name : string
the fully-qualified name of that object's class
message : string
a human-readable explanation of the error
serials : (string, string)
a pair of 8-byte packed strings; these are the serial numbers
(old and new) of the object in conflict. (Serial numbers are
closely related [equal?] to transaction IDs; a ConflictError may
be triggered by a serial number mismatch.)
The caller should pass either object or oid as a keyword argument,
but not both of them. If object is passed, it should be a
persistent object with an _p_oid attribute.
"""
This transaction should be resubmitted. def __init__(self, message=None, object=None, oid=None, serials=None):
if message is None:
self.message = "database conflict error"
else:
self.message = message
if object is None:
self.oid = None
self.class_name = None
else:
self.oid = object._p_oid
klass = object.__class__
self.class_name = klass.__module__ + "." + klass.__name__
if oid is not None:
assert self.oid is None
self.oid = oid
self.serials = serials
def __str__(self):
extras = []
if self.oid:
extras.append("oid %016x" % utils.U64(self.oid))
if self.class_name:
extras.append("class %s" % self.class_name)
if self.serials:
extras.append("serial was %016x, now %016x" %
tuple(map(utils.U64, self.serials)))
if extras:
return "%s (%s)" % (self.message, ", ".join(extras))
else:
return self.message
def get_oid(self):
return self.oid
def get_class_name(self):
return self.class_name
def get_old_serial(self):
return self.serials[0]
def get_new_serial(self):
return self.serials[1]
def get_serials(self):
return self.serials
class ReadConflictError(ConflictError):
"""A conflict detected at read time -- attempt to read an object
that has changed in another transaction (eg. another thread
or process).
""" """
def __init__(self, message=None, object=None, serials=None):
if message is None:
message = "database read conflict error"
ConflictError.__init__(self, message=message, object=object,
serials=serials)
class BTreesConflictError(ConflictError):
"""A special subclass for BTrees conflict errors, which return
an undocumented four-tuple."""
def __init__(self, *btree_args):
ConflictError.__init__(self, message="BTrees conflict error")
self.btree = btree_args
class VersionError(POSError): class VersionError(POSError):
"""An error in handling versions occurred """An error in handling versions occurred
...@@ -84,6 +168,10 @@ class MountedStorageError(StorageError): ...@@ -84,6 +168,10 @@ class MountedStorageError(StorageError):
"""Unable to access mounted storage. """Unable to access mounted storage.
""" """
class ReadOnlyError(StorageError):
"""Unable to modify objects in a read-only storage.
"""
class ExportError(POSError): class ExportError(POSError):
"""An export file doesn't have the right format. """An export file doesn't have the right format.
""" """
......
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