1 #
2 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
3 # This module is free software, and you may redistribute it and/or modify
4 # under the same terms as Python, so long as this copyright message and
5 # disclaimer are retained in their original form.
6 #
7 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
8 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
9 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
10 # POSSIBILITY OF SUCH DAMAGE.
11 #
12 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17 #
18 #$Id: back_bsddb.py,v 1.25 2003-03-26 11:19:28 richard Exp $
19 '''
20 This module defines a backend that saves the hyperdatabase in BSDDB.
21 '''
23 import bsddb, os, marshal
24 from roundup import hyperdb, date
26 # these classes are so similar, we just use the anydbm methods
27 from back_anydbm import Database, Class, FileClass, IssueClass
29 #
30 # Now the database
31 #
32 class Database(Database):
33 """A database for storing records containing flexible data types."""
34 #
35 # Class DBs
36 #
37 def clear(self):
38 for cn in self.classes.keys():
39 db = os.path.join(self.dir, 'nodes.%s'%cn)
40 bsddb.btopen(db, 'n')
41 db = os.path.join(self.dir, 'journals.%s'%cn)
42 bsddb.btopen(db, 'n')
44 def getclassdb(self, classname, mode='r'):
45 ''' grab a connection to the class db that will be used for
46 multiple actions
47 '''
48 path = os.path.join(os.getcwd(), self.dir, 'nodes.%s'%classname)
49 if os.path.exists(path):
50 return bsddb.btopen(path, mode)
51 else:
52 return bsddb.btopen(path, 'c')
54 def opendb(self, name, mode):
55 '''Low-level database opener that gets around anydbm/dbm
56 eccentricities.
57 '''
58 if __debug__:
59 print >>hyperdb.DEBUG, self, 'opendb', (self, name, mode)
60 # determine which DB wrote the class file
61 path = os.path.join(os.getcwd(), self.dir, name)
62 if not os.path.exists(path):
63 if __debug__:
64 print >>hyperdb.DEBUG, "opendb bsddb.open(%r, 'c')"%path
65 return bsddb.btopen(path, 'c')
67 # open the database with the correct module
68 if __debug__:
69 print >>hyperdb.DEBUG, "opendb bsddb.open(%r, %r)"%(path, mode)
70 return bsddb.btopen(path, mode)
72 #
73 # Journal
74 #
75 def getjournal(self, classname, nodeid):
76 ''' get the journal for id
77 '''
78 if __debug__:
79 print >>hyperdb.DEBUG, 'getjournal', (self, classname, nodeid)
81 # our journal result
82 res = []
84 # add any journal entries for transactions not committed to the
85 # database
86 for method, args in self.transactions:
87 if method != self.doSaveJournal:
88 continue
89 (cache_classname, cache_nodeid, cache_action, cache_params,
90 cache_creator, cache_creation) = args
91 if cache_classname == classname and cache_nodeid == nodeid:
92 if not cache_creator:
93 cache_creator = self.curuserid
94 if not cache_creation:
95 cache_creation = date.Date()
96 res.append((cache_nodeid, cache_creation, cache_creator,
97 cache_action, cache_params))
99 # attempt to open the journal - in some rare cases, the journal may
100 # not exist
101 try:
102 db = bsddb.btopen(os.path.join(self.dir, 'journals.%s'%classname),
103 'r')
104 except bsddb.error, error:
105 if error.args[0] != 2: raise
106 raise IndexError, 'no such %s %s'%(classname, nodeid)
107 # more handling of bad journals
108 if not db.has_key(nodeid):
109 if res:
110 # we have some unsaved journal entries, be happy!
111 return res
112 raise IndexError, 'no such %s %s'%(classname, nodeid)
113 journal = marshal.loads(db[nodeid])
114 db.close()
116 # add all the saved journal entries for this node
117 for entry in journal:
118 (nodeid, date_stamp, user, action, params) = entry
119 date_obj = date.Date(date_stamp)
120 res.append((nodeid, date_obj, user, action, params))
121 return res
123 def getCachedJournalDB(self, classname):
124 ''' get the journal db, looking in our cache of databases for commit
125 '''
126 # get the database handle
127 db_name = 'journals.%s'%classname
128 if self.databases.has_key(db_name):
129 return self.databases[db_name]
130 else:
131 db = bsddb.btopen(os.path.join(self.dir, db_name), 'c')
132 self.databases[db_name] = db
133 return db