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__ = '''
33About 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
0 commit comments