Hello Michael,

Monday, April 03, 2006, 11:33:13 AM, you wrote:

So if I understand correctly if I want several simultaneously opened
transactions I have to construct several engines? Please correct me if
I am wrong.

So I changed behaviour of my program to following:
When tab with object opened for editing is created I do something like
this:

# Create a new engine using manually constructed connection pool
self.engine = tables.new_engine()

# Create new table corresponding to new engine
new_table = tables.contragents.toengine(self.engine)

# Create a copy of a class of object we edit
new_class = copy.copy(contragent.Contragent)

# Attach mapper to a new class
sqlalchemy.assign_mapper(new_class, new_table)

# Begin SQL level transaction
self.engine.begin()

# Select object from database using new engine, mapper and class
new_obj = new_class.mapper.select( \
    self.obj.__class__.c.id==id, for_update=True)[0]
print new_obj

And when object is saved to database, I do someting like that:

sqlalchemy.objectstore.commit(self.obj)
self.engine.commit()

And it works as I expect. Hurah!!! Thank's a lot.

How do you think is it a good solution, or there is a better way to
do it using SQLAlchemy?

And also it looks like I discovered a bug with connection pooling in
sqlalchemy.engine.py (I created a ticket in trac).

MB> for any kind of transactional locking to occur, you have to use explicit
MB> sessions with the engine.  SQLAlchemy has two different levels of
MB> operation; the "engine" level, which deals with SQL statements and
MB> connections, and the "object relational mapper" level, which deals with
MB> the state of objects in memory.

MB> so the "Session" you use from the "objectstore" does *not* represent a
MB> SQL-level transaction.  it will use one internally within its commit()
MB> statement but that one is opened and closed all inside that function.

MB> the session you are looking for looks like this:

MB> trans = engine.begin()

MB> ....do stuff

MB> trans.commit()

MB> you can use the objectstore.commit() within that as well, as described here:

MB> 
http://www.sqlalchemy.org/docs/unitofwork.myt#unitofwork_advscope_transactionnesting



MB> Vasily Sulatskov wrote:
>> Hello Michael,
>>
>> Monday, April 03, 2006, 1:05:27 AM, you wrote:
>>
>> I am building a GUI program, where opertators will modify database by
>> hand. So if two operators open one row of table for edition at the
>> same time and then one commits and then second commits then changes
>> made by operator who commits first will be lost.
>>
>> I googled for a while and found a suggestion to compare state of the
>> row in database before commit and if it changed do not commit but tell
>> the operator something like "We are sorry but the object you spent
>> editing for a last 30 minutes is changed in database, so all your
>> changes lost, try again". This is acceptable behaviour, but IMHO
>> locking objects open for editing is a better solution. It ensures no
>> data loss on database level. Perhaps there is a better solution but I
>> don't know it and can not find.
>>
>> Actually  SQLAlchemy works pretty good for me I use one connection for
>> objects  open for all read-only operations and when user wants to edit
>> object       I       create       another       connection      (using
>> sqlalchemy.objectstore.Session()   ).
>>
>> Documentation  says  that  "Sessions can be created on an ad-hoc basis
>> and used for individual groups of objects and operations. This has the
>> effect  of  bypassing  the  normal thread-local Session and explicitly
>> using a particular Session:". So if I understand this paragraph
>> correctly it will open new database connection and objects selected
>> from mapper using this section will use different database connection
>> from default "thread-local" objects.
>>
>> But I can't understand how can it be so that SQLAlchemy sends correct
>> SQL (like SELECT ... FOR UPDATE;) but the row is not locked. It should
>> be locked until commit in this connection, but it doesn't lock.
>> MySQLdb behaves itself in similar way when it in autocommit mode, i.e. you
>> send SELECT ... FOR UPDATE; command but it automatically commits and
>> lock you made instantly released.
>>
>> As far as I understand SQLAlchemy should send transaction commit when
>> i command session.commit(), but the lock is released instantly.
>>
>> Can someone explain what's happening?
>>
>> MB> "for update" is not a behavior SA's mapper was really designed to
>> support.
>> MB>  if you do not use an explicit engine transaction, then the connection
>> MB> object used for each operation will possibly be different each time,
>> and
>> MB> also a new cursor is used.  its not like it will always be this way,
>> but
>> MB> ive never had an occasion to use FOR UPDATE myself....is there any
>> reason
>> MB> why you cant just use a regular transaction ?
>>
>> MB> Vasily Sulatskov wrote:
>>>> Hello,
>>>>
>>>> I have a problem with "SELECT ... FOR UPDATE;" command.
>>>>
>>>> I have a MySQL database, table created with TYPE=INNODB engine
>>>> specification with proper transaction isolation level set.
>>>>
>>>> I want to issue "SELECT ... FOR UPDATE;" command to lock specific row
>>>> of table for updates.
>>>>
>>>> Here's a sample script:
>>>> # -*- coding: cp1251 -*-
>>>>
>>>> import sqlalchemy
>>>> import time
>>>> import sys
>>>>
>>>> databaseParams = { \
>>>>     'echo': True,
>>>>     'echo_uow': True,
>>>>     'logger': file( 'sql.log', 'w' ),
>>>>     'convert_unicode': True,}
>>>>
>>>> engine = sqlalchemy.create_engine(
>>>>     'mysql',
>>>>     {
>>>>         'db':'vasilytest',
>>>>         'user':'root',
>>>>         'passwd':'',
>>>>         'host':'127.0.0.1'
>>>>     },
>>>>     **databaseParams )
>>>>
>>>> contragents = sqlalchemy.Table( 'contragents', engine,
>>>>     sqlalchemy.Column( 'id', sqlalchemy.Integer, primary_key=True ),
>>>>     sqlalchemy.Column( 'first_name', sqlalchemy.String(50), default='',
>>>>         key='firstName' ),
>>>>     sqlalchemy.Column( 'last_name', sqlalchemy.String(50), default='',
>>>>         key='lastName' ),
>>>>     sqlalchemy.Column( 'patronymic', sqlalchemy.String(50) ),
>>>> default='',
>>>>     mysql_engine='INNODB' )
>>>>
>>>> if 'create' in sys.argv:
>>>>     contragents.create()
>>>>
>>>> class SqlStrMixing( object ):
>>>>     def __str__( self ):
>>>>         s = [ self.__class__.__name__ + ': ' ]
>>>>
>>>>         for c in self.c:
>>>>             s.append( '%s=%s ' % ( c.key, getattr(self, c.key) ) )
>>>>         return ''.join(s).encode('cp866')
>>>>
>>>> class Contragent(SqlStrMixing):
>>>>     pass
>>>>
>>>> sqlalchemy.assign_mapper( Contragent, contragents )
>>>>
>>>> session = sqlalchemy.objectstore.Session()
>>>>
>>>> session.begin()
>>>>
>>>> obj = Contragent.mapper.using(session).select(Contragent.c.id==17,
>>>> for_update=True)[0]
>>>> print obj
>>>>
>>>> time.sleep(20)
>>>>
>>>> session.commit()
>>>>
>>>> sqlalchemy.objectstore.commit()
>>>>
>>>> I launch first copy of this script and it immediatlely prints selected
>>>> object and sleeps for 20 seconds. Then I launch second copy of script
>>>> and it immediately prints selected object too. That's wrong, it should
>>>> block and wait for first script to commit transaction.
>>>>
>>>> When I inspect sql.log I see following:
>>>> SELECT contragents.patronymic [skiped for clarity]
>>>> contragents.first_name
>>>> AS contragents_first_name
>>>> FROM contragents
>>>> WHERE contragents.id = %s FOR UPDATE[17]
>>>>
>>>> i.e. right SQL command, but selected row doesn't lock.
>>>>
>>>> Perhaps I am doing something wrong with transactions.
>>>>
>>>> Can anyone explain what's going on?
>>>>
>>>> And how to achieve desired behaviour?
>>>>
>>>> And maybee there's another way to lock row of table for update?
>>>>
>>>> I did the same using regular DB-API and it worked as expected.
>>>> Here's source code:
>>>>
>>>> # -*- coding: cp1251 -*-
>>>> import MySQLdb as dbms
>>>> import time
>>>>
>>>> params = { \
>>>>     'host': '127.0.0.1',
>>>>     'user': 'root',
>>>>     'db'  : 'vasilytest',
>>>>     'passwd': '' }
>>>>
>>>> db = dbms.Connect( **params )
>>>> cursor = db.cursor()
>>>>
>>>> cursor.execute( """select * from contragents where id=17 for update;"""
>>>> )
>>>>
>>>> print cursor.fetchall()
>>>> time.sleep(20)
>>>>
>>>> db.commit()
>>>>
>>>> I launch first copy of this script and it immediately prints fetched
>>>> columns and sleeps for 20 seconds. Then I launch second copy of the
>>>> script and it blocks untill first script commits or interrupted (using
>>>> Ctrl-C or something), i.e. desired behaviour.
>>>>
>>>> --
>>>> Best regards,
>>>>  Vasily
>>>>
>>>>
>>>>
>>>>
>>>> -------------------------------------------------------
>>>> This SF.Net email is sponsored by xPML, a groundbreaking scripting
>>>> language
>>>> that extends applications into web and mobile media. Attend the live
>>>> webcast
>>>> and join the prime developer group breaking into this new coding
>>>> territory!
>>>> http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
>>>> _______________________________________________
>>>> Sqlalchemy-users mailing list
>>>> Sqlalchemy-users@lists.sourceforge.net
>>>> https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users
>>>>
>>
>>
>>
>> --
>> Best regards,
>>  Vasily                            mailto:[EMAIL PROTECTED]
>>
>>
>>
>>
>> -------------------------------------------------------
>> This SF.Net email is sponsored by xPML, a groundbreaking scripting
>> language
>> that extends applications into web and mobile media. Attend the live
>> webcast
>> and join the prime developer group breaking into this new coding
>> territory!
>> http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
>> _______________________________________________
>> Sqlalchemy-users mailing list
>> Sqlalchemy-users@lists.sourceforge.net
>> https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users
>>



MB> -------------------------------------------------------
MB> This SF.Net email is sponsored by xPML, a groundbreaking scripting language
MB> that extends applications into web and mobile media. Attend the live webcast
MB> and join the prime developer group breaking into this new coding territory!
MB> http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
MB> _______________________________________________
MB> Sqlalchemy-users mailing list
MB> Sqlalchemy-users@lists.sourceforge.net
MB> https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users



-- 
Best regards,
 Vasily                            mailto:[EMAIL PROTECTED]




-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users

Reply via email to