[Zope3-checkins] CVS: ZODB4/src/zodb/storage - tmpstore.py:1.1.2.1

Barry Warsaw barry@wooz.org
Mon, 10 Feb 2003 18:16:36 -0500


Update of /cvs-repository/ZODB4/src/zodb/storage
In directory cvs.zope.org:/tmp/cvs-serv6430/src/zodb/storage

Added Files:
      Tag: opaque-pickles-branch
	tmpstore.py 
Log Message:
Move TmpStore and UndoInfo.

.store(): Unpack the data when it's a tuple.


=== Added File ZODB4/src/zodb/storage/tmpstore.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################

import tempfile

from zodb import interfaces
from zodb.interfaces import ZERO
from zodb.utils import p64, u64, Set


class UndoInfo:
    """A helper class for rollback.

    The class stores the state necessary for rolling back to a
    particular time.
    """

    def __init__(self, store, pos, index):
        self._store = store
        self._pos = pos
        self._index = index

    def current(self, cur_store):
        """Return true if the UndoInfo is for cur_store."""
        return self._store is cur_store

    def rollback(self):
        self._store.rollback(self._pos, self._index)


class TmpStore:
    """A storage to support savepoints."""

    _bver = ''

    def __init__(self, base_version):
        self._transaction = None
        if base_version:
            self._bver = base_version
        self._file = tempfile.TemporaryFile()
        # _pos: current file position
        # _tpos: file position at last commit point
        self._pos = self._tpos = 0
        # _index: map oid to pos of last committed version
        self._index = {}
        # _tindex: map oid to pos for new updates
        self._tindex = {}
        self._created = Set()
        self._db = None

    def close(self):
        # XXX Is this necessary?
        self._file.close()

    def getName(self):
        return self._db.getName()

    def getSize(self):
        return self._pos

    def load(self, oid, version):
        pos = self._index.get(oid, None)
        if pos is None:
            return self._storage.load(oid, self._bver)
        self._file.seek(pos)
        h = self._file.read(24)
        if h[:8] != oid:
            raise interfaces.StorageSystemError('Bad temporary storage')
        size = u64(h[16:])
        serial = h[8:16]
        return self._file.read(size), serial

    # XXX clarify difference between self._storage & self._db._storage

    def modifiedInVersion(self, oid):
        if self._index.has_key(oid):
            return self._bver
        return self._db._storage.modifiedInVersion(oid)

    def newObjectId(self):
        return self._db._storage.newObjectId()

    def registerDB(self, db):
        self._db = db
        self._storage = db._storage

    def store(self, oid, serial, data, version, transaction):
        if transaction is not self._transaction:
            raise interfaces.StorageTransactionError(self, transaction)
        # XXX Store this natively and get rid of the conditional split
        if isinstance(data, tuple):
            data, refs = data
        self._file.seek(self._pos)
        l = len(data)
        if serial is None:
            serial = ZERO
        self._file.write(oid + serial + p64(l))
        self._file.write(data)
        self._tindex[oid] = self._pos
        self._pos += l + 24
        return serial

    def tpcAbort(self, transaction):
        if transaction is not self._transaction:
            return
        self._tindex.clear()
        self._transaction = None
        self._pos = self._tpos

    def tpcBegin(self, transaction):
        if self._transaction is transaction:
            return
        self._transaction = transaction
        self._tindex.clear() # Just to be sure!
        self._pos = self._tpos

    def tpcVote(self, transaction):
        pass

    def tpcFinish(self, transaction, f=None):
        if transaction is not self._transaction:
            return
        if f is not None:
            f()
        undo = UndoInfo(self, self._tpos, self._index.copy())
        self._index.update(self._tindex)
        self._tindex.clear()
        self._tpos = self._pos
        return undo

    def undoLog(self, first, last, filter=None):
        return ()

    def versionEmpty(self, version):
        # XXX what is this supposed to do?
        if version == self._bver:
            return len(self._index)

    def rollback(self, pos, index):
        if not (pos <= self._tpos <= self._pos):
            msg = "transaction rolled back to early point"
            raise interfaces.RollbackError(msg)
        self._tpos = self._pos = pos
        self._index = index
        self._tindex.clear()