Hi! I applied the patch with an additional test - that the connection is not a transaction and is in autocommit mode. A transaction or non-autocommit connection must be rolled back after a exception but the place for the rollback is application-dependent so I didn't include a rollback in the code. Inside a transaction it's also usually impossible to remove the parent row(s) after an error - the only thing that's allowed is a rollback. Committed in the revisions 4101-4103 (branches 0.11, 0.12 ad the trunk). Thank you!
On Mon, Dec 07, 2009 at 06:41:54PM +0200, Tom Coetser wrote: > I am doing some tests with InheritableSQLobject and have run into a problem > which I hope to get some feedback on. > > The problem is that when creating a new object based on an > InheritableSQLObject class, and the creation of the child object fails after > a new parent was created, the parent record will remain in the database > without the relevant child. > > Here is a short example: > > ~~~~~~~~~~ file: inheritTest3.py ~~~~~~~~~~~~ > from sqlobject import * > from sqlobject.inheritance import InheritableSQLObject > > __connection__ = "sqlite:/:memory:" > > class Vehicle(InheritableSQLObject): > vid = StringCol(notNone=True, default=None, unique=True) > wheels = IntCol() > > class Bicycle(Vehicle): > seats = IntCol(notNone=True, default=None) > > class Car(Vehicle): > doors = IntCol(notNone=True, default=None) > > def createTables(): > Vehicle.createTable() > Bicycle.createTable() > Car.createTable() > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > Here is a sample session showing the problem when trying to create a bicycle > but only supplying the args required for the Vehicle part of the object: > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > In [1]: from inheritTest3 import * > > In [2]: createTables(True) > > In [3]: b = Bicycle(vid="id1", wheels=4) > ERROR: An unexpected error occurred while tokenizing input > The following traceback may be corrupted or invalid > The error message is: ('EOF in multi-line statement', (22, 0)) > > --------------------------------------------------------------------------- > IntegrityError Traceback (most recent call last) > > /home/tomc/mobilereporter.repo/main/app/<ipython console> in <module>() > > /usr/lib/python2.5/site-packages/sqlobject/main.pyc in __init__(self, **kw) > 1221 id = None > 1222 > -> 1223 self._create(id, **kw) > 1224 > > .... snipping lots of traceback parts ............ > > /usr/lib/python2.5/site-packages/sqlobject/sqlite/sqliteconnection.pyc in > _executeRetry(self, conn, cursor, query) > 187 raise DuplicateEntryError(msg) > 188 else: > --> 189 raise IntegrityError(msg) > 190 except self.module.InternalError, e: > 191 raise InternalError(ErrorMessage(e)) > > IntegrityError: bicycle.seats may not be NULL > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > If you now supply all required args for the bicycle, using the same field > values for the vid and wheels as before, a duplicate entry error is raised: > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > In [4]: b = Bicycle(vid="id1", wheels=4, seats=2) > ERROR: An unexpected error occurred while tokenizing input > The following traceback may be corrupted or invalid > The error message is: ('EOF in multi-line statement', (22, 0)) > > --------------------------------------------------------------------------- > DuplicateEntryError Traceback (most recent call last) > > /home/tomc/mobilereporter.repo/main/app/<ipython console> in <module>() > > /usr/lib/python2.5/site-packages/sqlobject/main.pyc in __init__(self, **kw) > 1221 id = None > 1222 > -> 1223 self._create(id, **kw) > 1224 > 1225 for func in post_funcs: > > .... snipping lots of traceback parts ............ > > /usr/lib/python2.5/site-packages/sqlobject/sqlite/sqliteconnection.pyc in > _executeRetry(self, conn, cursor, query) > 185 msg = ErrorMessage(e) > 186 if msg.startswith('column') and msg.endswith('not > unique'): > --> 187 raise DuplicateEntryError(msg) > 188 else: > 189 raise IntegrityError(msg) > > DuplicateEntryError: column vid is not unique > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > The duplicate entry is because of the parent vehicle record was created when > the first bicycle creation failed: > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > In [5]: q = "SELECT * from vehicle" > > In [6]: Vehicle._connection.queryAll(q) > Out[6]: [(1, 'id1', 4, 'Bicycle')] > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > So, the problem is that if a new child object is created, but fails after the > parent has been created, the parent is left in tact in the database. > > I'm attaching a patch as a possible fix for this, but I am not sure under > which other circumstances this patch will cause other problems. Maybe a flag > is needed inside the block that creates the child (parentCreatedHere=True), > and only if this flag is set, should the parent be deleted in exception if > the child creation failed? > > I would appreciate if someone with more insight into this can look at the > patch an modify where neccessary. > > BTW: This patch is against SQLObject 0.11.0. > > > > Cheers, > Tom > --- inheritance/__init__.py.orig 2009-12-07 14:47:31.000000000 +0000 > +++ inheritance/__init__.py 2009-12-07 16:35:39.000000000 +0000 > @@ -384,7 +384,19 @@ > > id = self._parent.id > > - super(InheritableSQLObject, self)._create(id, **kw) > + # TC: Create this record and catch all exceptions in order to destroy > + # TC: the parent if the child can not be created. > + try: > + super(InheritableSQLObject, self)._create(id, **kw) > + except: > + # TC: If we are the child, destroy the parent > + if self.sqlmeta.parentClass: > + self._parent.destroySelf() > + #TC: Do we need to do this?? > + self._parent = None > + # TC: Reraise the original exception > + raise > + > > def _findAlternateID(cls, name, dbName, value, connection=None): > result = list(cls.selectBy(connection, **{name: value})) Oleg. -- Oleg Broytman http://phd.pp.ru/ p...@phd.pp.ru Programmers don't die, they just GOSUB without RETURN. ------------------------------------------------------------------------------ The Planet: dedicated and managed hosting, cloud storage, colocation Stay online with enterprise data centers and the best network in the business Choose flexible plans and management services without long-term contracts Personal 24x7 support from experience hosting pros just a phone call away. http://p.sf.net/sfu/theplanet-com _______________________________________________ sqlobject-discuss mailing list sqlobject-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss