source: python/vendor/Python-2.7.6/Lib/bsddb/dbtables.py

Last change on this file was 388, checked in by dmik, 11 years ago

python: Update vendor to 2.7.6.

  • Property svn:eol-style set to native
File size: 30.1 KB
Line 
1#-----------------------------------------------------------------------
2#
3# Copyright (C) 2000, 2001 by Autonomous Zone Industries
4# Copyright (C) 2002 Gregory P. Smith
5#
6# License: This is free software. You may use this software for any
7# purpose including modification/redistribution, so long as
8# this header remains intact and that you do not claim any
9# rights of ownership or authorship of this software. This
10# software has been tested, but no warranty is expressed or
11# implied.
12#
13# -- Gregory P. Smith <greg@krypto.org>
14
15# This provides a simple database table interface built on top of
16# the Python Berkeley DB 3 interface.
17#
18_cvsid = '$Id$'
19
20import re
21import sys
22import copy
23import random
24import struct
25
26
27if sys.version_info[0] >= 3 :
28 import pickle
29else :
30 if sys.version_info < (2, 6) :
31 import cPickle as pickle
32 else :
33 # When we drop support for python 2.4
34 # we could use: (in 2.5 we need a __future__ statement)
35 #
36 # with warnings.catch_warnings():
37 # warnings.filterwarnings(...)
38 # ...
39 #
40 # We can not use "with" as is, because it would be invalid syntax
41 # in python 2.4 and (with no __future__) 2.5.
42 # Here we simulate "with" following PEP 343 :
43 import warnings
44 w = warnings.catch_warnings()
45 w.__enter__()
46 try :
47 warnings.filterwarnings('ignore',
48 message='the cPickle module has been removed in Python 3.0',
49 category=DeprecationWarning)
50 import cPickle as pickle
51 finally :
52 w.__exit__()
53 del w
54
55try:
56 # For Pythons w/distutils pybsddb
57 from bsddb3 import db
58except ImportError:
59 # For Python 2.3
60 from bsddb import db
61
62class TableDBError(StandardError):
63 pass
64class TableAlreadyExists(TableDBError):
65 pass
66
67
68class Cond:
69 """This condition matches everything"""
70 def __call__(self, s):
71 return 1
72
73class ExactCond(Cond):
74 """Acts as an exact match condition function"""
75 def __init__(self, strtomatch):
76 self.strtomatch = strtomatch
77 def __call__(self, s):
78 return s == self.strtomatch
79
80class PrefixCond(Cond):
81 """Acts as a condition function for matching a string prefix"""
82 def __init__(self, prefix):
83 self.prefix = prefix
84 def __call__(self, s):
85 return s[:len(self.prefix)] == self.prefix
86
87class PostfixCond(Cond):
88 """Acts as a condition function for matching a string postfix"""
89 def __init__(self, postfix):
90 self.postfix = postfix
91 def __call__(self, s):
92 return s[-len(self.postfix):] == self.postfix
93
94class LikeCond(Cond):
95 """
96 Acts as a function that will match using an SQL 'LIKE' style
97 string. Case insensitive and % signs are wild cards.
98 This isn't perfect but it should work for the simple common cases.
99 """
100 def __init__(self, likestr, re_flags=re.IGNORECASE):
101 # escape python re characters
102 chars_to_escape = '.*+()[]?'
103 for char in chars_to_escape :
104 likestr = likestr.replace(char, '\\'+char)
105 # convert %s to wildcards
106 self.likestr = likestr.replace('%', '.*')
107 self.re = re.compile('^'+self.likestr+'$', re_flags)
108 def __call__(self, s):
109 return self.re.match(s)
110
111#
112# keys used to store database metadata
113#
114_table_names_key = '__TABLE_NAMES__' # list of the tables in this db
115_columns = '._COLUMNS__' # table_name+this key contains a list of columns
116
117def _columns_key(table):
118 return table + _columns
119
120#
121# these keys are found within table sub databases
122#
123_data = '._DATA_.' # this+column+this+rowid key contains table data
124_rowid = '._ROWID_.' # this+rowid+this key contains a unique entry for each
125 # row in the table. (no data is stored)
126_rowid_str_len = 8 # length in bytes of the unique rowid strings
127
128
129def _data_key(table, col, rowid):
130 return table + _data + col + _data + rowid
131
132def _search_col_data_key(table, col):
133 return table + _data + col + _data
134
135def _search_all_data_key(table):
136 return table + _data
137
138def _rowid_key(table, rowid):
139 return table + _rowid + rowid + _rowid
140
141def _search_rowid_key(table):
142 return table + _rowid
143
144def contains_metastrings(s) :
145 """Verify that the given string does not contain any
146 metadata strings that might interfere with dbtables database operation.
147 """
148 if (s.find(_table_names_key) >= 0 or
149 s.find(_columns) >= 0 or
150 s.find(_data) >= 0 or
151 s.find(_rowid) >= 0):
152 # Then
153 return 1
154 else:
155 return 0
156
157
158class bsdTableDB :
159 def __init__(self, filename, dbhome, create=0, truncate=0, mode=0600,
160 recover=0, dbflags=0):
161 """bsdTableDB(filename, dbhome, create=0, truncate=0, mode=0600)
162
163 Open database name in the dbhome Berkeley DB directory.
164 Use keyword arguments when calling this constructor.
165 """
166 self.db = None
167 myflags = db.DB_THREAD
168 if create:
169 myflags |= db.DB_CREATE
170 flagsforenv = (db.DB_INIT_MPOOL | db.DB_INIT_LOCK | db.DB_INIT_LOG |
171 db.DB_INIT_TXN | dbflags)
172 # DB_AUTO_COMMIT isn't a valid flag for env.open()
173 try:
174 dbflags |= db.DB_AUTO_COMMIT
175 except AttributeError:
176 pass
177 if recover:
178 flagsforenv = flagsforenv | db.DB_RECOVER
179 self.env = db.DBEnv()
180 # enable auto deadlock avoidance
181 self.env.set_lk_detect(db.DB_LOCK_DEFAULT)
182 self.env.open(dbhome, myflags | flagsforenv)
183 if truncate:
184 myflags |= db.DB_TRUNCATE
185 self.db = db.DB(self.env)
186 # this code relies on DBCursor.set* methods to raise exceptions
187 # rather than returning None
188 self.db.set_get_returns_none(1)
189 # allow duplicate entries [warning: be careful w/ metadata]
190 self.db.set_flags(db.DB_DUP)
191 self.db.open(filename, db.DB_BTREE, dbflags | myflags, mode)
192 self.dbfilename = filename
193
194 if sys.version_info[0] >= 3 :
195 class cursor_py3k(object) :
196 def __init__(self, dbcursor) :
197 self._dbcursor = dbcursor
198
199 def close(self) :
200 return self._dbcursor.close()
201
202 def set_range(self, search) :
203 v = self._dbcursor.set_range(bytes(search, "iso8859-1"))
204 if v is not None :
205 v = (v[0].decode("iso8859-1"),
206 v[1].decode("iso8859-1"))
207 return v
208
209 def __next__(self) :
210 v = getattr(self._dbcursor, "next")()
211 if v is not None :
212 v = (v[0].decode("iso8859-1"),
213 v[1].decode("iso8859-1"))
214 return v
215
216 class db_py3k(object) :
217 def __init__(self, db) :
218 self._db = db
219
220 def cursor(self, txn=None) :
221 return cursor_py3k(self._db.cursor(txn=txn))
222
223 def has_key(self, key, txn=None) :
224 return getattr(self._db,"has_key")(bytes(key, "iso8859-1"),
225 txn=txn)
226
227 def put(self, key, value, flags=0, txn=None) :
228 key = bytes(key, "iso8859-1")
229 if value is not None :
230 value = bytes(value, "iso8859-1")
231 return self._db.put(key, value, flags=flags, txn=txn)
232
233 def put_bytes(self, key, value, txn=None) :
234 key = bytes(key, "iso8859-1")
235 return self._db.put(key, value, txn=txn)
236
237 def get(self, key, txn=None, flags=0) :
238 key = bytes(key, "iso8859-1")
239 v = self._db.get(key, txn=txn, flags=flags)
240 if v is not None :
241 v = v.decode("iso8859-1")
242 return v
243
244 def get_bytes(self, key, txn=None, flags=0) :
245 key = bytes(key, "iso8859-1")
246 return self._db.get(key, txn=txn, flags=flags)
247
248 def delete(self, key, txn=None) :
249 key = bytes(key, "iso8859-1")
250 return self._db.delete(key, txn=txn)
251
252 def close (self) :
253 return self._db.close()
254
255 self.db = db_py3k(self.db)
256 else : # Python 2.x
257 pass
258
259 # Initialize the table names list if this is a new database
260 txn = self.env.txn_begin()
261 try:
262 if not getattr(self.db, "has_key")(_table_names_key, txn):
263 getattr(self.db, "put_bytes", self.db.put) \
264 (_table_names_key, pickle.dumps([], 1), txn=txn)
265 # Yes, bare except
266 except:
267 txn.abort()
268 raise
269 else:
270 txn.commit()
271 # TODO verify more of the database's metadata?
272 self.__tablecolumns = {}
273
274 def __del__(self):
275 self.close()
276
277 def close(self):
278 if self.db is not None:
279 self.db.close()
280 self.db = None
281 if self.env is not None:
282 self.env.close()
283 self.env = None
284
285 def checkpoint(self, mins=0):
286 self.env.txn_checkpoint(mins)
287
288 def sync(self):
289 self.db.sync()
290
291 def _db_print(self) :
292 """Print the database to stdout for debugging"""
293 print "******** Printing raw database for debugging ********"
294 cur = self.db.cursor()
295 try:
296 key, data = cur.first()
297 while 1:
298 print repr({key: data})
299 next = cur.next()
300 if next:
301 key, data = next
302 else:
303 cur.close()
304 return
305 except db.DBNotFoundError:
306 cur.close()
307
308
309 def CreateTable(self, table, columns):
310 """CreateTable(table, columns) - Create a new table in the database.
311
312 raises TableDBError if it already exists or for other DB errors.
313 """
314 assert isinstance(columns, list)
315
316 txn = None
317 try:
318 # checking sanity of the table and column names here on
319 # table creation will prevent problems elsewhere.
320 if contains_metastrings(table):
321 raise ValueError(
322 "bad table name: contains reserved metastrings")
323 for column in columns :
324 if contains_metastrings(column):
325 raise ValueError(
326 "bad column name: contains reserved metastrings")
327
328 columnlist_key = _columns_key(table)
329 if getattr(self.db, "has_key")(columnlist_key):
330 raise TableAlreadyExists, "table already exists"
331
332 txn = self.env.txn_begin()
333 # store the table's column info
334 getattr(self.db, "put_bytes", self.db.put)(columnlist_key,
335 pickle.dumps(columns, 1), txn=txn)
336
337 # add the table name to the tablelist
338 tablelist = pickle.loads(getattr(self.db, "get_bytes",
339 self.db.get) (_table_names_key, txn=txn, flags=db.DB_RMW))
340 tablelist.append(table)
341 # delete 1st, in case we opened with DB_DUP
342 self.db.delete(_table_names_key, txn=txn)
343 getattr(self.db, "put_bytes", self.db.put)(_table_names_key,
344 pickle.dumps(tablelist, 1), txn=txn)
345
346 txn.commit()
347 txn = None
348 except db.DBError, dberror:
349 if txn:
350 txn.abort()
351 if sys.version_info < (2, 6) :
352 raise TableDBError, dberror[1]
353 else :
354 raise TableDBError, dberror.args[1]
355
356
357 def ListTableColumns(self, table):
358 """Return a list of columns in the given table.
359 [] if the table doesn't exist.
360 """
361 assert isinstance(table, str)
362 if contains_metastrings(table):
363 raise ValueError, "bad table name: contains reserved metastrings"
364
365 columnlist_key = _columns_key(table)
366 if not getattr(self.db, "has_key")(columnlist_key):
367 return []
368 pickledcolumnlist = getattr(self.db, "get_bytes",
369 self.db.get)(columnlist_key)
370 if pickledcolumnlist:
371 return pickle.loads(pickledcolumnlist)
372 else:
373 return []
374
375 def ListTables(self):
376 """Return a list of tables in this database."""
377 pickledtablelist = self.db.get_get(_table_names_key)
378 if pickledtablelist:
379 return pickle.loads(pickledtablelist)
380 else:
381 return []
382
383 def CreateOrExtendTable(self, table, columns):
384 """CreateOrExtendTable(table, columns)
385
386 Create a new table in the database.
387
388 If a table of this name already exists, extend it to have any
389 additional columns present in the given list as well as
390 all of its current columns.
391 """
392 assert isinstance(columns, list)
393
394 try:
395 self.CreateTable(table, columns)
396 except TableAlreadyExists:
397 # the table already existed, add any new columns
398 txn = None
399 try:
400 columnlist_key = _columns_key(table)
401 txn = self.env.txn_begin()
402
403 # load the current column list
404 oldcolumnlist = pickle.loads(
405 getattr(self.db, "get_bytes",
406 self.db.get)(columnlist_key, txn=txn, flags=db.DB_RMW))
407 # create a hash table for fast lookups of column names in the
408 # loop below
409 oldcolumnhash = {}
410 for c in oldcolumnlist:
411 oldcolumnhash[c] = c
412
413 # create a new column list containing both the old and new
414 # column names
415 newcolumnlist = copy.copy(oldcolumnlist)
416 for c in columns:
417 if not c in oldcolumnhash:
418 newcolumnlist.append(c)
419
420 # store the table's new extended column list
421 if newcolumnlist != oldcolumnlist :
422 # delete the old one first since we opened with DB_DUP
423 self.db.delete(columnlist_key, txn=txn)
424 getattr(self.db, "put_bytes", self.db.put)(columnlist_key,
425 pickle.dumps(newcolumnlist, 1),
426 txn=txn)
427
428 txn.commit()
429 txn = None
430
431 self.__load_column_info(table)
432 except db.DBError, dberror:
433 if txn:
434 txn.abort()
435 if sys.version_info < (2, 6) :
436 raise TableDBError, dberror[1]
437 else :
438 raise TableDBError, dberror.args[1]
439
440
441 def __load_column_info(self, table) :
442 """initialize the self.__tablecolumns dict"""
443 # check the column names
444 try:
445 tcolpickles = getattr(self.db, "get_bytes",
446 self.db.get)(_columns_key(table))
447 except db.DBNotFoundError:
448 raise TableDBError, "unknown table: %r" % (table,)
449 if not tcolpickles:
450 raise TableDBError, "unknown table: %r" % (table,)
451 self.__tablecolumns[table] = pickle.loads(tcolpickles)
452
453 def __new_rowid(self, table, txn) :
454 """Create a new unique row identifier"""
455 unique = 0
456 while not unique:
457 # Generate a random 64-bit row ID string
458 # (note: might have <64 bits of true randomness
459 # but it's plenty for our database id needs!)
460 blist = []
461 for x in xrange(_rowid_str_len):
462 blist.append(random.randint(0,255))
463 newid = struct.pack('B'*_rowid_str_len, *blist)
464
465 if sys.version_info[0] >= 3 :
466 newid = newid.decode("iso8859-1") # 8 bits
467
468 # Guarantee uniqueness by adding this key to the database
469 try:
470 self.db.put(_rowid_key(table, newid), None, txn=txn,
471 flags=db.DB_NOOVERWRITE)
472 except db.DBKeyExistError:
473 pass
474 else:
475 unique = 1
476
477 return newid
478
479
480 def Insert(self, table, rowdict) :
481 """Insert(table, datadict) - Insert a new row into the table
482 using the keys+values from rowdict as the column values.
483 """
484
485 txn = None
486 try:
487 if not getattr(self.db, "has_key")(_columns_key(table)):
488 raise TableDBError, "unknown table"
489
490 # check the validity of each column name
491 if not table in self.__tablecolumns:
492 self.__load_column_info(table)
493 for column in rowdict.keys() :
494 if not self.__tablecolumns[table].count(column):
495 raise TableDBError, "unknown column: %r" % (column,)
496
497 # get a unique row identifier for this row
498 txn = self.env.txn_begin()
499 rowid = self.__new_rowid(table, txn=txn)
500
501 # insert the row values into the table database
502 for column, dataitem in rowdict.items():
503 # store the value
504 self.db.put(_data_key(table, column, rowid), dataitem, txn=txn)
505
506 txn.commit()
507 txn = None
508
509 except db.DBError, dberror:
510 # WIBNI we could just abort the txn and re-raise the exception?
511 # But no, because TableDBError is not related to DBError via
512 # inheritance, so it would be backwards incompatible. Do the next
513 # best thing.
514 info = sys.exc_info()
515 if txn:
516 txn.abort()
517 self.db.delete(_rowid_key(table, rowid))
518 if sys.version_info < (2, 6) :
519 raise TableDBError, dberror[1], info[2]
520 else :
521 raise TableDBError, dberror.args[1], info[2]
522
523
524 def Modify(self, table, conditions={}, mappings={}):
525 """Modify(table, conditions={}, mappings={}) - Modify items in rows matching 'conditions' using mapping functions in 'mappings'
526
527 * table - the table name
528 * conditions - a dictionary keyed on column names containing
529 a condition callable expecting the data string as an
530 argument and returning a boolean.
531 * mappings - a dictionary keyed on column names containing a
532 condition callable expecting the data string as an argument and
533 returning the new string for that column.
534 """
535
536 try:
537 matching_rowids = self.__Select(table, [], conditions)
538
539 # modify only requested columns
540 columns = mappings.keys()
541 for rowid in matching_rowids.keys():
542 txn = None
543 try:
544 for column in columns:
545 txn = self.env.txn_begin()
546 # modify the requested column
547 try:
548 dataitem = self.db.get(
549 _data_key(table, column, rowid),
550 txn=txn)
551 self.db.delete(
552 _data_key(table, column, rowid),
553 txn=txn)
554 except db.DBNotFoundError:
555 # XXXXXXX row key somehow didn't exist, assume no
556 # error
557 dataitem = None
558 dataitem = mappings[column](dataitem)
559 if dataitem is not None:
560 self.db.put(
561 _data_key(table, column, rowid),
562 dataitem, txn=txn)
563 txn.commit()
564 txn = None
565
566 # catch all exceptions here since we call unknown callables
567 except:
568 if txn:
569 txn.abort()
570 raise
571
572 except db.DBError, dberror:
573 if sys.version_info < (2, 6) :
574 raise TableDBError, dberror[1]
575 else :
576 raise TableDBError, dberror.args[1]
577
578 def Delete(self, table, conditions={}):
579 """Delete(table, conditions) - Delete items matching the given
580 conditions from the table.
581
582 * conditions - a dictionary keyed on column names containing
583 condition functions expecting the data string as an
584 argument and returning a boolean.
585 """
586
587 try:
588 matching_rowids = self.__Select(table, [], conditions)
589
590 # delete row data from all columns
591 columns = self.__tablecolumns[table]
592 for rowid in matching_rowids.keys():
593 txn = None
594 try:
595 txn = self.env.txn_begin()
596 for column in columns:
597 # delete the data key
598 try:
599 self.db.delete(_data_key(table, column, rowid),
600 txn=txn)
601 except db.DBNotFoundError:
602 # XXXXXXX column may not exist, assume no error
603 pass
604
605 try:
606 self.db.delete(_rowid_key(table, rowid), txn=txn)
607 except db.DBNotFoundError:
608 # XXXXXXX row key somehow didn't exist, assume no error
609 pass
610 txn.commit()
611 txn = None
612 except db.DBError, dberror:
613 if txn:
614 txn.abort()
615 raise
616 except db.DBError, dberror:
617 if sys.version_info < (2, 6) :
618 raise TableDBError, dberror[1]
619 else :
620 raise TableDBError, dberror.args[1]
621
622
623 def Select(self, table, columns, conditions={}):
624 """Select(table, columns, conditions) - retrieve specific row data
625 Returns a list of row column->value mapping dictionaries.
626
627 * columns - a list of which column data to return. If
628 columns is None, all columns will be returned.
629 * conditions - a dictionary keyed on column names
630 containing callable conditions expecting the data string as an
631 argument and returning a boolean.
632 """
633 try:
634 if not table in self.__tablecolumns:
635 self.__load_column_info(table)
636 if columns is None:
637 columns = self.__tablecolumns[table]
638 matching_rowids = self.__Select(table, columns, conditions)
639 except db.DBError, dberror:
640 if sys.version_info < (2, 6) :
641 raise TableDBError, dberror[1]
642 else :
643 raise TableDBError, dberror.args[1]
644 # return the matches as a list of dictionaries
645 return matching_rowids.values()
646
647
648 def __Select(self, table, columns, conditions):
649 """__Select() - Used to implement Select and Delete (above)
650 Returns a dictionary keyed on rowids containing dicts
651 holding the row data for columns listed in the columns param
652 that match the given conditions.
653 * conditions is a dictionary keyed on column names
654 containing callable conditions expecting the data string as an
655 argument and returning a boolean.
656 """
657 # check the validity of each column name
658 if not table in self.__tablecolumns:
659 self.__load_column_info(table)
660 if columns is None:
661 columns = self.tablecolumns[table]
662 for column in (columns + conditions.keys()):
663 if not self.__tablecolumns[table].count(column):
664 raise TableDBError, "unknown column: %r" % (column,)
665
666 # keyed on rows that match so far, containings dicts keyed on
667 # column names containing the data for that row and column.
668 matching_rowids = {}
669 # keys are rowids that do not match
670 rejected_rowids = {}
671
672 # attempt to sort the conditions in such a way as to minimize full
673 # column lookups
674 def cmp_conditions(atuple, btuple):
675 a = atuple[1]
676 b = btuple[1]
677 if type(a) is type(b):
678
679 # Needed for python 3. "cmp" vanished in 3.0.1
680 def cmp(a, b) :
681 if a==b : return 0
682 if a<b : return -1
683 return 1
684
685 if isinstance(a, PrefixCond) and isinstance(b, PrefixCond):
686 # longest prefix first
687 return cmp(len(b.prefix), len(a.prefix))
688 if isinstance(a, LikeCond) and isinstance(b, LikeCond):
689 # longest likestr first
690 return cmp(len(b.likestr), len(a.likestr))
691 return 0
692 if isinstance(a, ExactCond):
693 return -1
694 if isinstance(b, ExactCond):
695 return 1
696 if isinstance(a, PrefixCond):
697 return -1
698 if isinstance(b, PrefixCond):
699 return 1
700 # leave all unknown condition callables alone as equals
701 return 0
702
703 if sys.version_info < (2, 6) :
704 conditionlist = conditions.items()
705 conditionlist.sort(cmp_conditions)
706 else : # Insertion Sort. Please, improve
707 conditionlist = []
708 for i in conditions.items() :
709 for j, k in enumerate(conditionlist) :
710 r = cmp_conditions(k, i)
711 if r == 1 :
712 conditionlist.insert(j, i)
713 break
714 else :
715 conditionlist.append(i)
716
717 # Apply conditions to column data to find what we want
718 cur = self.db.cursor()
719 column_num = -1
720 for column, condition in conditionlist:
721 column_num = column_num + 1
722 searchkey = _search_col_data_key(table, column)
723 # speedup: don't linear search columns within loop
724 if column in columns:
725 savethiscolumndata = 1 # save the data for return
726 else:
727 savethiscolumndata = 0 # data only used for selection
728
729 try:
730 key, data = cur.set_range(searchkey)
731 while key[:len(searchkey)] == searchkey:
732 # extract the rowid from the key
733 rowid = key[-_rowid_str_len:]
734
735 if not rowid in rejected_rowids:
736 # if no condition was specified or the condition
737 # succeeds, add row to our match list.
738 if not condition or condition(data):
739 if not rowid in matching_rowids:
740 matching_rowids[rowid] = {}
741 if savethiscolumndata:
742 matching_rowids[rowid][column] = data
743 else:
744 if rowid in matching_rowids:
745 del matching_rowids[rowid]
746 rejected_rowids[rowid] = rowid
747
748 key, data = cur.next()
749
750 except db.DBError, dberror:
751 if dberror.args[0] != db.DB_NOTFOUND:
752 raise
753 continue
754
755 cur.close()
756
757 # we're done selecting rows, garbage collect the reject list
758 del rejected_rowids
759
760 # extract any remaining desired column data from the
761 # database for the matching rows.
762 if len(columns) > 0:
763 for rowid, rowdata in matching_rowids.items():
764 for column in columns:
765 if column in rowdata:
766 continue
767 try:
768 rowdata[column] = self.db.get(
769 _data_key(table, column, rowid))
770 except db.DBError, dberror:
771 if sys.version_info < (2, 6) :
772 if dberror[0] != db.DB_NOTFOUND:
773 raise
774 else :
775 if dberror.args[0] != db.DB_NOTFOUND:
776 raise
777 rowdata[column] = None
778
779 # return the matches
780 return matching_rowids
781
782
783 def Drop(self, table):
784 """Remove an entire table from the database"""
785 txn = None
786 try:
787 txn = self.env.txn_begin()
788
789 # delete the column list
790 self.db.delete(_columns_key(table), txn=txn)
791
792 cur = self.db.cursor(txn)
793
794 # delete all keys containing this tables column and row info
795 table_key = _search_all_data_key(table)
796 while 1:
797 try:
798 key, data = cur.set_range(table_key)
799 except db.DBNotFoundError:
800 break
801 # only delete items in this table
802 if key[:len(table_key)] != table_key:
803 break
804 cur.delete()
805
806 # delete all rowids used by this table
807 table_key = _search_rowid_key(table)
808 while 1:
809 try:
810 key, data = cur.set_range(table_key)
811 except db.DBNotFoundError:
812 break
813 # only delete items in this table
814 if key[:len(table_key)] != table_key:
815 break
816 cur.delete()
817
818 cur.close()
819
820 # delete the tablename from the table name list
821 tablelist = pickle.loads(
822 getattr(self.db, "get_bytes", self.db.get)(_table_names_key,
823 txn=txn, flags=db.DB_RMW))
824 try:
825 tablelist.remove(table)
826 except ValueError:
827 # hmm, it wasn't there, oh well, that's what we want.
828 pass
829 # delete 1st, incase we opened with DB_DUP
830 self.db.delete(_table_names_key, txn=txn)
831 getattr(self.db, "put_bytes", self.db.put)(_table_names_key,
832 pickle.dumps(tablelist, 1), txn=txn)
833
834 txn.commit()
835 txn = None
836
837 if table in self.__tablecolumns:
838 del self.__tablecolumns[table]
839
840 except db.DBError, dberror:
841 if txn:
842 txn.abort()
843 raise TableDBError(dberror.args[1])
Note: See TracBrowser for help on using the repository browser.