Skip to content

Commit 9d15d95

Browse files
author
Richard Jones
committed
gadfly backend now complete
can handle schema changes in non-Multilinks (though only one at a time at present)
1 parent 7ec40c7 commit 9d15d95

File tree

5 files changed

+113
-53
lines changed

5 files changed

+113
-53
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ are given with the most recent entry first.
66
. handling of journal values in export/import
77
. password edit now has a confirmation field
88
. registration error punts back to register page
9+
. gadfly backend now handles changes to the schema - but only one property
10+
at a time
911

1012

1113
2002-09-13 0.5.0 beta2

TODO.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,7 @@ pending web allow multilink selections to select a "none" element to allow
5151

5252
bug web request.url is incorrect in cgi-bin environments
5353
bug web do something about file.newitem
54+
bug mailgw some f*ked mailers QUOTE their Re; "Re: "[issue1] bla blah""
55+
bug docs need to mention somewhere how sorting works
5456
======= ========= =============================================================
5557

doc/customizing.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Customising Roundup
33
===================
44

5-
:Version: $Revision: 1.41 $
5+
:Version: $Revision: 1.42 $
66

77
.. This document borrows from the ZopeBook section on ZPT. The original is at:
88
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -1887,7 +1887,7 @@ Setting up a "wizard" (or "druid") for controlled adding of issues
18871887
<input type="hidden" name=":action" value="page1submit">
18881888

18891889
<strong>Category:</strong>
1890-
<span tal:replace="structure context/category/menu" />
1890+
<tal:block tal:replace="structure context/category/menu" />
18911891
<input type="submit" value="Continue">
18921892
</form>
18931893

roundup/backends/back_gadfly.py

Lines changed: 106 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_gadfly.py,v 1.20 2002-09-15 23:06:20 richard Exp $
1+
# $Id: back_gadfly.py,v 1.21 2002-09-16 08:04:46 richard Exp $
22
__doc__ = '''
33
About Gadfly
44
============
@@ -148,15 +148,20 @@ def reindex(self):
148148
klass.index(nodeid)
149149
self.indexer.save_index()
150150

151-
def determine_columns(self, spec):
151+
def determine_columns(self, properties):
152152
''' Figure the column names and multilink properties from the spec
153+
154+
"properties" is a list of (name, prop) where prop may be an
155+
instance of a hyperdb "type" _or_ a string repr of that type.
153156
'''
154157
cols = []
155158
mls = []
156159
# add the multilinks separately
157-
for col, prop in spec.properties.items():
160+
for col, prop in properties:
158161
if isinstance(prop, Multilink):
159162
mls.append(col)
163+
elif isinstance(prop, type('')) and prop.find('Multilink') != -1:
164+
mls.append(col)
160165
else:
161166
cols.append('_'+col)
162167
cols.sort()
@@ -165,9 +170,6 @@ def determine_columns(self, spec):
165170
def update_class(self, spec, dbspec):
166171
''' Determine the differences between the current spec and the
167172
database version of the spec, and update where necessary
168-
169-
NOTE that this doesn't work for adding/deleting properties!
170-
... until gadfly grows an ALTER TABLE command, it's not going to!
171173
'''
172174
spec_schema = spec.schema()
173175
if spec_schema == dbspec:
@@ -197,29 +199,54 @@ def update_class(self, spec, dbspec):
197199
# now compare
198200
for propname in spec_propnames:
199201
prop = spec_props[propname]
200-
if __debug__:
201-
print >>hyperdb.DEBUG, 'update_class ...', `prop`
202202
if dbspec_props.has_key(propname) and prop==dbspec_props[propname]:
203203
continue
204204
if __debug__:
205-
print >>hyperdb.DEBUG, 'update_class', `prop`
205+
print >>hyperdb.DEBUG, 'update_class ADD', (propname, prop)
206206

207207
if not dbspec_props.has_key(propname):
208208
# add the property
209209
if isinstance(prop, Multilink):
210-
sql = 'create table %s_%s (linkid varchar, nodeid '\
211-
'varchar)'%(spec.classname, prop)
212-
if __debug__:
213-
print >>hyperdb.DEBUG, 'update_class', (self, sql)
214-
cursor.execute(sql)
215-
else:
216-
# XXX gadfly doesn't have an ALTER TABLE command
217-
raise NotImplementedError
218-
sql = 'alter table _%s add column (_%s varchar)'%(
219-
spec.classname, propname)
220-
if __debug__:
221-
print >>hyperdb.DEBUG, 'update_class', (self, sql)
222-
cursor.execute(sql)
210+
# all we have to do here is create a new table, easy!
211+
self.create_multilink_table(cursor, spec, propname)
212+
continue
213+
214+
# no ALTER TABLE, so we:
215+
# 1. pull out the data, including an extra None column
216+
oldcols, x = self.determine_columns(dbspec[1])
217+
oldcols.append('id')
218+
oldcols.append('__retired__')
219+
cn = spec.classname
220+
sql = 'select %s,? from _%s'%(','.join(oldcols), cn)
221+
if __debug__:
222+
print >>hyperdb.DEBUG, 'update_class', (self, sql, None)
223+
cursor.execute(sql, (None,))
224+
olddata = cursor.fetchall()
225+
226+
# 2. drop the old table
227+
cursor.execute('drop table _%s'%cn)
228+
229+
# 3. create the new table
230+
cols, mls = self.create_class_table(cursor, spec)
231+
# ensure the new column is last
232+
cols.remove('_'+propname)
233+
assert oldcols == cols, "Column lists don't match!"
234+
cols.append('_'+propname)
235+
236+
# 4. populate with the data from step one
237+
s = ','.join(['?' for x in cols])
238+
scols = ','.join(cols)
239+
sql = 'insert into _%s (%s) values (%s)'%(cn, scols, s)
240+
241+
# GAH, nothing had better go wrong from here on in... but
242+
# we have to commit the drop...
243+
self.conn.commit()
244+
245+
# we're safe to insert now
246+
if __debug__:
247+
print >>hyperdb.DEBUG, 'update_class', (self, sql, olddata)
248+
cursor.execute(sql, olddata)
249+
223250
else:
224251
# modify the property
225252
if __debug__:
@@ -232,7 +259,7 @@ def update_class(self, spec, dbspec):
232259
if spec_props.has_key(propname):
233260
continue
234261
if __debug__:
235-
print >>hyperdb.DEBUG, 'update_class', `prop`
262+
print >>hyperdb.DEBUG, 'update_class REMOVE', `prop`
236263

237264
# delete the property
238265
if isinstance(prop, Multilink):
@@ -241,32 +268,52 @@ def update_class(self, spec, dbspec):
241268
print >>hyperdb.DEBUG, 'update_class', (self, sql)
242269
cursor.execute(sql)
243270
else:
244-
# XXX gadfly doesn't have an ALTER TABLE command
245-
raise NotImplementedError
246-
sql = 'alter table _%s delete column _%s'%(spec.classname,
247-
propname)
248-
if __debug__:
249-
print >>hyperdb.DEBUG, 'update_class', (self, sql)
250-
cursor.execute(sql)
251-
252-
def create_class(self, spec):
253-
''' Create a database table according to the given spec.
254-
'''
255-
cols, mls = self.determine_columns(spec)
271+
# no ALTER TABLE, so we:
272+
# 1. pull out the data, excluding the removed column
273+
oldcols, x = self.determine_columns(spec.properties.items())
274+
oldcols.append('id')
275+
oldcols.append('__retired__')
276+
# remove the missing column
277+
oldcols.remove('_'+propname)
278+
cn = spec.classname
279+
sql = 'select %s from _%s'%(','.join(oldcols), cn)
280+
cursor.execute(sql, (None,))
281+
olddata = sql.fetchall()
282+
283+
# 2. drop the old table
284+
cursor.execute('drop table _%s'%cn)
285+
286+
# 3. create the new table
287+
cols, mls = self.create_class_table(self, cursor, spec)
288+
assert oldcols != cols, "Column lists don't match!"
289+
290+
# 4. populate with the data from step one
291+
qs = ','.join(['?' for x in cols])
292+
sql = 'insert into _%s values (%s)'%(cn, s)
293+
cursor.execute(sql, olddata)
294+
295+
def create_class_table(self, cursor, spec):
296+
''' create the class table for the given spec
297+
'''
298+
cols, mls = self.determine_columns(spec.properties.items())
256299

257300
# add on our special columns
258301
cols.append('id')
259302
cols.append('__retired__')
260303

261-
cursor = self.conn.cursor()
262-
263304
# create the base table
264-
cols = ','.join(['%s varchar'%x for x in cols])
265-
sql = 'create table _%s (%s)'%(spec.classname, cols)
305+
scols = ','.join(['%s varchar'%x for x in cols])
306+
sql = 'create table _%s (%s)'%(spec.classname, scols)
266307
if __debug__:
267308
print >>hyperdb.DEBUG, 'create_class', (self, sql)
268309
cursor.execute(sql)
269310

311+
return cols, mls
312+
313+
def create_journal_table(self, cursor, spec):
314+
''' create the journal table for a class given the spec and
315+
already-determined cols
316+
'''
270317
# journal table
271318
cols = ','.join(['%s varchar'%x
272319
for x in 'nodeid date tag action params'.split()])
@@ -275,13 +322,26 @@ def create_class(self, spec):
275322
print >>hyperdb.DEBUG, 'create_class', (self, sql)
276323
cursor.execute(sql)
277324

325+
def create_multilink_table(self, cursor, spec, ml):
326+
''' Create a multilink table for the "ml" property of the class
327+
given by the spec
328+
'''
329+
sql = 'create table %s_%s (linkid varchar, nodeid varchar)'%(
330+
spec.classname, ml)
331+
if __debug__:
332+
print >>hyperdb.DEBUG, 'create_class', (self, sql)
333+
cursor.execute(sql)
334+
335+
def create_class(self, spec):
336+
''' Create a database table according to the given spec.
337+
'''
338+
cursor = self.conn.cursor()
339+
cols, mls = self.create_class_table(cursor, spec)
340+
self.create_journal_table(cursor, spec)
341+
278342
# now create the multilink tables
279343
for ml in mls:
280-
sql = 'create table %s_%s (linkid varchar, nodeid varchar)'%(
281-
spec.classname, ml)
282-
if __debug__:
283-
print >>hyperdb.DEBUG, 'create_class', (self, sql)
284-
cursor.execute(sql)
344+
self.create_multilink_table(cursor, spec, ml)
285345

286346
# ID counter
287347
sql = 'insert into ids (name, num) values (?,?)'
@@ -421,7 +481,7 @@ def addnode(self, classname, nodeid, node):
421481
print >>hyperdb.DEBUG, 'addnode', (self, classname, nodeid, node)
422482
# gadfly requires values for all non-multilink columns
423483
cl = self.classes[classname]
424-
cols, mls = self.determine_columns(cl)
484+
cols, mls = self.determine_columns(cl.properties.items())
425485

426486
# default the non-multilink columns
427487
for col, prop in cl.properties.items():
@@ -513,7 +573,7 @@ def getnode(self, classname, nodeid):
513573
print >>hyperdb.DEBUG, 'getnode', (self, classname, nodeid)
514574
# figure the columns we're fetching
515575
cl = self.classes[classname]
516-
cols, mls = self.determine_columns(cl)
576+
cols, mls = self.determine_columns(cl.properties.items())
517577
scols = ','.join(cols)
518578

519579
# perform the basic property fetch

test/test_db.py

Lines changed: 1 addition & 5 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.46 2002-09-13 08:20:13 richard Exp $
18+
# $Id: test_db.py,v 1.47 2002-09-16 08:04:46 richard Exp $
1919

2020
import unittest, os, shutil, time
2121

@@ -576,10 +576,6 @@ def testIDGeneration(self):
576576
id2 = self.db.issue.create(title="eggs", status='2')
577577
self.assertNotEqual(id1, id2)
578578

579-
def testNewProperty(self):
580-
# gadfly doesn't have an ALTER TABLE command :(
581-
pass
582-
583579
class gadflyReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
584580
def setUp(self):
585581
from roundup.backends import gadfly

0 commit comments

Comments
 (0)