Skip to content

Commit a8e117d

Browse files
author
Richard Jones
committed
fixes to the rdbms backends
1 parent 837d4bd commit a8e117d

File tree

3 files changed

+170
-33
lines changed

3 files changed

+170
-33
lines changed

roundup/backends/back_sqlite.py

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_sqlite.py,v 1.1 2002-09-18 05:07:47 richard Exp $
1+
# $Id: back_sqlite.py,v 1.2 2002-09-18 07:04:37 richard Exp $
22
__doc__ = '''
33
See https://pysqlite.sourceforge.net/ for pysqlite info
44
'''
@@ -88,3 +88,120 @@ def load_journal(self, cursor, classname, cols, nodeid):
8888
res.append((nodeid, date.Date(date_stamp), user, action, params))
8989
return res
9090

91+
def unserialise(self, classname, node):
92+
''' Decode the marshalled node data
93+
94+
SQLite stringifies _everything_... so we need to re-numberificate
95+
Booleans and Numbers.
96+
'''
97+
if __debug__:
98+
print >>hyperdb.DEBUG, 'unserialise', classname, node
99+
properties = self.getclass(classname).getprops()
100+
d = {}
101+
for k, v in node.items():
102+
# if the property doesn't exist, or is the "retired" flag then
103+
# it won't be in the properties dict
104+
if not properties.has_key(k):
105+
d[k] = v
106+
continue
107+
108+
# get the property spec
109+
prop = properties[k]
110+
111+
if isinstance(prop, Date) and v is not None:
112+
d[k] = date.Date(v)
113+
elif isinstance(prop, Interval) and v is not None:
114+
d[k] = date.Interval(v)
115+
elif isinstance(prop, Password):
116+
p = password.Password()
117+
p.unpack(v)
118+
d[k] = p
119+
elif isinstance(prop, Boolean) and v is not None:
120+
d[k] = int(v)
121+
elif isinstance(prop, Number) and v is not None:
122+
# try int first, then assume it's a float
123+
try:
124+
d[k] = int(v)
125+
except ValueError:
126+
d[k] = float(v)
127+
else:
128+
d[k] = v
129+
return d
130+
131+
class Class(Class):
132+
_marker = []
133+
def get(self, nodeid, propname, default=_marker, cache=1):
134+
'''Get the value of a property on an existing node of this class.
135+
136+
'nodeid' must be the id of an existing node of this class or an
137+
IndexError is raised. 'propname' must be the name of a property
138+
of this class or a KeyError is raised.
139+
140+
'cache' indicates whether the transaction cache should be queried
141+
for the node. If the node has been modified and you need to
142+
determine what its values prior to modification are, you need to
143+
set cache=0.
144+
'''
145+
if propname == 'id':
146+
return nodeid
147+
148+
if propname == 'creation':
149+
if not self.do_journal:
150+
raise ValueError, 'Journalling is disabled for this class'
151+
journal = self.db.getjournal(self.classname, nodeid)
152+
if journal:
153+
return self.db.getjournal(self.classname, nodeid)[0][1]
154+
else:
155+
# on the strange chance that there's no journal
156+
return date.Date()
157+
if propname == 'activity':
158+
if not self.do_journal:
159+
raise ValueError, 'Journalling is disabled for this class'
160+
journal = self.db.getjournal(self.classname, nodeid)
161+
if journal:
162+
return self.db.getjournal(self.classname, nodeid)[-1][1]
163+
else:
164+
# on the strange chance that there's no journal
165+
return date.Date()
166+
if propname == 'creator':
167+
if not self.do_journal:
168+
raise ValueError, 'Journalling is disabled for this class'
169+
journal = self.db.getjournal(self.classname, nodeid)
170+
if journal:
171+
name = self.db.getjournal(self.classname, nodeid)[0][2]
172+
else:
173+
return None
174+
try:
175+
return self.db.user.lookup(name)
176+
except KeyError:
177+
# the journaltag user doesn't exist any more
178+
return None
179+
180+
# get the property (raises KeyErorr if invalid)
181+
prop = self.properties[propname]
182+
183+
# get the node's dict
184+
d = self.db.getnode(self.classname, nodeid) #, cache=cache)
185+
186+
if not d.has_key(propname):
187+
if default is self._marker:
188+
if isinstance(prop, Multilink):
189+
return []
190+
else:
191+
return None
192+
else:
193+
return default
194+
195+
# special handling for some types
196+
if isinstance(prop, Multilink):
197+
# don't pass our list to other code
198+
return d[propname][:]
199+
elif d[propname] is None:
200+
# always return None right now, no conversion
201+
return None
202+
elif isinstance(prop, Boolean) or isinstance(prop, Number):
203+
# turn Booleans and Numbers into integers
204+
return int(d[propname])
205+
206+
return d[propname]
207+

roundup/backends/rdbms_common.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: rdbms_common.py,v 1.1 2002-09-18 05:07:47 richard Exp $
1+
# $Id: rdbms_common.py,v 1.2 2002-09-18 07:04:38 richard Exp $
22

33
# standard python modules
44
import sys, os, time, re, errno, weakref, copy
@@ -70,23 +70,27 @@ def post_init(self):
7070
attribute actually matches the schema in the database.
7171
'''
7272
# now detect changes in the schema
73+
save = 0
7374
for classname, spec in self.classes.items():
7475
if self.database_schema.has_key(classname):
7576
dbspec = self.database_schema[classname]
76-
self.update_class(spec, dbspec)
77-
self.database_schema[classname] = spec.schema()
77+
if self.update_class(spec, dbspec):
78+
self.database_schema[classname] = spec.schema()
79+
save = 1
7880
else:
7981
self.create_class(spec)
8082
self.database_schema[classname] = spec.schema()
83+
save = 1
8184

8285
for classname in self.database_schema.keys():
8386
if not self.classes.has_key(classname):
8487
self.drop_class(classname)
8588

8689
# update the database version of the schema
87-
cursor = self.conn.cursor()
88-
self.sql(cursor, 'delete from schema')
89-
self.save_dbschema(cursor, self.database_schema)
90+
if save:
91+
cursor = self.conn.cursor()
92+
self.sql(cursor, 'delete from schema')
93+
self.save_dbschema(cursor, self.database_schema)
9094

9195
# reindex the db if necessary
9296
if self.indexer.should_reindex():
@@ -126,7 +130,8 @@ def update_class(self, spec, dbspec):
126130
'''
127131
spec_schema = spec.schema()
128132
if spec_schema == dbspec:
129-
return
133+
# no save needed for this one
134+
return 0
130135
if __debug__:
131136
print >>hyperdb.DEBUG, 'update_class FIRING'
132137

@@ -244,6 +249,7 @@ def update_class(self, spec, dbspec):
244249
qs = ','.join([self.arg for x in cols])
245250
sql = 'insert into _%s values (%s)'%(cn, s)
246251
cursor.execute(sql, olddata)
252+
return 1
247253

248254
def create_class_table(self, cursor, spec):
249255
''' create the class table for the given spec
@@ -1590,6 +1596,7 @@ def filter(self, search_matches, filterspec, sort, group):
15901596
frum = ['_'+cn]
15911597
where = []
15921598
args = []
1599+
a = self.db.arg
15931600
for k, v in filterspec.items():
15941601
propclass = props[k]
15951602
if isinstance(propclass, Multilink):
@@ -1605,41 +1612,54 @@ def filter(self, search_matches, filterspec, sort, group):
16051612
args.append(v)
16061613
else:
16071614
if isinstance(v, type([])):
1608-
s = ','.join([self.arg for x in v])
1615+
s = ','.join([a for x in v])
16091616
where.append('_%s in (%s)'%(k, s))
16101617
args = args + v
16111618
else:
1612-
where.append('_%s=%s'%(k, self.arg))
1619+
where.append('_%s=%s'%(k, a))
16131620
args.append(v)
16141621

16151622
# add results of full text search
16161623
if search_matches is not None:
16171624
v = search_matches.keys()
1618-
s = ','.join([self.arg for x in v])
1625+
s = ','.join([a for x in v])
16191626
where.append('id in (%s)'%s)
16201627
args = args + v
16211628

16221629
# figure the order by clause
16231630
orderby = []
16241631
ordercols = []
16251632
if sort[0] is not None and sort[1] is not None:
1626-
if sort[0] != '-':
1627-
orderby.append('_'+sort[1])
1628-
ordercols.append(sort[1])
1633+
direction, colname = sort
1634+
if direction != '-':
1635+
if colname == 'activity':
1636+
orderby.append('activity')
1637+
ordercols.append('max(%s__journal.date) as activity'%cn)
1638+
frum.append('%s__journal'%cn)
1639+
where.append('%s__journal.nodeid = _%s.id'%(cn, cn))
1640+
else:
1641+
orderby.append('_'+colname)
1642+
ordercols.append('_'+colname)
16291643
else:
1630-
orderby.append('_'+sort[1]+' desc')
1631-
ordercols.append(sort[1])
1644+
if colname == 'activity':
1645+
orderby.append('activity desc')
1646+
ordercols.append('max(%s__journal.date) as activity'%cn)
1647+
frum.append('%s__journal'%cn)
1648+
where.append('%s__journal.nodeid = _%s.id'%(cn, cn))
1649+
else:
1650+
orderby.append('_'+colname+' desc')
1651+
ordercols.append('_'+colname)
16321652

16331653
# figure the group by clause
16341654
groupby = []
16351655
groupcols = []
16361656
if group[0] is not None and group[1] is not None:
16371657
if group[0] != '-':
16381658
groupby.append('_'+group[1])
1639-
groupcols.append(group[1])
1659+
groupcols.append('_'+group[1])
16401660
else:
16411661
groupby.append('_'+group[1]+' desc')
1642-
groupcols.append(group[1])
1662+
groupcols.append('_'+group[1])
16431663

16441664
# construct the SQL
16451665
frum = ','.join(frum)
@@ -1650,7 +1670,7 @@ def filter(self, search_matches, filterspec, sort, group):
16501670
order = ' order by %s'%(','.join(orderby))
16511671
else:
16521672
order = ''
1653-
if groupby:
1673+
if 0: #groupby:
16541674
cols = cols + groupcols
16551675
group = ' group by %s'%(','.join(groupby))
16561676
else:
@@ -1660,10 +1680,13 @@ def filter(self, search_matches, filterspec, sort, group):
16601680
group)
16611681
args = tuple(args)
16621682
if __debug__:
1663-
print >>hyperdb.DEBUG, 'find', (self, sql, args)
1683+
print >>hyperdb.DEBUG, 'filter', (self, sql, args)
16641684
cursor = self.db.conn.cursor()
16651685
cursor.execute(sql, args)
16661686

1687+
# return the IDs
1688+
return [row[0] for row in cursor.fetchall()]
1689+
16671690
def count(self):
16681691
'''Get the number of nodes in this class.
16691692

test/test_db.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: test_db.py,v 1.48 2002-09-18 05:07:48 richard Exp $
18+
# $Id: test_db.py,v 1.49 2002-09-18 07:04:39 richard Exp $
1919

2020
import unittest, os, shutil, time
2121

@@ -143,21 +143,18 @@ def testIntervalChange(self):
143143

144144
def testBooleanChange(self):
145145
userid = self.db.user.create(username='foo', assignable=1)
146-
self.db.user.create(username='foo2', assignable=0)
147-
a = self.db.user.get(userid, 'assignable')
146+
self.assertEqual(1, self.db.user.get(userid, 'assignable'))
148147
self.db.user.set(userid, assignable=0)
149-
self.assertNotEqual(self.db.user.get(userid, 'assignable'), a)
150-
self.db.user.set(userid, assignable=0)
151-
self.db.user.set(userid, assignable=1)
152-
self.db.user.set('1', assignable=None)
148+
self.assertEqual(self.db.user.get(userid, 'assignable'), 0)
149+
self.db.user.set(userid, assignable=None)
153150
self.assertEqual(self.db.user.get('1', "assignable"), None)
154151

155152
def testNumberChange(self):
156-
self.db.user.create(username='foo', age='1')
157-
a = self.db.user.get('1', 'age')
158-
self.db.user.set('1', age='3')
159-
self.assertNotEqual(self.db.user.get('1', 'age'), a)
160-
self.db.user.set('1', age='1.0')
153+
self.db.user.create(username='foo', age=1)
154+
self.assertEqual(1, self.db.user.get('1', 'age'))
155+
self.db.user.set('1', age=3)
156+
self.assertNotEqual(self.db.user.get('1', 'age'), 1)
157+
self.db.user.set('1', age=1.0)
161158
self.db.user.set('1', age=None)
162159
self.assertEqual(self.db.user.get('1', "age"), None)
163160

@@ -681,7 +678,7 @@ def suite():
681678
unittest.makeSuite(anydbmDBTestCase, 'test'),
682679
unittest.makeSuite(anydbmReadOnlyDBTestCase, 'test')
683680
]
684-
#return unittest.TestSuite(l)
681+
# return unittest.TestSuite(l)
685682

686683
try:
687684
import bsddb

0 commit comments

Comments
 (0)