Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 1 | #---------------------------------------------------------------------- |
| 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. Smith | 41631e8 | 2003-09-21 00:08:14 +0000 | [diff] [blame] | 36 | """Support for BerkeleyDB 3.2 through 4.2. |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 37 | """ |
| 38 | |
Martin v. Löwis | 65730a4 | 2002-11-24 08:26:01 +0000 | [diff] [blame] | 39 | try: |
Gregory P. Smith | 41631e8 | 2003-09-21 00:08:14 +0000 | [diff] [blame] | 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. |
| 44 | import _pybsddb |
| 45 | _bsddb = _pybsddb |
| 46 | else: |
| 47 | import _bsddb |
Martin v. Löwis | 65730a4 | 2002-11-24 08:26:01 +0000 | [diff] [blame] | 48 | except ImportError: |
| 49 | # Remove ourselves from sys.modules |
| 50 | import sys |
| 51 | del sys.modules[__name__] |
| 52 | raise |
Tim Peters | 0eadaac | 2003-04-24 16:02:54 +0000 | [diff] [blame] | 53 | |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 54 | # bsddb3 calls it db, but provide _db for backwards compatibility |
| 55 | db = _db = _bsddb |
| 56 | __version__ = db.__version__ |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 57 | |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 58 | error = db.DBError # So bsddb.error will mean something... |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 59 | |
| 60 | #---------------------------------------------------------------------- |
| 61 | |
Gregory P. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 62 | import sys |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 63 | |
Gregory P. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 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': |
| 68 | exec """ |
| 69 | import UserDict |
| 70 | class _iter_mixin(UserDict.DictMixin): |
| 71 | def __iter__(self): |
| 72 | try: |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 73 | 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. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 82 | while 1: |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 83 | 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. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 100 | except _bsddb.DBNotFoundError: |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 101 | try: |
| 102 | del self._iter_cursors[str(cur)] |
| 103 | except KeyError: |
| 104 | pass |
Gregory P. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 105 | return |
| 106 | |
| 107 | def iteritems(self): |
| 108 | try: |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 109 | 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. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 117 | while 1: |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 118 | 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. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 136 | except _bsddb.DBNotFoundError: |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 137 | try: |
| 138 | del self._iter_cursors[str(cur)] |
| 139 | except KeyError: |
| 140 | pass |
Gregory P. Smith | cec1b3f | 2003-09-20 23:51:34 +0000 | [diff] [blame] | 141 | return |
| 142 | """ |
| 143 | else: |
| 144 | class _iter_mixin: pass |
| 145 | |
| 146 | |
| 147 | class _DBWithCursor(_iter_mixin): |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 148 | """ |
| 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öwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 154 | self.db.set_get_returns_none(0) |
| 155 | |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 156 | # 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öwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 175 | def __del__(self): |
| 176 | self.close() |
| 177 | |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 178 | def _get_dbc(self): |
| 179 | return self.dbc |
| 180 | |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 181 | def _checkCursor(self): |
| 182 | if self.dbc is None: |
| 183 | self.dbc = self.db.cursor() |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 184 | 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öwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 201 | |
| 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. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 219 | self._closeCursors() |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 220 | self.db[key] = value |
| 221 | |
| 222 | def __delitem__(self, key): |
| 223 | self._checkOpen() |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 224 | self._closeCursors() |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 225 | del self.db[key] |
| 226 | |
| 227 | def close(self): |
Gregory P. Smith | dc113a8 | 2003-11-02 09:10:16 +0000 | [diff] [blame^] | 228 | self._closeCursors(save=False) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 229 | 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 Hettinger | 2e9da60 | 2003-09-13 03:18:34 +0000 | [diff] [blame] | 279 | |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 280 | #---------------------------------------------------------------------- |
| 281 | # Compatibility object factory functions |
| 282 | |
| 283 | def 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. Smith | 1eb41e2 | 2003-09-27 23:00:19 +0000 | [diff] [blame] | 287 | e = _openDBEnv() |
| 288 | d = db.DB(e) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 289 | 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 Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 295 | d.open(file, db.DB_HASH, flags, mode) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 296 | return _DBWithCursor(d) |
| 297 | |
| 298 | #---------------------------------------------------------------------- |
| 299 | |
| 300 | def 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. Smith | 1eb41e2 | 2003-09-27 23:00:19 +0000 | [diff] [blame] | 305 | e = _openDBEnv() |
| 306 | d = db.DB(e) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 307 | 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 Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 313 | d.open(file, db.DB_BTREE, flags, mode) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 314 | return _DBWithCursor(d) |
| 315 | |
| 316 | #---------------------------------------------------------------------- |
| 317 | |
| 318 | |
| 319 | def 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. Smith | 1eb41e2 | 2003-09-27 23:00:19 +0000 | [diff] [blame] | 324 | e = _openDBEnv() |
| 325 | d = db.DB(e) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 326 | 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 Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 334 | d.open(file, db.DB_RECNO, flags, mode) |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 335 | return _DBWithCursor(d) |
| 336 | |
| 337 | #---------------------------------------------------------------------- |
| 338 | |
Gregory P. Smith | 1eb41e2 | 2003-09-27 23:00:19 +0000 | [diff] [blame] | 339 | def _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öwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 343 | |
| 344 | def _checkflag(flag): |
| 345 | if flag == 'r': |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 346 | flags = db.DB_RDONLY |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 347 | elif flag == 'rw': |
| 348 | flags = 0 |
| 349 | elif flag == 'w': |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 350 | flags = db.DB_CREATE |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 351 | elif flag == 'c': |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 352 | flags = db.DB_CREATE |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 353 | elif flag == 'n': |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 354 | flags = db.DB_CREATE | db.DB_TRUNCATE |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 355 | else: |
| 356 | raise error, "flags should be one of 'r', 'w', 'c' or 'n'" |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 357 | return flags | db.DB_THREAD |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 358 | |
| 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 | |
| 369 | try: |
| 370 | import thread |
| 371 | del thread |
| 372 | except ImportError: |
Barry Warsaw | f71de3e | 2003-01-28 17:20:44 +0000 | [diff] [blame] | 373 | db.DB_THREAD = 0 |
Martin v. Löwis | 6aa4a1f | 2002-11-19 08:09:52 +0000 | [diff] [blame] | 374 | |
| 375 | |
| 376 | #---------------------------------------------------------------------- |