1 #----------------------------------------------------------------------
2 # Copyright (c) 1999-2001, Digital Creations, Fredericksburg, VA, USA
3 # and Andrew Kuchling. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
9 # o Redistributions of source code must retain the above copyright
10 # notice, this list of conditions, and the disclaimer that follows.
12 # o Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions, and the following disclaimer in
14 # the documentation and/or other materials provided with the
17 # o Neither the name of Digital Creations nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
21 # THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS
22 # IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24 # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
25 # CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
31 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
33 #----------------------------------------------------------------------
36 """Support for BerkeleyDB 3.2 through 4.2.
40 if __name__
== 'bsddb3':
41 # import _pybsddb binary as it should be the more recent version from
42 # a standalone pybsddb addon package than the version included with
43 # python as bsddb._bsddb.
49 # Remove ourselves from sys.modules
51 del sys
.modules
[__name__
]
54 # bsddb3 calls it db, but provide _db for backwards compatibility
56 __version__
= db
.__version
__
58 error
= db
.DBError
# So bsddb.error will mean something...
60 #----------------------------------------------------------------------
64 # for backwards compatibility with python versions older than 2.3, the
65 # iterator interface is dynamically defined and added using a mixin
66 # class. old python can't tokenize it due to the yield keyword.
67 if sys
.version
>= '2.3':
70 from weakref import ref
71 class _iter_mixin(UserDict.DictMixin):
72 def _make_iter_cursor(self):
73 cur = self.db.cursor()
75 self._cursor_refs[key] = ref(cur, self._gen_cref_cleaner(key))
78 def _gen_cref_cleaner(self, key):
79 # use generate the function for the weakref callback here
80 # to ensure that we do not hold a strict reference to cur
82 return lambda ref: self._cursor_refs.pop(key, None)
86 cur = self._make_iter_cursor()
88 # FIXME-20031102-greg: race condition. cursor could
89 # be closed by another thread before this call.
91 # since we're only returning keys, we call the cursor
92 # methods with flags=0, dlen=0, dofs=0
93 key = cur.first(0,0,0)[0]
101 except _bsddb.DBCursorClosedError:
102 cur = self._make_iter_cursor()
103 # FIXME-20031101-greg: race condition. cursor could
104 # be closed by another thread before this call.
107 except _bsddb.DBNotFoundError:
109 except _bsddb.DBCursorClosedError:
110 # the database was modified during iteration. abort.
115 cur = self._make_iter_cursor()
117 # FIXME-20031102-greg: race condition. cursor could
118 # be closed by another thread before this call.
130 except _bsddb.DBCursorClosedError:
131 cur = self._make_iter_cursor()
132 # FIXME-20031101-greg: race condition. cursor could
133 # be closed by another thread before this call.
136 except _bsddb.DBNotFoundError:
138 except _bsddb.DBCursorClosedError:
139 # the database was modified during iteration. abort.
143 class _iter_mixin
: pass
146 class _DBWithCursor(_iter_mixin
):
148 A simple wrapper around DB that makes it look like the bsddbobject in
149 the old module. It uses a cursor as needed to provide DB traversal.
151 def __init__(self
, db
):
153 self
.db
.set_get_returns_none(0)
155 # FIXME-20031101-greg: I believe there is still the potential
156 # for deadlocks in a multithreaded environment if someone
157 # attempts to use the any of the cursor interfaces in one
158 # thread while doing a put or delete in another thread. The
159 # reason is that _checkCursor and _closeCursors are not atomic
160 # operations. Doing our own locking around self.dbc,
161 # self.saved_dbc_key and self._cursor_refs could prevent this.
162 # TODO: A test case demonstrating the problem needs to be written.
164 # self.dbc is a DBCursor object used to implement the
165 # first/next/previous/last/set_location methods.
167 self
.saved_dbc_key
= None
169 # a collection of all DBCursor objects currently allocated
170 # by the _iter_mixin interface.
171 self
._cursor
_refs
= {}
176 def _checkCursor(self
):
178 self
.dbc
= self
.db
.cursor()
179 if self
.saved_dbc_key
is not None:
180 self
.dbc
.set(self
.saved_dbc_key
)
181 self
.saved_dbc_key
= None
183 # This method is needed for all non-cursor DB calls to avoid
184 # BerkeleyDB deadlocks (due to being opened with DB_INIT_LOCK
185 # and DB_THREAD to be thread safe) when intermixing database
186 # operations that use the cursor internally with those that don't.
187 def _closeCursors(self
, save
=1):
192 self
.saved_dbc_key
= c
.current(0,0,0)[0]
195 for cref
in self
._cursor
_refs
.values():
200 def _checkOpen(self
):
202 raise error
, "BSDDB object has already been closed"
205 return self
.db
is not None
211 def __getitem__(self
, key
):
215 def __setitem__(self
, key
, value
):
220 def __delitem__(self
, key
):
226 self
._closeCursors
(save
=0)
227 if self
.dbc
is not None:
230 if self
.db
is not None:
238 return self
.db
.keys()
240 def has_key(self
, key
):
242 return self
.db
.has_key(key
)
244 def set_location(self
, key
):
247 return self
.dbc
.set_range(key
)
264 rv
= self
.dbc
.first()
275 return self
.db
.sync()
278 #----------------------------------------------------------------------
279 # Compatibility object factory functions
281 def hashopen(file, flag
='c', mode
=0666, pgsize
=None, ffactor
=None, nelem
=None,
282 cachesize
=None, lorder
=None, hflags
=0):
284 flags
= _checkflag(flag
, file)
288 if cachesize
is not None: d
.set_cachesize(0, cachesize
)
289 if pgsize
is not None: d
.set_pagesize(pgsize
)
290 if lorder
is not None: d
.set_lorder(lorder
)
291 if ffactor
is not None: d
.set_h_ffactor(ffactor
)
292 if nelem
is not None: d
.set_h_nelem(nelem
)
293 d
.open(file, db
.DB_HASH
, flags
, mode
)
294 return _DBWithCursor(d
)
296 #----------------------------------------------------------------------
298 def btopen(file, flag
='c', mode
=0666,
299 btflags
=0, cachesize
=None, maxkeypage
=None, minkeypage
=None,
300 pgsize
=None, lorder
=None):
302 flags
= _checkflag(flag
, file)
305 if cachesize
is not None: d
.set_cachesize(0, cachesize
)
306 if pgsize
is not None: d
.set_pagesize(pgsize
)
307 if lorder
is not None: d
.set_lorder(lorder
)
309 if minkeypage
is not None: d
.set_bt_minkey(minkeypage
)
310 if maxkeypage
is not None: d
.set_bt_maxkey(maxkeypage
)
311 d
.open(file, db
.DB_BTREE
, flags
, mode
)
312 return _DBWithCursor(d
)
314 #----------------------------------------------------------------------
317 def rnopen(file, flag
='c', mode
=0666,
318 rnflags
=0, cachesize
=None, pgsize
=None, lorder
=None,
319 rlen
=None, delim
=None, source
=None, pad
=None):
321 flags
= _checkflag(flag
, file)
324 if cachesize
is not None: d
.set_cachesize(0, cachesize
)
325 if pgsize
is not None: d
.set_pagesize(pgsize
)
326 if lorder
is not None: d
.set_lorder(lorder
)
328 if delim
is not None: d
.set_re_delim(delim
)
329 if rlen
is not None: d
.set_re_len(rlen
)
330 if source
is not None: d
.set_re_source(source
)
331 if pad
is not None: d
.set_re_pad(pad
)
332 d
.open(file, db
.DB_RECNO
, flags
, mode
)
333 return _DBWithCursor(d
)
335 #----------------------------------------------------------------------
339 e
.open('.', db
.DB_PRIVATE | db
.DB_CREATE | db
.DB_THREAD | db
.DB_INIT_LOCK | db
.DB_INIT_MPOOL
)
342 def _checkflag(flag
, file):
353 #flags = db.DB_CREATE | db.DB_TRUNCATE
354 # we used db.DB_TRUNCATE flag for this before but BerkeleyDB
355 # 4.2.52 changed to disallowed truncate with txn environments.
356 if os
.path
.isfile(file):
359 raise error
, "flags should be one of 'r', 'w', 'c' or 'n'"
360 return flags | db
.DB_THREAD
362 #----------------------------------------------------------------------
365 # This is a silly little hack that allows apps to continue to use the
366 # DB_THREAD flag even on systems without threads without freaking out
369 # This assumes that if Python was built with thread support then
370 # BerkeleyDB was too.
379 #----------------------------------------------------------------------