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.23 2002-09-10 00:11:50 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 # attempt to open the journal - in some rare cases, the journal may
79 # not exist
80 try:
81 db = bsddb.btopen(os.path.join(self.dir, 'journals.%s'%classname),
82 'r')
83 except bsddb.error, error:
84 if error.args[0] != 2: raise
85 raise IndexError, 'no such %s %s'%(classname, nodeid)
86 # more handling of bad journals
87 if not db.has_key(nodeid):
88 raise IndexError, 'no such %s %s'%(classname, nodeid)
89 journal = marshal.loads(db[nodeid])
90 res = []
91 for entry in journal:
92 (nodeid, date_stamp, user, action, params) = entry
93 date_obj = date.Date(date_stamp)
94 res.append((nodeid, date_obj, user, action, params))
95 db.close()
96 return res
98 def getCachedJournalDB(self, classname):
99 ''' get the journal db, looking in our cache of databases for commit
100 '''
101 # get the database handle
102 db_name = 'journals.%s'%classname
103 if self.databases.has_key(db_name):
104 return self.databases[db_name]
105 else:
106 db = bsddb.btopen(os.path.join(self.dir, db_name), 'c')
107 self.databases[db_name] = db
108 return db
110 def doSaveJournal(self, classname, nodeid, action, params):
111 # serialise first
112 if action in ('set', 'create'):
113 params = self.serialise(classname, params)
115 entry = (nodeid, date.Date().get_tuple(), self.journaltag, action,
116 params)
118 if __debug__:
119 print >>hyperdb.DEBUG, 'doSaveJournal', entry
121 db = self.getCachedJournalDB(classname)
123 if db.has_key(nodeid):
124 s = db[nodeid]
125 l = marshal.loads(s)
126 l.append(entry)
127 else:
128 l = [entry]
130 db[nodeid] = marshal.dumps(l)