[Zodb-checkins] CVS: Zope2/lib/python/ZODB - FileStorage.py:1.52
Jim Fulton
jim@digicool.com
Wed, 11 Apr 2001 14:29:01 -0400 (EDT)
Update of /cvs-repository/Zope2/lib/python/ZODB
In directory korak:/tmp/cvs-serv2258
Modified Files:
FileStorage.py
Log Message:
Added arguments in undoLog to match the spec.
Removed some log-ago-commented-out code.
Added some doc strings.
Added a bunch of not-yet-enabled (because not yet fully working)
transactional undo code.
--- Updated File FileStorage.py in package Zope2/lib/python/ZODB --
--- FileStorage.py 2001/03/15 13:16:26 1.51
+++ FileStorage.py 2001/04/11 18:29:00 1.52
@@ -329,7 +329,6 @@
self._vindex_get=vindex.get
self._tappend=tindex.append
-
def __len__(self): return len(self._index)
def _newIndexes(self): return {}, {}, [], {}
@@ -528,18 +527,6 @@
# Yee ha! We can quit
break
- # The following optimization fails miserably
- # if, for some reason, an object is written twice
- # in the same transaction!
- #elif h[16:24] == pnv and pnv != z64:
- # # This is the first current record, so unmark it.
- # # Note that we don't need to check if this was
- # # undone. If it *was* undone, then there must
- # # be a later record that is the first record, or
- # # there isn't a current record. In either case,
- # # we can't be in this branch. :)
- # del current_oids[oid]
-
spos=h[-8:]
srcpos=U64(spos)
@@ -849,8 +836,181 @@
for oid, pos in t.items(): index[oid]=pos
return t.keys()
finally: self._lock_release()
+
+ def supportsTransactionalUndo(self): return 0
+
+ def _dataInfo(self, oid, pos):
+ """Return the serial, version and data pointer for the oid
+ record at pos"""
+ file=self._file
+ read=file.read
+ file.seek(pos)
+ h=read(42)
+ roid,serial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
+ if roid != oid:
+ raise POSException.UndoError, 'Invalid undo transaction id'
+ if vlen:
+ read(16) # skip nv pointer and version previous pointer
+ version=read(vlen)
+ else:
+ version=''
+
+ if U64(splen):
+ return serial, pos, version
+ else:
+ return serial, U64(read(8)), version
+
+ def _getVersion(self, oid, pos):
+ self._file.seek(pos)
+ read=self._file.read
+ h=read(42)
+ doid,serial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
+ if vlen:
+ h=read(16)
+ return read(vlen), h[:8]
+ else:
+ return '',''
+
+ def _getSerial(self, oid, pos):
+ self._file.seek(pos+8)
+ return self._file.read(8)
+
+
+ def _transactionalUndoRecord(self, oid, pos, serial, pre, version):
+ """Get the indo information for a data record
+
+ Return a 5-tuple consisting of a pickle, data pointer,
+ version, packed non-version data pointer, and current
+ position. If the pickle is true, then the data pointer must
+ be 0, but the pickle can be empty *and* the pointer 0.
+
+ """
+
+ copy=1 # Can we just copy a data pointer
+ ipos=self._index.get(oid,0)
+ if ipos != pos:
+ # Eek, a later transaction modified the data, but,
+ # maybe it is pointing at the same data we are.
+ cserial, cdata, cver = self._dataInfo(oid, ipos)
+ # Versions of undone record and current record *must* match!
+ if cver != version:
+ raise POSException.UndoError(
+ 'non-undoable transaction')
+
+ if cdata != pos:
+ # We aren't sure if we are talking about the same data
+ if (
+ cdata == ipos # The current record wrote a new pickle
+ or
+ # Backpointers are different
+ _loadBackPOS(self._file, oid, p64(pos)) !=
+ _loadBackPOS(self._file, oid, p64(cdata))
+ ):
+ if pre:
+ copy=0 # we'll try to do conflict resolution
+ else:
+ raise POSException.UndoError(
+ 'non-undoable transaction')
+
+ version, snv = self._getVersion(oid, pre)
+ if copy:
+ # we can just copy our previous-record pointer forward
+ return '', pre, version, snv, ipos
+
+ data=self.tryToResolveConflict(
+ oid, cserial, serial, _loadBack(self._file, oid, p64(pre)))
+
+ if data:
+ return data, 0, version, snv, ipos
+
+ raise POSException.UndoError(
+ 'non-undoable transaction')
+
+ def transactionalUndo(self, transaction_id, transaction):
+ """Undo a transaction, given by transaction_id.
+
+ Do so by writing new data that reverses tyhe action taken by
+ the transaction."""
+ # Usually, we can get by with just copying a data pointer, by
+ # writing a file position rather than a pickle. Sometimes, we
+ # may do conflict resolution, in which case we actually copy
+ # new data that results from resolution.
+
+ if transaction is not self._transaction:
+ raise POSException.StorageTransactionError(self, transaction)
+
+ self._lock_acquire()
+ try:
+ transaction_id=base64.decodestring(transaction_id+'==\n')
+ tid, tpos = transaction_id[:8], U64(transaction_id[8:])
+
+ seek=self._file.seek
+ read=self._file.read
+ unpack=struct.unpack
+ write=self._tfile.write
+
+ ostloc = p64(self._pos)
+ newserial=self._serial
+ here=self._pos+(self._tfile.tell()+self._thl)
+
+ seek(tpos)
+ h=read(23)
+ if len(h) != 23 or h[:8] != tid:
+ raise POSException.UndoError, 'Invalid undo transaction id'
+ if h[16] == 'u': return
+ if h[16] != ' ':
+ raise POSException.UndoError, 'non-undoable transaction'
+ tl=U64(h[8:16])
+ ul,dl,el=unpack(">HHH", h[17:23])
+ tend=tpos+tl
+ pos=tpos+(23+ul+dl+el)
+ tindex={}
+ # Read the data records for this transaction
+ while pos < tend:
+ seek(pos)
+ h=read(42)
+ oid,serial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
+ plen=U64(splen)
+ prev=U64(sprev)
+ if vlen:
+ dlen=58+vlen+(plen or 8)
+ read(16)
+ version=read(vlen)
+ else:
+ dlen=42+(plen or 8)
+ version=''
+
+ p, prev, v, snv, ipos = self._transactionalUndoRecord(
+ oid, pos, serial, prev, version)
+
+ plen=len(p)
+ write(pack(">8s8s8s8sH8s",
+ oid, newserial, p64(ipos), ostloc,
+ len(v), p64(plen)))
+ if v:
+ vprev=self._tvindex.get(v, self._vindex.get(v, 0))
+ write(snv+p64(vprev)+v)
+ self._tvindex[v]=here
+ odlen = 58+len(v)+(plen or 8)
+ else:
+ odlen = 42+(plen or 8)
+
+ if p: write(p)
+ else: write(p64(prev))
+
+ pos=pos+dlen
+ if pos > tend:
+ raise POSException.UndoError, 'non-undoable transaction'
+ tindex[oid]=here
+ here=here+odlen
+
+ self._tindex[len(self._tindex):] = tindex.items()
+ return tindex.keys()
+
+ finally: self._lock_release()
- def undoLog(self, first, last, filter=None):
+ def undoLog(self, first=0, last=-20, filter=None):
+ if last < 0: last=first-last+1
self._lock_acquire()
try:
packt=self._packt
@@ -1719,6 +1879,8 @@
back=read(8) # We got a back pointer!
def _loadBackPOS(file, oid, back):
+ """Return the position of the record containing the data used by
+ the record at the given position (back)."""
seek=file.seek
read=file.read