On 10/19/15 11:57 AM, Mike Bayer wrote:
> 
> 
> On 10/19/15 11:44 AM, Steven Winfield wrote:
>> The problem seems to be that the result set from an executemany() call
>> in psycopg2 is discarded - here is a good stackoverflow article on this
>> subject:
>> http://stackoverflow.com/questions/21624844/getting-ids-of-multiple-rows-inserted-in-psycopg2
>>
>> In _emit_update_statements in orm/persistence.py, and here
>> <http://docs.sqlalchemy.org/en/rel_1_0/changelog/migration_10.html?highlight=version_id_col#update-statements-are-now-batched-with-executemany-in-a-flush>
>> in the v1.0 migration docs, one of the conditions for using executemany is:
>> "The mapping does not use a |version_id_col|
>> <http://docs.sqlalchemy.org/en/rel_1_0/orm/mapping_api.html#sqlalchemy.orm.mapper.params.version_id_col>,
>> or the backend dialect supports a “sane” rowcount for an executemany()
>> operation;"
>>
>> ...but I'm not sure that is enough - psycopg2 does support sane
>> rowcounts, and its dialect's supports_sane_rowcount and
>> supports_sane_multi_rowcount are both rightly True (I work with Tom, and
>> we're using psycopg2 v2.6 by the way - I know those values are
>> predicated on the psycopg2 version).
>>
>> I'm working on a patch that adds another member to the default dialect
>> called "supports_executemany_results" that defaults to True 
> 
> I wouldn't do that, no DBAPI has results for executemany.  The flag is
> not needed.
> 
> This is very likely yet another 1.0 regression caused by
> http://docs.sqlalchemy.org/en/rel_1_0/changelog/migration_10.html#update-statements-are-now-batched-with-executemany-in-a-flush
> and not checking closely enough for the right conditions.   I'll add a
> new ticket soon.

issue
https://bitbucket.org/zzzeek/sqlalchemy/issues/3556/update-executemany-enhancement-breaks
which is now resolved.  I will try to release 1.0.9 this week; please
confirm the fix in the latest rel_1_0 branch fixes this issue for you as
well, thanks!





> 
> 
> 
> 
> but is set
>> to False in PGDialect_psycopg2 in dialects/postgres/psycopg2.py. This
>> can then be used in the determination of allow_multirow (line 646 in
>> orm/persistence.py). This will preserve the current behaviour of other
>> dialects, but of course there could be others with the same problem.
>>
>> Steve
>>
>> On Monday, October 19, 2015 at 4:22:36 PM UTC+1, Tom Flannaghan wrote:
>>
>>     Hi,
>>
>>     We are currently trying to upgrade to sqlalchemy 1.0.8 and have hit
>>     a problem with executemany() and row versioning. We are using
>>     postgres and psycopg2 as a backend. We have tables that use server
>>     side versioning (as discussed at
>>     
>> http://docs.sqlalchemy.org/en/rel_0_9/orm/versioning.html#server-side-version-counters
>>     
>> <http://docs.sqlalchemy.org/en/rel_0_9/orm/versioning.html#server-side-version-counters>),
>>     and when multiple rows are updated, the UPDATE statements are
>>     batched together using executemany() in the new version of
>>     sqlalchemy. Unfortunately, psycopg2 does not support iterating over
>>     the results of an executemany() query (see
>>     http://initd.org/psycopg/docs/cursor.html#cursor.executemany
>>     <http://initd.org/psycopg/docs/cursor.html#cursor.executemany>) so
>>     the new row versions returned by the update statement cannot be read.
>>
>>     Here is an example that illustrates the problem:
>>
>>     from sqlalchemy import Column, FetchedValue, Integer, String
>>     from sqlalchemy.ext.declarative import declarative_base
>>     from sqlalchemy.orm import sessionmaker
>>
>>     Base = declarative_base()
>>
>>     class Port(Base):
>>         ''' Table for storing ports. '''
>>         __tablename__ = "port"
>>         
>>         name = Column(String, primary_key=True)
>>         port = Column(Integer)
>>         
>>         xmin = Column(Integer, server_default=FetchedValue(),
>>     server_onupdate=FetchedValue(), system=True)
>>         __mapper_args__ = {"version_id_col": xmin,
>>                            "version_id_generator": False}
>>                             
>>
>>     engine = create_engine(...)
>>     Session = sessionmaker(bind=engine)
>>
>>     session = Session()
>>     session.bind.echo = True
>>
>>     p1 = Port(name='a', port=1)
>>     p2 = Port(name='b', port=2)
>>     session.add(p1)
>>     session.add(p2)
>>     session.flush()
>>
>>     # this update fails
>>     p1.port = 3
>>     p2.port = 4
>>     session.flush()
>>
>>     session.rollback()
>>
>>     The SQL generated for the update looks like this:
>>
>>     2015-10-19 14:46:30,289 INFO sqlalchemy.engine.base.Engine UPDATE
>>     comm.port SET port=%(port)s WHERE comm.port.name
>>     <http://comm.port.name> = %(comm_port_name)s AND comm.port.xmin =
>>     %(comm_port_xmin)s RETURNING comm.port.xmin
>>     2015-10-19 14:46:30,289 INFO sqlalchemy.engine.base.Engine
>>     ({'comm_port_xmin': '536394944', 'comm_port_name': 'a', 'port': 3},
>>     {'comm_port_xmin': '536394944', 'comm_port_name': 'b', 'port': 4})
>>
>>     This code raises the following exception:
>>
>>     File user!flannt!untitled6.py, line 52, in : session.flush() 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\session.py,
>>     line 2004, in flush : self._flush(objects) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\session.py,
>>     line 2122, in _flush : transaction.rollback(_capture_exception=True) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\util\langhelpers.py,
>>     line 60, in __exit__ : compat.reraise(exc_type, exc_value, exc_tb) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\session.py,
>>     line 2086, in _flush : flush_context.execute() 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\unitofwork.py,
>>     line 373, in execute : rec.execute(self) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\unitofwork.py,
>>     line 532, in execute : uow 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\persistence.py,
>>     line 170, in save_obj : mapper, table, update) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\orm\persistence.py,
>>     line 692, in _emit_update_statements : execute(statement, multiparams) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\base.py,
>>     line 914, in execute : return meth(self, multiparams, params) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\sql\elements.py,
>>     line 323, in _execute_on_connection : return
>>     connection._execute_clauseelement(self, multiparams, params) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\base.py,
>>     line 1010, in _execute_clauseelement : compiled_sql, distilled_params 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\base.py,
>>     line 1159, in _execute_context : result =
>>     context._setup_crud_result_proxy() 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\default.py,
>>     line 832, in _setup_crud_result_proxy : row = result.fetchone() 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\result.py,
>>     line 1026, in fetchone : self.cursor, self.context) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\base.py,
>>     line 1341, in _handle_dbapi_exception : exc_info 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\util\compat.py,
>>     line 199, in raise_from_cause : reraise(type(exception), exception,
>>     tb=exc_tb) 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\result.py,
>>     line 1017, in fetchone : row = self._fetchone_impl() 
>>     File
>>     
>> I:\sw\external\20151014-0\python27\lib\site-packages\sqlalchemy-1.0.8-py2.7-win32.egg\sqlalchemy\engine\result.py,
>>     line 898, in _fetchone_impl : return self.cursor.fetchone() 
>>     ProgrammingError: (psycopg2.ProgrammingError) no results to fetch
>>
>> -- 
>> You received this message because you are subscribed to the Google
>> Groups "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send
>> an email to sqlalchemy+unsubscr...@googlegroups.com
>> <mailto:sqlalchemy+unsubscr...@googlegroups.com>.
>> To post to this group, send email to sqlalchemy@googlegroups.com
>> <mailto:sqlalchemy@googlegroups.com>.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to