|
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 | 2 | ''' Relational database (SQL) backend common code. |
3 | 3 |
|
4 | 4 | Basics: |
@@ -178,128 +178,80 @@ def determine_columns(self, properties): |
178 | 178 | cols.sort() |
179 | 179 | return cols, mls |
180 | 180 |
|
181 | | - def update_class(self, spec, dbspec): |
| 181 | + def update_class(self, spec, old_spec): |
182 | 182 | ''' Determine the differences between the current spec and the |
183 | 183 | database version of the spec, and update where necessary |
184 | 184 | ''' |
185 | | - spec_schema = spec.schema() |
186 | | - if spec_schema == dbspec: |
187 | | - # no save needed for this one |
| 185 | + new_spec = spec |
| 186 | + new_has = new_spec.properties.has_key |
| 187 | + |
| 188 | + new_spec = new_spec.schema() |
| 189 | + if new_spec == old_spec: |
| 190 | + # no changes |
188 | 191 | return 0 |
| 192 | + |
189 | 193 | if __debug__: |
190 | 194 | print >>hyperdb.DEBUG, 'update_class FIRING' |
191 | 195 |
|
192 | 196 | # key property changed? |
193 | | - if dbspec[0] != spec_schema[0]: |
| 197 | + if old_spec[0] != new_spec[0]: |
194 | 198 | if __debug__: |
195 | 199 | print >>hyperdb.DEBUG, 'update_class setting keyprop', `spec[0]` |
196 | 200 | # XXX turn on indexing for the key property |
197 | 201 |
|
198 | | - # dict 'em up |
199 | | - spec_propnames,spec_props = [],{} |
200 | | - for propname,prop in spec_schema[1]: |
201 | | - spec_propnames.append(propname) |
202 | | - spec_props[propname] = prop |
203 | | - dbspec_propnames,dbspec_props = [],{} |
204 | | - for propname,prop in dbspec[1]: |
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]: |
| 202 | + # detect multilinks that have been removed, and drop their table |
| 203 | + old_has = {} |
| 204 | + for name,prop in old_spec[1]: |
| 205 | + old_has[name] = 1 |
| 206 | + if not new_has(name) and isinstance(prop, Multilink): |
| 207 | + # it's a multilink, and it's been removed - drop the old |
| 208 | + # table |
| 209 | + sql = 'drop table %s_%s'%(spec.classname, prop) |
| 210 | + if __debug__: |
| 211 | + print >>hyperdb.DEBUG, 'update_class', (self, sql) |
| 212 | + self.cursor.execute(sql) |
212 | 213 | continue |
213 | | - if __debug__: |
214 | | - print >>hyperdb.DEBUG, 'update_class ADD', (propname, prop) |
| 214 | + old_has = old_has.has_key |
215 | 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! |
| 216 | + # now figure how we populate the new table |
| 217 | + fetch = [] # fetch these from the old table |
| 218 | + properties = spec.getprops() |
| 219 | + for propname,x in new_spec[1]: |
| 220 | + prop = properties[propname] |
| 221 | + if isinstance(prop, Multilink): |
| 222 | + if not old_has(propname): |
| 223 | + # we need to create the new table |
220 | 224 | 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)) |
| 225 | + elif old_has(propname): |
| 226 | + # we copy this col over from the old table |
| 227 | + fetch.append('_'+propname) |
| 228 | + |
| 229 | + # select the data out of the old table |
| 230 | + fetch.append('id') |
| 231 | + fetch.append('__retired__') |
| 232 | + fetchcols = ','.join(fetch) |
| 233 | + cn = spec.classname |
| 234 | + sql = 'select %s from _%s'%(fetchcols, cn) |
| 235 | + if __debug__: |
| 236 | + print >>hyperdb.DEBUG, 'update_class', (self, sql) |
| 237 | + self.cursor.execute(sql) |
| 238 | + olddata = self.cursor.fetchall() |
258 | 239 |
|
259 | | - else: |
260 | | - # modify the property |
261 | | - if __debug__: |
262 | | - print >>hyperdb.DEBUG, 'update_class NOOP' |
263 | | - pass # NOOP in gadfly |
| 240 | + # drop the old table |
| 241 | + self.cursor.execute('drop table _%s'%cn) |
264 | 242 |
|
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 |
| 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) |
270 | 250 | if __debug__: |
271 | | - print >>hyperdb.DEBUG, 'update_class REMOVE', `prop` |
| 251 | + print >>hyperdb.DEBUG, 'update_class', (self, sql, olddata[0]) |
| 252 | + for entry in olddata: |
| 253 | + self.cursor.execute(sql, *entry) |
272 | 254 |
|
273 | | - # delete the property |
274 | | - if isinstance(prop, Multilink): |
275 | | - sql = 'drop table %s_%s'%(spec.classname, prop) |
276 | | - if __debug__: |
277 | | - print >>hyperdb.DEBUG, 'update_class', (self, sql) |
278 | | - self.cursor.execute(sql) |
279 | | - else: |
280 | | - # no ALTER TABLE, so we: |
281 | | - # 1. pull out the data, excluding the removed column |
282 | | - oldcols, x = self.determine_columns(spec.properties.items()) |
283 | | - oldcols.append('id') |
284 | | - oldcols.append('__retired__') |
285 | | - # remove the missing column |
286 | | - oldcols.remove('_'+propname) |
287 | | - cn = spec.classname |
288 | | - sql = 'select %s from _%s'%(','.join(oldcols), cn) |
289 | | - self.cursor.execute(sql, (None,)) |
290 | | - olddata = sql.fetchall() |
291 | | - |
292 | | - # 2. drop the old table |
293 | | - self.cursor.execute('drop table _%s'%cn) |
294 | | - |
295 | | - # 3. create the new table |
296 | | - cols, mls = self.create_class_table(self, spec) |
297 | | - assert oldcols != cols, "Column lists don't match!" |
298 | | - |
299 | | - # 4. populate with the data from step one |
300 | | - qs = ','.join([self.arg for x in cols]) |
301 | | - sql = 'insert into _%s values (%s)'%(cn, s) |
302 | | - self.cursor.execute(sql, olddata) |
303 | 255 | return 1 |
304 | 256 |
|
305 | 257 | def create_class_table(self, spec): |
|
0 commit comments