Hello,

* Apologies if this is a duplicate -- I attempted to post a few hours
earlier but the result vanished.  Could be that the earlier effort went off
as a direct email to MB, instead of a post to this group as intended.
"chris e" posted about this topic in April and I was trying to respond on
that thread, but Google Groups won't take a response this long after.

Below, i've included a working test script that causes a
ConcurrentModificationError.  I'm wondering if that is the right result.  I
was working with this section of my code specifically to work out what
cascade relationships I wanted to use in my tables and mappers, and while
experimenting with various cascade options I fully expected to run into some
exceptions where I couldn't delete some object because of its
dependencies.  But seeing a ConcurrentModificationError instead was a
surprise.  So I'm wondering if I've just got something set up wrong.

I've done my best to trim this to a bare-bones minimal reproduction of the
error, though it's still not tiny.  Can't really cut out any more
tables/classes/mappers without rendering the test script alien to what I'm
actually working on.

  I'm modeling a rental reservation system.  rentable Items are (multiple
table) polymorphic.  In this example the subclasses of Item have all been
trimmed down to just one for clarity: Ski.  Events are polymorphic, too;
here I've included only most obvious subclass of event: Reservation.  Each
reservation has one customer associated with it.  A reservation is
associated to one or more Gearsets; each gearset has one or more
Items.  Items to Gearsets is actually many-to-many, and this is accomplished
via three secondary tables: historyfile, currentfile, and futurefile.  In
this way an item has a distinct history of what sets (and thereby what
rentals) it has been a part of in the past; a record of what it's doing
right now, and what sets (and thereby rentals) it is reserved to be a part
of in the future.

If I had to guess, maybe this 3-way split of secondary/association tables is
the thing that might be causing problems.  I set it up that way to keep a
strong, up-front distinction between events being in an item's future vs.
its present case vs. its past.  But I suppose I could actually achieve the
same thing with all gearset<>item associations being in a single table, and
just map the item's future/current/past to separate selects on that
table.  Right?

But that's semi-off-topic.  Again, I thought maybe I'd encounter some
cascade problems but I didn't expect a ConcurrentModificationError to happen
where it's happening here.

Here's the test script and stack trace:

# semi-minimal test example for ConcurrentModificationError:
# Deleted rowcount 0 does not match number of objects deleted 1

import sqlalchemy
from sqlalchemy import Table, BoundMetaData, Column
from sqlalchemy import Integer, String, ForeignKey, PrimaryKeyConstraint
from sqlalchemy import ForeignKeyConstraint, polymorphic_union
from sqlalchemy import mapper, relation, backref
from sqlalchemy import create_engine, create_session

db = create_engine('postgres://postgres:[EMAIL PROTECTED]:5432/test2',
    encoding='utf-8')
metadata = BoundMetaData(db)

items = Table('items', metadata,
    Column('id', Integer, primary_key=True, autoincrement=True),
    Column('type', String(20)))

skis = Table('skis', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer),
    ForeignKeyConstraint(['id'], ['items.id']))

events = Table('events', metadata,
    Column('id', Integer, primary_key=True),
    Column('etype', String(20)),
    Column('data', Integer))

reservations = Table('reservations', metadata,
    Column('id', Integer, ForeignKey('events.id'), primary_key=True),
    Column('customer_id', Integer, ForeignKey('customers.id')),
    Column('data', Integer))

gearsets = Table('gearsets', metadata,
    Column('id', Integer, primary_key=True),
    Column('event_id', Integer, ForeignKey('events.id')),
    Column('data', Integer))

historyfile = Table('historyfile', metadata,
    Column('item_id', Integer, ForeignKey('items.id'), primary_key=True),
    Column('gearset_id', Integer, ForeignKey('gearsets.id'),
primary_key=True))

currentfile = Table('currentfile', metadata,
    Column('item_id', Integer, ForeignKey('items.id'), primary_key=True),
    Column('gearset_id', Integer, ForeignKey('gearsets.id'),
primary_key=True))

futurefile = Table('futurefile', metadata,
    Column('item_id', Integer, ForeignKey('items.id'), primary_key=True),
    Column('gearset_id', Integer, ForeignKey('gearsets.id'),
primary_key=True))

customers = Table('customers', metadata,
    Column('id', Integer, primary_key = True, autoincrement=True),
    Column('data', Integer))

class Item(object): pass
class Ski(Item): pass
class GearSet(object): pass
class Event(object): pass
class Reservation(Event): pass
class Customer(object): pass

item_join = polymorphic_union(
    {
        'ski':items.join(skis),
        'item':items.select(items.c.type=='item'),
    }, None, 'ijoin')

item_mapper = mapper(Item, items,
    select_table=item_join,
    polymorphic_on=item_join.c.type,
    polymorphic_identity='item',
    properties = {
        'history': relation(GearSet,
            secondary=historyfile,
            backref=backref('items_history', uselist=True),
            uselist=True),
        'current': relation(GearSet,
            secondary=currentfile,
            backref=backref('items_current', uselist=True),
            uselist=False),
        'future': relation(GearSet,
            secondary=futurefile,
            backref=backref('items_future', uselist=True),
            uselist=True)})

customer_mapper = mapper(Customer, customers)

gearset_mapper = mapper(GearSet, gearsets,
    properties = {
        'event': relation(Event,
            backref=backref('gearsets', uselist=True),
            uselist=False)
    })


event_join = polymorphic_union(
    {
        'reservation':events.join(reservations),
        'event':events.select(events.c.etype=='event'),
    }, None, 'ejoin')

event_mapper = mapper(Event, events,
    select_table=event_join,
    polymorphic_on=event_join.c.etype,
    polymorphic_identity='event')

reservation_mapper = mapper(Reservation, reservations,
    inherits=event_mapper,
    polymorphic_identity='reservation',
    properties = {
        'customer': relation(Customer,
            backref=backref('reservations', uselist=True),
            uselist=False)
    })

ski_mapper = mapper(Ski, skis,
    inherits=item_mapper,
    polymorphic_identity='ski')


if __name__ == '__main__':
    metadata.create_all()
    session = create_session()

    ski = Ski()
    gearset = GearSet()
    customer = Customer()
    reservation = Reservation()
    reservation.customer = customer
    gearset.items_future.append(ski)
    gearset.event = reservation
    session.save(reservation)
    session.flush()

    item = session.query(Item).selectfirst()
    item.future = []
    session.delete(item)
    session.flush()
    session.close()
    metadata.drop_all()


ConcurrentModificationError: Deleted rowcount 0 does not match number of
objects deleted 1

Traceback (innermost last):

File "h:\Documents and Settings\Eric\My
Documents\rentalproject\deletetest.py", line 1, in <module>
  # semi-minimal test example for ConcurrentModificationError:
File "h:\Documents and Settings\Eric\My
Documents\rentalproject\deletetest.py", line 138, in <module>
  session.flush()
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\session.py", line 302, in flush
  self.uow.flush(self, objects)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 213, in flush
  flush_context.execute()
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 400, in execute
  UOWExecutor().execute(self, head)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1018, in execute
  self.execute_save_steps(trans, task)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1037, in
execute_save_steps
  self.execute_childtasks(trans, task, False)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1055, in
execute_childtasks
  self.execute(trans, child, isdelete)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1018, in execute
  self.execute_save_steps(trans, task)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1037, in
execute_save_steps
  self.execute_childtasks(trans, task, False)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1055, in
execute_childtasks
  self.execute(trans, child, isdelete)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1018, in execute
  self.execute_save_steps(trans, task)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1037, in
execute_save_steps
  self.execute_childtasks(trans, task, False)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1055, in
execute_childtasks
  self.execute(trans, child, isdelete)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1018, in execute
  self.execute_save_steps(trans, task)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1036, in
execute_save_steps
  self.execute_dependencies(trans, task, True)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1051, in
execute_dependencies
  self.execute_dependency(trans, dep, True)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 1029, in
execute_dependency
  dep.execute(trans, isdelete)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\unitofwork.py", line 986, in execute
  self.processor.process_dependencies(self.targettask, [elem.obj for elem in
self.targettask.polymorphic_todelete_elements if elem.obj is not None],
trans, delete=True)
File "h:\Python25\Lib\site-packages\sqlalchemy-
0.3.8-py2.5.egg\sqlalchemy\orm\dependency.py", line 372, in
process_dependencies
  raise exceptions.ConcurrentModificationError("Deleted rowcount %d does not
match number of objects deleted %d" % (result.rowcount,
len(secondary_delete)))


Any observations/suggestions appreciated.

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to