Hey Michael,

Just looking at your usage there, but would it make any sense at all to
have engine.begin() and engine.push_session() turn into one method?

Does it make sense to create multiple sessions if you're not going to be
doing simultaneous transactions?

I think I'm looking for a way to somehow reduce the number of
begin()/commit()/push()/pop() steps involved to maybe eliminate some of the
tendency users, including myself, will have towards confusion on this
issue.

Also, what happens if you pop a session without commiting transactions made
within it?

And then try to commit the transaction afterwards?  What about the
following case:

  sess1 = objectstore.Session()
  sess2 = objectstore.Session()

  objectstore.push_session(sess1)
  foo = MyObj()
  objectstore.push_session(sess2)
  bar = MyObj()
  bar.refers_to = foo
  objectstore.commit()     # What, exactly, will this commit?
  objectstore.pop_session()
  objectstore.commit()     # Or this?
  objectstore.pop_session()

Mind you, I'm not insisting that this is valid behavior -- the right answer
may be "it throws an exception somewhere around >< here".  Something to add
to the docs, at any rate.
-G

On Monday, April 3, 2006, 7:13:13 PM, you wrote:
> Yah, ok , youre getting into features that were just written a few  
> weeks ago, if you want simultaneous transactions, theres a feature on  
> engine called "push_session"/"pop_session".  you should not be  
> creating multiple engines for the same connection (i mean, you can,  
> but the experience will be very painful), since an engine doesnt  
> really represent a "connection", it represents "a database".    So  
> your example isnt "wrong" but its possible that it wont go very far  
> since it wasnt designed to work that way.

> To use engines with "nested" transactions, looks like this (ack,  
> havent documented on the site yet...)

> # outer transaction
> engine.begin()

> sqlsess = engine.push_session()
> try:
>         # inner transaction
>         engine.begin()

>         #commit inner transaction
>         engine.commit()
> finally:
>         sqlsess.pop()

> # commit outer transaction
> trans.commit()

> Now, you can do your transactions just like that above.  there is a  
> set of unit tests that illustrate this in the file test/engine.py .

> *Alternatively*, you can let the ORM do more of the work for you, by  
> using the "nest_on" argument to Session.  An example of this is here:

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

> Will try to document some more this week since your confusion is  
> entirely reasonable.

> On Apr 3, 2006, at 4:08 AM, Vasily Sulatskov wrote:

>> 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



-------------------------------------------------------
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