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