Commit 3e3800a2 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 0431ca7b
......@@ -14,7 +14,7 @@
"""
# Do this portably in the face of checking out with -kv
import string
__version__ = string.split('$Revision: 1.16 $')[-2:][0]
__version__ = string.split('$Revision: 1.17 $')[-2:][0]
import ThreadLock, bpthread
import time, UndoLogCompatible
......@@ -26,6 +26,7 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
_transaction=None # Transaction that is being committed
_serial=z64 # Transaction serial number
_tstatus=' ' # Transaction status, used for copying data
_is_read_only = 0
def __init__(self, name, base=None):
......@@ -42,8 +43,10 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
t=time.time()
t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
self._serial=`t`
if base is None: self._oid='\0\0\0\0\0\0\0\0'
else: self._oid=base._oid
if base is None:
self._oid='\0\0\0\0\0\0\0\0'
else:
self._oid=base._oid
def abortVersion(self, src, transaction):
if transaction is not self._transaction:
......@@ -55,15 +58,24 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
raise POSException.StorageTransactionError(self, transaction)
return []
def close(self): pass
def close(self):
pass
def getName(self): return self.__name__
def getSize(self): return len(self)*300 # WAG!
def history(self, oid, version, length=1): pass
def getName(self):
return self.__name__
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):
if self._is_read_only:
raise POSException.ReadOnlyError()
if last is None:
self._lock_acquire()
try:
......@@ -79,10 +91,17 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
if d < 255: return last[:-1]+chr(d+1)+'\0'*(8-len(last))
else: return self.new_oid(last[:-1])
def registerDB(self, db, limit): pass # we don't care
def supportsUndo(self): return 0
def supportsVersions(self): return 0
def registerDB(self, db, limit):
pass # we don't care
def isReadOnly(self):
return self._is_read_only
def supportsUndo(self):
return 0
def supportsVersions(self):
return 0
def tpc_abort(self, transaction):
self._lock_acquire()
......@@ -171,15 +190,22 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
pass
def undo(self, transaction_id):
if self._is_read_only:
raise POSException.ReadOnlyError()
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):
self._lock_acquire()
......@@ -232,3 +258,9 @@ class BaseStorage(UndoLogCompatible.UndoLogCompatible):
self.tpc_vote(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 @@
##############################################################################
"""Database connection support
$Id: Connection.py,v 1.61 2001/11/28 15:51:18 matt Exp $"""
__version__='$Revision: 1.61 $'[11:-2]
$Id: Connection.py,v 1.62 2002/01/17 17:34:33 jeremy Exp $"""
__version__='$Revision: 1.62 $'[11:-2]
from cPickleCache import PickleCache
from POSException import ConflictError
from POSException import ConflictError, ReadConflictError
from ExtensionClass import Base
import ExportImport, TmpStore
from zLOG import LOG, ERROR, BLATHER
......@@ -248,7 +248,7 @@ class Connection(ExportImport.ExportImport):
or
invalid(None)
):
raise ConflictError, `oid`
raise ConflictError(object=object)
self._invalidating.append(oid)
else:
......@@ -315,7 +315,7 @@ class Connection(ExportImport.ExportImport):
or
invalid(None)
):
raise ConflictError, `oid`
raise ConflictError(object=object)
self._invalidating.append(oid)
klass = object.__class__
......@@ -459,7 +459,7 @@ class Connection(ExportImport.ExportImport):
if invalid(oid) or invalid(None):
if not hasattr(object.__class__, '_p_independent'):
get_transaction().register(self)
raise ConflictError(`oid`, `object.__class__`)
raise ReadConflictError(object=object)
invalid=1
else:
invalid=0
......@@ -484,7 +484,7 @@ class Connection(ExportImport.ExportImport):
except KeyError: pass
else:
get_transaction().register(self)
raise ConflictError(`oid`, `object.__class__`)
raise ConflictError(object=object)
except ConflictError:
raise
......@@ -544,7 +544,7 @@ class Connection(ExportImport.ExportImport):
def tpc_begin(self, transaction, sub=None):
if self._invalid(None): # Some nitwit invalidated everything!
raise ConflictError, "transaction already invalidated"
raise ConflictError("transaction already invalidated")
self._invalidating=[]
self._creating=[]
......
......@@ -78,7 +78,7 @@ method::
and call it to monitor the storage.
"""
__version__='$Revision: 1.8 $'[11:-2]
__version__='$Revision: 1.9 $'[11:-2]
import base64, time, string
from ZODB import POSException, BaseStorage, utils
......@@ -244,7 +244,8 @@ class DemoStorage(BaseStorage.BaseStorage):
else:
nv=old
if serial != oserial: raise POSException.ConflictError
if serial != oserial:
raise POSException.ConflictError(serials=(oserial, serial))
serial=self._serial
r=[oid, serial, old, version and (version, nv) or None, data]
......
This diff is collapsed.
......@@ -86,7 +86,7 @@ method::
and call it to minotor the storage.
"""
__version__='$Revision: 1.4 $'[11:-2]
__version__='$Revision: 1.5 $'[11:-2]
import POSException, BaseStorage, string, utils
from TimeStamp import TimeStamp
......@@ -136,7 +136,8 @@ class MappingStorage(BaseStorage.BaseStorage):
if self._index.has_key(oid):
old=self._index[oid]
oserial=old[:8]
if serial != oserial: raise POSException.ConflictError
if serial != oserial:
raise POSException.ConflictError(serials=(oserial, serial))
serial=self._serial
self._tindex.append((oid,serial+data))
......
......@@ -10,14 +10,14 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''BoboPOS-defined exceptions
"""BoboPOS-defined exceptions
$Id: POSException.py,v 1.8 2001/11/28 15:51:20 matt Exp $'''
__version__='$Revision: 1.8 $'[11:-2]
$Id: POSException.py,v 1.9 2002/01/17 17:34:33 jeremy Exp $"""
__version__ = '$Revision: 1.9 $'.split()[-2:][0]
from string import join
StringType=type('')
DictType=type({})
from types import StringType, DictType
from ZODB import utils
class POSError(Exception):
"""Persistent object system error
......@@ -28,10 +28,94 @@ class TransactionError(POSError):
"""
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):
"""An error in handling versions occurred
......@@ -84,6 +168,10 @@ class MountedStorageError(StorageError):
"""Unable to access mounted storage.
"""
class ReadOnlyError(StorageError):
"""Unable to modify objects in a read-only storage.
"""
class ExportError(POSError):
"""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