0001 #---------------------------------------------------------------------- 0002 # Copyright (c) 1999-2001, Digital Creations, Fredericksburg, VA, USA 0003 # and Andrew Kuchling. All rights reserved. 0004 # 0005 # Redistribution and use in source and binary forms, with or without 0006 # modification, are permitted provided that the following conditions are 0007 # met: 0008 # 0009 # o Redistributions of source code must retain the above copyright 0010 # notice, this list of conditions, and the disclaimer that follows. 0011 # 0012 # o Redistributions in binary form must reproduce the above copyright 0013 # notice, this list of conditions, and the following disclaimer in 0014 # the documentation and/or other materials provided with the 0015 # distribution. 0016 # 0017 # o Neither the name of Digital Creations nor the names of its 0018 # contributors may be used to endorse or promote products derived 0019 # from this software without specific prior written permission. 0020 # 0021 # THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS 0022 # IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 0023 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 0024 # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL 0025 # CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 0026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 0027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 0028 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 0029 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 0030 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 0031 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 0032 # DAMAGE. 0033 #---------------------------------------------------------------------- 0034 0035 0036 """Support for BerkeleyDB 3.2 through 4.2. 0037 """ 0038 0039 try: 0040 if __name__ == 'bsddb3': 0041 # import _pybsddb binary as it should be the more recent version from 0042 # a standalone pybsddb addon package than the version included with 0043 # python as bsddb._bsddb. 0044 import _pybsddb 0045 _bsddb = _pybsddb 0046 else: 0047 import _bsddb 0048 except ImportError: 0049 # Remove ourselves from sys.modules 0050 import sys 0051 del sys.modules[__name__] 0052 raise 0053 0054 # bsddb3 calls it db, but provide _db for backwards compatibility 0055 db = _db = _bsddb 0056 __version__ = db.__version__ 0057 0058 error = db.DBError # So bsddb.error will mean something... 0059 0060 #---------------------------------------------------------------------- 0061 0062 import sys, os 0063 0064 # for backwards compatibility with python versions older than 2.3, the 0065 # iterator interface is dynamically defined and added using a mixin 0066 # class. old python can't tokenize it due to the yield keyword. 0067 if sys.version >= '2.3': 0068 exec """ 0069 import UserDict 0070 from weakref import ref 0071 class _iter_mixin(UserDict.DictMixin): 0072 def _make_iter_cursor(self): 0073 cur = self.db.cursor() 0074 key = id(cur) 0075 self._cursor_refs[key] = ref(cur, self._gen_cref_cleaner(key)) 0076 return cur 0077 0078 def _gen_cref_cleaner(self, key): 0079 # use generate the function for the weakref callback here 0080 # to ensure that we do not hold a strict reference to cur 0081 # in the callback. 0082 return lambda ref: self._cursor_refs.pop(key, None) 0083 0084 def __iter__(self): 0085 try: 0086 cur = self._make_iter_cursor() 0087 0088 # FIXME-20031102-greg: race condition. cursor could 0089 # be closed by another thread before this call. 0090 0091 # since we're only returning keys, we call the cursor 0092 # methods with flags=0, dlen=0, dofs=0 0093 key = cur.first(0,0,0)[0] 0094 yield key 0095 0096 next = cur.next 0097 while 1: 0098 try: 0099 key = next(0,0,0)[0] 0100 yield key 0101 except _bsddb.DBCursorClosedError: 0102 cur = self._make_iter_cursor() 0103 # FIXME-20031101-greg: race condition. cursor could 0104 # be closed by another thread before this call. 0105 cur.set(key,0,0,0) 0106 next = cur.next 0107 except _bsddb.DBNotFoundError: 0108 return 0109 except _bsddb.DBCursorClosedError: 0110 # the database was modified during iteration. abort. 0111 return 0112 0113 def iteritems(self): 0114 try: 0115 cur = self._make_iter_cursor() 0116 0117 # FIXME-20031102-greg: race condition. cursor could 0118 # be closed by another thread before this call. 0119 0120 kv = cur.first() 0121 key = kv[0] 0122 yield kv 0123 0124 next = cur.next 0125 while 1: 0126 try: 0127 kv = next() 0128 key = kv[0] 0129 yield kv 0130 except _bsddb.DBCursorClosedError: 0131 cur = self._make_iter_cursor() 0132 # FIXME-20031101-greg: race condition. cursor could 0133 # be closed by another thread before this call. 0134 cur.set(key,0,0,0) 0135 next = cur.next 0136 except _bsddb.DBNotFoundError: 0137 return 0138 except _bsddb.DBCursorClosedError: 0139 # the database was modified during iteration. abort. 0140 return 0141 """ 0142 else: 0143 class _iter_mixin: pass 0144 0145 0146 class _DBWithCursor(_iter_mixin): 0147 """ 0148 A simple wrapper around DB that makes it look like the bsddbobject in 0149 the old module. It uses a cursor as needed to provide DB traversal. 0150 """ 0151 def __init__(self, db): 0152 self.db = db 0153 self.db.set_get_returns_none(0) 0154 0155 # FIXME-20031101-greg: I believe there is still the potential 0156 # for deadlocks in a multithreaded environment if someone 0157 # attempts to use the any of the cursor interfaces in one 0158 # thread while doing a put or delete in another thread. The 0159 # reason is that _checkCursor and _closeCursors are not atomic 0160 # operations. Doing our own locking around self.dbc, 0161 # self.saved_dbc_key and self._cursor_refs could prevent this. 0162 # TODO: A test case demonstrating the problem needs to be written. 0163 0164 # self.dbc is a DBCursor object used to implement the 0165 # first/next/previous/last/set_location methods. 0166 self.dbc = None 0167 self.saved_dbc_key = None 0168 0169 # a collection of all DBCursor objects currently allocated 0170 # by the _iter_mixin interface. 0171 self._cursor_refs = {} 0172 0173 def __del__(self): 0174 self.close() 0175 0176 def _checkCursor(self): 0177 if self.dbc is None: 0178 self.dbc = self.db.cursor() 0179 if self.saved_dbc_key is not None: 0180 self.dbc.set(self.saved_dbc_key) 0181 self.saved_dbc_key = None 0182 0183 # This method is needed for all non-cursor DB calls to avoid 0184 # BerkeleyDB deadlocks (due to being opened with DB_INIT_LOCK 0185 # and DB_THREAD to be thread safe) when intermixing database 0186 # operations that use the cursor internally with those that don't. 0187 def _closeCursors(self, save=1): 0188 if self.dbc: 0189 c = self.dbc 0190 self.dbc = None 0191 if save: 0192 self.saved_dbc_key = c.current(0,0,0)[0] 0193 c.close() 0194 del c 0195 for cref in self._cursor_refs.values(): 0196 c = cref() 0197 if c is not None: 0198 c.close() 0199 0200 def _checkOpen(self): 0201 if self.db is None: 0202 raise error, "BSDDB object has already been closed" 0203 0204 def isOpen(self): 0205 return self.db is not None 0206 0207 def __len__(self): 0208 self._checkOpen() 0209 return len(self.db) 0210 0211 def __getitem__(self, key): 0212 self._checkOpen() 0213 return self.db[key] 0214 0215 def __setitem__(self, key, value): 0216 self._checkOpen() 0217 self._closeCursors() 0218 self.db[key] = value 0219 0220 def __delitem__(self, key): 0221 self._checkOpen() 0222 self._closeCursors() 0223 del self.db[key] 0224 0225 def close(self): 0226 self._closeCursors(save=0) 0227 if self.dbc is not None: 0228 self.dbc.close() 0229 v = 0 0230 if self.db is not None: 0231 v = self.db.close() 0232 self.dbc = None 0233 self.db = None 0234 return v 0235 0236 def keys(self): 0237 self._checkOpen() 0238 return self.db.keys() 0239 0240 def has_key(self, key): 0241 self._checkOpen() 0242 return self.db.has_key(key) 0243 0244 def set_location(self, key): 0245 self._checkOpen() 0246 self._checkCursor() 0247 return self.dbc.set_range(key) 0248 0249 def next(self): 0250 self._checkOpen() 0251 self._checkCursor() 0252 rv = self.dbc.next() 0253 return rv 0254 0255 def previous(self): 0256 self._checkOpen() 0257 self._checkCursor() 0258 rv = self.dbc.prev() 0259 return rv 0260 0261 def first(self): 0262 self._checkOpen() 0263 self._checkCursor() 0264 rv = self.dbc.first() 0265 return rv 0266 0267 def last(self): 0268 self._checkOpen() 0269 self._checkCursor() 0270 rv = self.dbc.last() 0271 return rv 0272 0273 def sync(self): 0274 self._checkOpen() 0275 return self.db.sync() 0276 0277 0278 #---------------------------------------------------------------------- 0279 # Compatibility object factory functions 0280 0281 def hashopen(file, flag='c', mode=0666, pgsize=None, ffactor=None, nelem=None, 0282 cachesize=None, lorder=None, hflags=0): 0283 0284 flags = _checkflag(flag, file) 0285 e = _openDBEnv() 0286 d = db.DB(e) 0287 d.set_flags(hflags) 0288 if cachesize is not None: d.set_cachesize(0, cachesize) 0289 if pgsize is not None: d.set_pagesize(pgsize) 0290 if lorder is not None: d.set_lorder(lorder) 0291 if ffactor is not None: d.set_h_ffactor(ffactor) 0292 if nelem is not None: d.set_h_nelem(nelem) 0293 d.open(file, db.DB_HASH, flags, mode) 0294 return _DBWithCursor(d) 0295 0296 #---------------------------------------------------------------------- 0297 0298 def btopen(file, flag='c', mode=0666, 0299 btflags=0, cachesize=None, maxkeypage=None, minkeypage=None, 0300 pgsize=None, lorder=None): 0301 0302 flags = _checkflag(flag, file) 0303 e = _openDBEnv() 0304 d = db.DB(e) 0305 if cachesize is not None: d.set_cachesize(0, cachesize) 0306 if pgsize is not None: d.set_pagesize(pgsize) 0307 if lorder is not None: d.set_lorder(lorder) 0308 d.set_flags(btflags) 0309 if minkeypage is not None: d.set_bt_minkey(minkeypage) 0310 if maxkeypage is not None: d.set_bt_maxkey(maxkeypage) 0311 d.open(file, db.DB_BTREE, flags, mode) 0312 return _DBWithCursor(d) 0313 0314 #---------------------------------------------------------------------- 0315 0316 0317 def rnopen(file, flag='c', mode=0666, 0318 rnflags=0, cachesize=None, pgsize=None, lorder=None, 0319 rlen=None, delim=None, source=None, pad=None): 0320 0321 flags = _checkflag(flag, file) 0322 e = _openDBEnv() 0323 d = db.DB(e) 0324 if cachesize is not None: d.set_cachesize(0, cachesize) 0325 if pgsize is not None: d.set_pagesize(pgsize) 0326 if lorder is not None: d.set_lorder(lorder) 0327 d.set_flags(rnflags) 0328 if delim is not None: d.set_re_delim(delim) 0329 if rlen is not None: d.set_re_len(rlen) 0330 if source is not None: d.set_re_source(source) 0331 if pad is not None: d.set_re_pad(pad) 0332 d.open(file, db.DB_RECNO, flags, mode) 0333 return _DBWithCursor(d) 0334 0335 #---------------------------------------------------------------------- 0336 0337 def _openDBEnv(): 0338 e = db.DBEnv() 0339 e.open('.', db.DB_PRIVATE | db.DB_CREATE | db.DB_THREAD | db.DB_INIT_LOCK | db.DB_INIT_MPOOL) 0340 return e 0341 0342 def _checkflag(flag, file): 0343 if flag == 'r': 0344 flags = db.DB_RDONLY 0345 elif flag == 'rw': 0346 flags = 0 0347 elif flag == 'w': 0348 flags = db.DB_CREATE 0349 elif flag == 'c': 0350 flags = db.DB_CREATE 0351 elif flag == 'n': 0352 flags = db.DB_CREATE 0353 #flags = db.DB_CREATE | db.DB_TRUNCATE 0354 # we used db.DB_TRUNCATE flag for this before but BerkeleyDB 0355 # 4.2.52 changed to disallowed truncate with txn environments. 0356 if os.path.isfile(file): 0357 os.unlink(file) 0358 else: 0359 raise error, "flags should be one of 'r', 'w', 'c' or 'n'" 0360 return flags | db.DB_THREAD 0361 0362 #---------------------------------------------------------------------- 0363 0364 0365 # This is a silly little hack that allows apps to continue to use the 0366 # DB_THREAD flag even on systems without threads without freaking out 0367 # BerkeleyDB. 0368 # 0369 # This assumes that if Python was built with thread support then 0370 # BerkeleyDB was too. 0371 0372 try: 0373 import thread 0374 del thread 0375 except ImportError: 0376 db.DB_THREAD = 0 0377 0378 0379 #---------------------------------------------------------------------- 0380
Generated by PyXR 0.9.4