############################################################################## # # Zope Public License (ZPL) Version 1.0 # ------------------------------------- # # Copyright (c) Digital Creations. All rights reserved. # Copyright (c) 1999 Tyler C. Sarna. All rights reserved. # # This license has been certified as Open Source(tm). # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions in source code must retain the above copyright # notice, this list of conditions, and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions, and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # 3. Digital Creations requests that attribution be given to Zope # in any manner possible. Zope includes a "Powered by Zope" # button that is installed by default. While it is not a license # violation to remove this button, it is requested that the # attribution remain. A significant investment has been put # into Zope, and this effort will continue if the Zope community # continues to grow. This is one way to assure that growth. # # 4. All advertising materials and documentation mentioning # features derived from or use of this software must display # the following acknowledgement: # # "This product includes software developed by Digital Creations # for use in the Z Object Publishing Environment # (http://www.zope.org/)." # # In the event that the product being advertised includes an # intact Zope distribution (with copyright and license included) # then this clause is waived. # # 5. Names associated with Zope or Digital Creations must not be used to # endorse or promote products derived from this software without # prior written permission from Digital Creations. # # 6. Modified redistributions of any form whatsoever must retain # the following acknowledgment: # # "This product includes software developed by Digital Creations # for use in the Z Object Publishing Environment # (http://www.zope.org/)." # # Intact (re-)distributions of any official Zope release do not # require an external acknowledgement. # # 7. Modifications are encouraged but must be packaged separately as # patches to official Zope releases. Distributions that do not # clearly separate the patches from the original work must be clearly # labeled as unofficial distributions. Modifications which do not # carry the name Zope may be packaged in any form, as long as they # conform to all of the clauses above. # # # Disclaimer # # THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # # This software consists of contributions made by Digital Creations and # many individuals on behalf of Digital Creations. Specific # attributions are listed in the accompanying credits file. # ############################################################################## """SleepyCat Berkeley DB 2.x Storage""" __version__='$Revision: 1.0 $'[11:-2] from TCS.bsddb import db import POSException, BaseStorage, string, utils, Globals, os, stat, operator from TimeStamp import TimeStamp dothread = 0 # db.DB_THREAD db.appinit(Globals.data_dir, db.DB_INIT_TXN | db.DB_CREATE | \ db.DB_INIT_MPOOL | db.DB_INIT_LOCK | dothread) class SleepyStorage(BaseStorage.BaseStorage): def __init__(self, name): BaseStorage.BaseStorage.__init__(self, name) self._name = name self._index = db.DB() self._index.info.db_pagesize = 2048 self._index.open(name, db.DB_BTREE, db.DB_CREATE | dothread) self._tindex = [] self._oid = '\0\0\0\0\0\0\0\0' try: c = self._index.cursor() self._oid, foo = c.get(db.DB_LAST) c.close() except: pass def __len__(self): """return number of objects""" return len(self._index) def getSize(self): """return size of database -- tricky for bsddb! Note that this may include the size of no-longer-needed logfiles""" files = os.listdir(Globals.data_dir) files = filter(lambda x,n=os.path.basename(self._name): \ x == n or len(x) == 14 and x[:4] == 'log.', files) files = map(lambda x,d=Globals.data_dir: os.path.join(d,x), files) sizes = map(lambda x: os.stat(x)[stat.ST_SIZE], files) return reduce(operator.add, sizes) def load(self, oid, version): self._lock_acquire() try: p = self._index[oid] return p[8:], p[:8] # pickle, serial finally: self._lock_release() def store(self, oid, serial, data, version, transaction): if transaction is not self._transaction: raise POSException.StorageTransactionError(self, transaction) if version: raise POSException.Unsupported, "Versions aren't supported" self._lock_acquire() try: if self._index.has_key(oid): #XXX be more efficient -- only get the first 8 bytes #XXX bsddb can do this, but python module can't (yet?) old = self._index[oid] oserial = old[:8] if serial != oserial: raise POSException.ConflictError serial = self._serial self._tindex.append((oid, serial+data)) finally: self._lock_release() return serial def _begin(self, tid, u, d, e): pass def _clear_temp(self): self._tindex = [] def _finish(self, tid, user, desc, ext): index = self._index dbtrans = db.beginTrans() for oid, p in self._tindex: index.put(oid, p, 0, dbtrans) db.commitTrans(dbtrans) def pack(self, t, referencesf): # remember highest valid oid as of the time we started. # any added after this will not be considered candidates for # deletion (otherwise we might delete newly added objects, # incorrectly thinking they're unreferenced) maxoid = self._oid # Build an index of *only* those objects reachable # from the root. index = self._index rootl = ['\0\0\0\0\0\0\0\0'] pop = rootl.pop pindex = {} referenced = pindex.has_key while rootl: oid = pop() if referenced(oid): continue # Scan pickle for references r = index.get(oid) pindex[oid] = r p = r[8:] referencesf(p, rootl) # Now delete any unreferenced entries: deleted=[] c = self._index.cursor() try: try: oid, foo = c.get(db.DB_FIRST) while oid != None: if not referenced(oid) and (oid <= maxoid): deleted.append(oid) oid, foo = c.next() except db.error, (val, estr): if val == db.DB_NOTFOUND: pass # exit loop else: # re-raise raise db.error, (val, estr) finally: c.close() pindex = referenced = None txn = db.beginTrans() ok = 1 try: for oid in deleted: try: index.delete(oid, txn) except db.error, (val, estr): if val == db.DB_NOTFOUND: pass else: # cause txn to be aborted ok = 0 # re-raise raise db.error, (val, estr) finally: if ok: db.commitTrans(txn) else: db.abortTrans(txn) index.sync()