comparison roundup/backends/rdbms_common.py @ 1479:405e91b5be46

fixed rdbms mutation of properties
author Richard Jones <richard@users.sourceforge.net>
date Thu, 27 Feb 2003 11:07:39 +0000
parents 5a01e90b7dc9
children b3f2484babce
comparison
equal deleted inserted replaced
1478:2ec91ead3add 1479:405e91b5be46
1 # $Id: rdbms_common.py,v 1.36 2003-02-26 23:42:54 richard Exp $ 1 # $Id: rdbms_common.py,v 1.37 2003-02-27 11:07:36 richard Exp $
2 ''' Relational database (SQL) backend common code. 2 ''' Relational database (SQL) backend common code.
3 3
4 Basics: 4 Basics:
5 5
6 - map roundup classes to relational tables 6 - map roundup classes to relational tables
176 else: 176 else:
177 cols.append('_'+col) 177 cols.append('_'+col)
178 cols.sort() 178 cols.sort()
179 return cols, mls 179 return cols, mls
180 180
181 def update_class(self, spec, dbspec): 181 def update_class(self, spec, old_spec):
182 ''' Determine the differences between the current spec and the 182 ''' Determine the differences between the current spec and the
183 database version of the spec, and update where necessary 183 database version of the spec, and update where necessary
184 ''' 184 '''
185 spec_schema = spec.schema() 185 new_spec = spec
186 if spec_schema == dbspec: 186 new_has = new_spec.properties.has_key
187 # no save needed for this one 187
188 new_spec = new_spec.schema()
189 if new_spec == old_spec:
190 # no changes
188 return 0 191 return 0
192
189 if __debug__: 193 if __debug__:
190 print >>hyperdb.DEBUG, 'update_class FIRING' 194 print >>hyperdb.DEBUG, 'update_class FIRING'
191 195
192 # key property changed? 196 # key property changed?
193 if dbspec[0] != spec_schema[0]: 197 if old_spec[0] != new_spec[0]:
194 if __debug__: 198 if __debug__:
195 print >>hyperdb.DEBUG, 'update_class setting keyprop', `spec[0]` 199 print >>hyperdb.DEBUG, 'update_class setting keyprop', `spec[0]`
196 # XXX turn on indexing for the key property 200 # XXX turn on indexing for the key property
197 201
198 # dict 'em up 202 # detect multilinks that have been removed, and drop their table
199 spec_propnames,spec_props = [],{} 203 old_has = {}
200 for propname,prop in spec_schema[1]: 204 for name,prop in old_spec[1]:
201 spec_propnames.append(propname) 205 old_has[name] = 1
202 spec_props[propname] = prop 206 if not new_has(name) and isinstance(prop, Multilink):
203 dbspec_propnames,dbspec_props = [],{} 207 # it's a multilink, and it's been removed - drop the old
204 for propname,prop in dbspec[1]: 208 # table
205 dbspec_propnames.append(propname)
206 dbspec_props[propname] = prop
207
208 # now compare
209 for propname in spec_propnames:
210 prop = spec_props[propname]
211 if dbspec_props.has_key(propname) and prop==dbspec_props[propname]:
212 continue
213 if __debug__:
214 print >>hyperdb.DEBUG, 'update_class ADD', (propname, prop)
215
216 if not dbspec_props.has_key(propname):
217 # add the property
218 if isinstance(prop, Multilink):
219 # all we have to do here is create a new table, easy!
220 self.create_multilink_table(spec, propname)
221 continue
222
223 # no ALTER TABLE, so we:
224 # 1. pull out the data, including an extra None column
225 oldcols, x = self.determine_columns(dbspec[1])
226 oldcols.append('id')
227 oldcols.append('__retired__')
228 cn = spec.classname
229 sql = 'select %s,%s from _%s'%(','.join(oldcols), self.arg, cn)
230 if __debug__:
231 print >>hyperdb.DEBUG, 'update_class', (self, sql, None)
232 self.cursor.execute(sql, (None,))
233 olddata = self.cursor.fetchall()
234
235 # 2. drop the old table
236 self.cursor.execute('drop table _%s'%cn)
237
238 # 3. create the new table
239 cols, mls = self.create_class_table(spec)
240 # ensure the new column is last
241 cols.remove('_'+propname)
242 assert oldcols == cols, "Column lists don't match!"
243 cols.append('_'+propname)
244
245 # 4. populate with the data from step one
246 s = ','.join([self.arg for x in cols])
247 scols = ','.join(cols)
248 sql = 'insert into _%s (%s) values (%s)'%(cn, scols, s)
249
250 # GAH, nothing had better go wrong from here on in... but
251 # we have to commit the drop...
252 # XXX this isn't necessary in sqlite :(
253 self.conn.commit()
254
255 # do the insert
256 for row in olddata:
257 self.sql(sql, tuple(row))
258
259 else:
260 # modify the property
261 if __debug__:
262 print >>hyperdb.DEBUG, 'update_class NOOP'
263 pass # NOOP in gadfly
264
265 # and the other way - only worry about deletions here
266 for propname in dbspec_propnames:
267 prop = dbspec_props[propname]
268 if spec_props.has_key(propname):
269 continue
270 if __debug__:
271 print >>hyperdb.DEBUG, 'update_class REMOVE', `prop`
272
273 # delete the property
274 if isinstance(prop, Multilink):
275 sql = 'drop table %s_%s'%(spec.classname, prop) 209 sql = 'drop table %s_%s'%(spec.classname, prop)
276 if __debug__: 210 if __debug__:
277 print >>hyperdb.DEBUG, 'update_class', (self, sql) 211 print >>hyperdb.DEBUG, 'update_class', (self, sql)
278 self.cursor.execute(sql) 212 self.cursor.execute(sql)
279 else: 213 continue
280 # no ALTER TABLE, so we: 214 old_has = old_has.has_key
281 # 1. pull out the data, excluding the removed column 215
282 oldcols, x = self.determine_columns(spec.properties.items()) 216 # now figure how we populate the new table
283 oldcols.append('id') 217 fetch = [] # fetch these from the old table
284 oldcols.append('__retired__') 218 properties = spec.getprops()
285 # remove the missing column 219 for propname,x in new_spec[1]:
286 oldcols.remove('_'+propname) 220 prop = properties[propname]
287 cn = spec.classname 221 if isinstance(prop, Multilink):
288 sql = 'select %s from _%s'%(','.join(oldcols), cn) 222 if not old_has(propname):
289 self.cursor.execute(sql, (None,)) 223 # we need to create the new table
290 olddata = sql.fetchall() 224 self.create_multilink_table(spec, propname)
291 225 elif old_has(propname):
292 # 2. drop the old table 226 # we copy this col over from the old table
293 self.cursor.execute('drop table _%s'%cn) 227 fetch.append('_'+propname)
294 228
295 # 3. create the new table 229 # select the data out of the old table
296 cols, mls = self.create_class_table(self, spec) 230 fetch.append('id')
297 assert oldcols != cols, "Column lists don't match!" 231 fetch.append('__retired__')
298 232 fetchcols = ','.join(fetch)
299 # 4. populate with the data from step one 233 cn = spec.classname
300 qs = ','.join([self.arg for x in cols]) 234 sql = 'select %s from _%s'%(fetchcols, cn)
301 sql = 'insert into _%s values (%s)'%(cn, s) 235 if __debug__:
302 self.cursor.execute(sql, olddata) 236 print >>hyperdb.DEBUG, 'update_class', (self, sql)
237 self.cursor.execute(sql)
238 olddata = self.cursor.fetchall()
239
240 # drop the old table
241 self.cursor.execute('drop table _%s'%cn)
242
243 # create the new table
244 self.create_class_table(spec)
245
246 if olddata:
247 # do the insert
248 args = ','.join([self.arg for x in fetch])
249 sql = 'insert into _%s (%s) values (%s)'%(cn, fetchcols, args)
250 if __debug__:
251 print >>hyperdb.DEBUG, 'update_class', (self, sql, olddata[0])
252 for entry in olddata:
253 self.cursor.execute(sql, *entry)
254
303 return 1 255 return 1
304 256
305 def create_class_table(self, spec): 257 def create_class_table(self, spec):
306 ''' create the class table for the given spec 258 ''' create the class table for the given spec
307 ''' 259 '''

Roundup Issue Tracker: http://roundup-tracker.org/