blob: 778ad296020221e524f60421dc32f5ae71bae26f [file] [log] [blame]
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +00001#----------------------------------------------------------------------
2# Copyright (c) 1999-2001, Digital Creations, Fredericksburg, VA, USA
3# and Andrew Kuchling. All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# o Redistributions of source code must retain the above copyright
10# notice, this list of conditions, and the disclaimer that follows.
11#
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
15# distribution.
16#
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.
20#
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
32# DAMAGE.
33#----------------------------------------------------------------------
34
35
Gregory P. Smith41631e82003-09-21 00:08:14 +000036"""Support for BerkeleyDB 3.2 through 4.2.
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000037"""
38
Martin v. Löwis65730a42002-11-24 08:26:01 +000039try:
Gregory P. Smith41631e82003-09-21 00:08:14 +000040 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.
44 import _pybsddb
45 _bsddb = _pybsddb
46 else:
47 import _bsddb
Martin v. Löwis65730a42002-11-24 08:26:01 +000048except ImportError:
49 # Remove ourselves from sys.modules
50 import sys
51 del sys.modules[__name__]
52 raise
Tim Peters0eadaac2003-04-24 16:02:54 +000053
Barry Warsawf71de3e2003-01-28 17:20:44 +000054# bsddb3 calls it db, but provide _db for backwards compatibility
55db = _db = _bsddb
56__version__ = db.__version__
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000057
Barry Warsawf71de3e2003-01-28 17:20:44 +000058error = db.DBError # So bsddb.error will mean something...
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000059
60#----------------------------------------------------------------------
61
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +000062import sys
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000063
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +000064# 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.
67if sys.version >= '2.3':
68 exec """
69import UserDict
70class _iter_mixin(UserDict.DictMixin):
71 def __iter__(self):
72 try:
Gregory P. Smithdc113a82003-11-02 09:10:16 +000073 cur = self.db.cursor()
74 self._iter_cursors[str(cur)] = cur
75
76 # since we're only returning keys, we call the cursor
77 # methods with flags=0, dlen=0, dofs=0
78 curkey = cur.first(0,0,0)[0]
79 yield curkey
80
81 next = cur.next
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +000082 while 1:
Gregory P. Smithdc113a82003-11-02 09:10:16 +000083 try:
84 curkey = next(0,0,0)[0]
85 yield curkey
86 except _bsddb.DBCursorClosedError:
87 # our cursor object was closed since we last yielded
88 # create a new one and attempt to reposition to the
89 # right place
90 cur = self.db.cursor()
91 self._iter_cursors[str(cur)] = cur
92 # FIXME-20031101-greg: race condition. cursor could
93 # be closed by another thread before this set call.
94 try:
95 cur.set(curkey,0,0,0)
96 except _bsddb.DBCursorClosedError:
97 # halt iteration on race condition...
98 raise _bsddb.DBNotFoundError
99 next = cur.next
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +0000100 except _bsddb.DBNotFoundError:
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000101 try:
102 del self._iter_cursors[str(cur)]
103 except KeyError:
104 pass
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +0000105 return
106
107 def iteritems(self):
108 try:
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000109 cur = self.db.cursor()
110 self._iter_cursors[str(cur)] = cur
111
112 kv = cur.first()
113 curkey = kv[0]
114 yield kv
115
116 next = cur.next
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +0000117 while 1:
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000118 try:
119 kv = next()
120 curkey = kv[0]
121 yield kv
122 except _bsddb.DBCursorClosedError:
123 # our cursor object was closed since we last yielded
124 # create a new one and attempt to reposition to the
125 # right place
126 cur = self.db.cursor()
127 self._iter_cursors[str(cur)] = cur
128 # FIXME-20031101-greg: race condition. cursor could
129 # be closed by another thread before this set call.
130 try:
131 cur.set(curkey,0,0,0)
132 except _bsddb.DBCursorClosedError:
133 # halt iteration on race condition...
134 raise _bsddb.DBNotFoundError
135 next = cur.next
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +0000136 except _bsddb.DBNotFoundError:
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000137 try:
138 del self._iter_cursors[str(cur)]
139 except KeyError:
140 pass
Gregory P. Smithcec1b3f2003-09-20 23:51:34 +0000141 return
142"""
143else:
144 class _iter_mixin: pass
145
146
147class _DBWithCursor(_iter_mixin):
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000148 """
149 A simple wrapper around DB that makes it look like the bsddbobject in
150 the old module. It uses a cursor as needed to provide DB traversal.
151 """
152 def __init__(self, db):
153 self.db = db
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000154 self.db.set_get_returns_none(0)
155
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000156 # FIXME-20031101-greg: I believe there is still the potential
157 # for deadlocks in a multithreaded environment if someone
158 # attempts to use the any of the cursor interfaces in one
159 # thread while doing a put or delete in another thread. The
160 # reason is that _checkCursor and _closeCursors are not atomic
161 # operations. Doing our own locking around self.dbc,
162 # self.saved_dbc_key and self._iter_cursors could prevent this.
163 # TODO: A test case demonstrating the problem needs to be written.
164
165 # self.dbc is a DBCursor object used to implement the
166 # first/next/previous/last/set_location methods.
167 self.dbc = None
168 self.saved_dbc_key = None
169
170 # a collection of all DBCursor objects currently allocated
171 # by the _iter_mixin interface.
172 self._iter_cursors = {}
173
174
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000175 def __del__(self):
176 self.close()
177
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000178 def _get_dbc(self):
179 return self.dbc
180
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000181 def _checkCursor(self):
182 if self.dbc is None:
183 self.dbc = self.db.cursor()
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000184 if self.saved_dbc_key is not None:
185 self.dbc.set(self.saved_dbc_key)
186 self.saved_dbc_key = None
187
188 # This method is needed for all non-cursor DB calls to avoid
189 # BerkeleyDB deadlocks (due to being opened with DB_INIT_LOCK
190 # and DB_THREAD to be thread safe) when intermixing database
191 # operations that use the cursor internally with those that don't.
192 def _closeCursors(self, save=True):
193 if self.dbc:
194 c = self.dbc
195 self.dbc = None
196 if save:
197 self.saved_dbc_key = c.current(0,0,0)[0]
198 c.close()
199 del c
200 map(lambda c: c.close(), self._iter_cursors.values())
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000201
202 def _checkOpen(self):
203 if self.db is None:
204 raise error, "BSDDB object has already been closed"
205
206 def isOpen(self):
207 return self.db is not None
208
209 def __len__(self):
210 self._checkOpen()
211 return len(self.db)
212
213 def __getitem__(self, key):
214 self._checkOpen()
215 return self.db[key]
216
217 def __setitem__(self, key, value):
218 self._checkOpen()
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000219 self._closeCursors()
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000220 self.db[key] = value
221
222 def __delitem__(self, key):
223 self._checkOpen()
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000224 self._closeCursors()
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000225 del self.db[key]
226
227 def close(self):
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000228 self._closeCursors(save=False)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000229 if self.dbc is not None:
230 self.dbc.close()
231 v = 0
232 if self.db is not None:
233 v = self.db.close()
234 self.dbc = None
235 self.db = None
236 return v
237
238 def keys(self):
239 self._checkOpen()
240 return self.db.keys()
241
242 def has_key(self, key):
243 self._checkOpen()
244 return self.db.has_key(key)
245
246 def set_location(self, key):
247 self._checkOpen()
248 self._checkCursor()
249 return self.dbc.set(key)
250
251 def next(self):
252 self._checkOpen()
253 self._checkCursor()
254 rv = self.dbc.next()
255 return rv
256
257 def previous(self):
258 self._checkOpen()
259 self._checkCursor()
260 rv = self.dbc.prev()
261 return rv
262
263 def first(self):
264 self._checkOpen()
265 self._checkCursor()
266 rv = self.dbc.first()
267 return rv
268
269 def last(self):
270 self._checkOpen()
271 self._checkCursor()
272 rv = self.dbc.last()
273 return rv
274
275 def sync(self):
276 self._checkOpen()
277 return self.db.sync()
278
Raymond Hettinger2e9da602003-09-13 03:18:34 +0000279
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000280#----------------------------------------------------------------------
281# Compatibility object factory functions
282
283def hashopen(file, flag='c', mode=0666, pgsize=None, ffactor=None, nelem=None,
284 cachesize=None, lorder=None, hflags=0):
285
286 flags = _checkflag(flag)
Gregory P. Smith1eb41e22003-09-27 23:00:19 +0000287 e = _openDBEnv()
288 d = db.DB(e)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000289 d.set_flags(hflags)
290 if cachesize is not None: d.set_cachesize(0, cachesize)
291 if pgsize is not None: d.set_pagesize(pgsize)
292 if lorder is not None: d.set_lorder(lorder)
293 if ffactor is not None: d.set_h_ffactor(ffactor)
294 if nelem is not None: d.set_h_nelem(nelem)
Barry Warsawf71de3e2003-01-28 17:20:44 +0000295 d.open(file, db.DB_HASH, flags, mode)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000296 return _DBWithCursor(d)
297
298#----------------------------------------------------------------------
299
300def btopen(file, flag='c', mode=0666,
301 btflags=0, cachesize=None, maxkeypage=None, minkeypage=None,
302 pgsize=None, lorder=None):
303
304 flags = _checkflag(flag)
Gregory P. Smith1eb41e22003-09-27 23:00:19 +0000305 e = _openDBEnv()
306 d = db.DB(e)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000307 if cachesize is not None: d.set_cachesize(0, cachesize)
308 if pgsize is not None: d.set_pagesize(pgsize)
309 if lorder is not None: d.set_lorder(lorder)
310 d.set_flags(btflags)
311 if minkeypage is not None: d.set_bt_minkey(minkeypage)
312 if maxkeypage is not None: d.set_bt_maxkey(maxkeypage)
Barry Warsawf71de3e2003-01-28 17:20:44 +0000313 d.open(file, db.DB_BTREE, flags, mode)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000314 return _DBWithCursor(d)
315
316#----------------------------------------------------------------------
317
318
319def rnopen(file, flag='c', mode=0666,
320 rnflags=0, cachesize=None, pgsize=None, lorder=None,
321 rlen=None, delim=None, source=None, pad=None):
322
323 flags = _checkflag(flag)
Gregory P. Smith1eb41e22003-09-27 23:00:19 +0000324 e = _openDBEnv()
325 d = db.DB(e)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000326 if cachesize is not None: d.set_cachesize(0, cachesize)
327 if pgsize is not None: d.set_pagesize(pgsize)
328 if lorder is not None: d.set_lorder(lorder)
329 d.set_flags(rnflags)
330 if delim is not None: d.set_re_delim(delim)
331 if rlen is not None: d.set_re_len(rlen)
332 if source is not None: d.set_re_source(source)
333 if pad is not None: d.set_re_pad(pad)
Barry Warsawf71de3e2003-01-28 17:20:44 +0000334 d.open(file, db.DB_RECNO, flags, mode)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000335 return _DBWithCursor(d)
336
337#----------------------------------------------------------------------
338
Gregory P. Smith1eb41e22003-09-27 23:00:19 +0000339def _openDBEnv():
340 e = db.DBEnv()
341 e.open('.', db.DB_PRIVATE | db.DB_CREATE | db.DB_THREAD | db.DB_INIT_LOCK | db.DB_INIT_MPOOL)
342 return e
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000343
344def _checkflag(flag):
345 if flag == 'r':
Barry Warsawf71de3e2003-01-28 17:20:44 +0000346 flags = db.DB_RDONLY
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000347 elif flag == 'rw':
348 flags = 0
349 elif flag == 'w':
Barry Warsawf71de3e2003-01-28 17:20:44 +0000350 flags = db.DB_CREATE
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000351 elif flag == 'c':
Barry Warsawf71de3e2003-01-28 17:20:44 +0000352 flags = db.DB_CREATE
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000353 elif flag == 'n':
Barry Warsawf71de3e2003-01-28 17:20:44 +0000354 flags = db.DB_CREATE | db.DB_TRUNCATE
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000355 else:
356 raise error, "flags should be one of 'r', 'w', 'c' or 'n'"
Barry Warsawf71de3e2003-01-28 17:20:44 +0000357 return flags | db.DB_THREAD
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000358
359#----------------------------------------------------------------------
360
361
362# This is a silly little hack that allows apps to continue to use the
363# DB_THREAD flag even on systems without threads without freaking out
364# BerkeleyDB.
365#
366# This assumes that if Python was built with thread support then
367# BerkeleyDB was too.
368
369try:
370 import thread
371 del thread
372except ImportError:
Barry Warsawf71de3e2003-01-28 17:20:44 +0000373 db.DB_THREAD = 0
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000374
375
376#----------------------------------------------------------------------