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_bsddb3.py,v 1.21 2003-11-14 00:11:18 richard Exp $
19 '''
20 This module defines a backend that saves the hyperdatabase in BSDDB3.
21 '''
23 import bsddb3, 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 bsddb3.btopen(db, 'n')
41 db = os.path.join(self.dir, 'journals.%s'%cn)
42 bsddb3.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 bsddb3.btopen(path, mode)
51 else:
52 return bsddb3.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 bsddb3.open(%r, 'c')"%path
65 return bsddb3.btopen(path, 'c')
67 # open the database with the correct module
68 if __debug__:
69 print >>hyperdb.DEBUG, "opendb bsddb3.open(%r, %r)"%(path, mode)
70 return bsddb3.btopen(path, mode)
72 #
73 # Journal
74 #
75 def getjournal(self, classname, nodeid):
76 ''' get the journal for id
78 Raise IndexError if the node doesn't exist (as per history()'s
79 API)
80 '''
81 if __debug__:
82 print >>hyperdb.DEBUG, 'getjournal', (self, classname, nodeid)
84 # our journal result
85 res = []
87 # add any journal entries for transactions not committed to the
88 # database
89 for method, args in self.transactions:
90 if method != self.doSaveJournal:
91 continue
92 (cache_classname, cache_nodeid, cache_action, cache_params,
93 cache_creator, cache_creation) = args
94 if cache_classname == classname and cache_nodeid == nodeid:
95 if not cache_creator:
96 cache_creator = self.getuid()
97 if not cache_creation:
98 cache_creation = date.Date()
99 res.append((cache_nodeid, cache_creation, cache_creator,
100 cache_action, cache_params))
102 # attempt to open the journal - in some rare cases, the journal may
103 # not exist
104 try:
105 db = bsddb3.btopen(os.path.join(self.dir, 'journals.%s'%classname),
106 'r')
107 except bsddb3._db.DBNoSuchFileError:
108 if res:
109 # we have unsaved journal entries, return them
110 return res
111 raise IndexError, 'no such %s %s'%(classname, nodeid)
112 # more handling of bad journals
113 if not db.has_key(nodeid):
114 db.close()
115 if res:
116 # we have some unsaved journal entries, be happy!
117 return res
118 raise IndexError, 'no such %s %s'%(classname, nodeid)
119 journal = marshal.loads(db[nodeid])
120 db.close()
122 # add all the saved journal entries for this node
123 for nodeid, date_stamp, user, action, params in journal:
124 res.append((nodeid, date.Date(date_stamp), user, action, params))
125 return res
127 def getCachedJournalDB(self, classname):
128 ''' get the journal db, looking in our cache of databases for commit
129 '''
130 # get the database handle
131 db_name = 'journals.%s'%classname
132 if self.databases.has_key(db_name):
133 return self.databases[db_name]
134 else:
135 db = bsddb3.btopen(os.path.join(self.dir, db_name), 'c')
136 self.databases[db_name] = db
137 return db