Mercurial > p > roundup > code
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 ''' |
