On 7/20/15 4:02 AM, Lele Gaifax wrote:
Hi all,

I have a rather simple tool that loads data from YAML and put it in the
database, using the ORM layer. It designed so that it basically perform what's
called an "UPSERT", so I can load the same YAML multiple times with no
additional updates.

A few days ago I got a TypeError exception deep inside the ORM layer:

     Traceback (most recent call last):
       File "p.py", line 59, in <module>
         session.commit()
       File ".../python3.4/site-packages/sqlalchemy/orm/session.py", line 790, 
in commit
         self.transaction.commit()
       File ".../python3.4/site-packages/sqlalchemy/orm/session.py", line 392, 
in commit
         self._prepare_impl()
       File ".../python3.4/site-packages/sqlalchemy/orm/session.py", line 372, 
in _prepare_impl
         self.session.flush()
       File ".../python3.4/site-packages/sqlalchemy/orm/session.py", line 2004, 
in flush
         self._flush(objects)
       File ".../python3.4/site-packages/sqlalchemy/orm/session.py", line 2122, 
in _flush
         transaction.rollback(_capture_exception=True)
       File ".../python3.4/site-packages/sqlalchemy/util/langhelpers.py", line 
60, in __exit__
         compat.reraise(exc_type, exc_value, exc_tb)
       File ".../python3.4/site-packages/sqlalchemy/util/compat.py", line 182, 
in reraise
         raise value
       File ".../python3.4/site-packages/sqlalchemy/orm/session.py", line 2086, 
in _flush
         flush_context.execute()
       File ".../python3.4/site-packages/sqlalchemy/orm/unitofwork.py", line 
373, in execute
         rec.execute(self)
       File ".../python3.4/site-packages/sqlalchemy/orm/unitofwork.py", line 
532, in execute
         uow
       File ".../python3.4/site-packages/sqlalchemy/orm/persistence.py", line 
170, in save_obj
         mapper, table, update)
       File ".../python3.4/site-packages/sqlalchemy/orm/persistence.py", line 
692, in _emit_update_statements
         rows += c.rowcount
     TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

This morning I investigated a bit and the following script exhibits the problem:

     import sqlalchemy as sa
     import sqlalchemy.orm as orm

     engine = sa.create_engine('mysql+cymysql://localhost/raccoon', echo=True)
     md = sa.MetaData(bind=engine)

     test_t = sa.Table(
         'test1', md,
         sa.Column('id', sa.Integer, primary_key=True),
         sa.Column('zip', sa.String(5)),
         sa.Column('description', sa.String(20)),
     )

     class Test(object):
         pass

     orm.mapper(Test, test_t)

     md.create_all()

     smaker = orm.sessionmaker(autoflush=False, autocommit=False, bind=engine)
     session = smaker()

     first = session.query(Test).get(1)
     if first is None:
         first = Test()
         first.id = 1
         first.zip = "2512"
         first.description = 'Test'
         session.add(first)

     second = session.query(Test).get(2)
     if second is None:
         second = Test()
         second.id = 2
         second.zip = "2512"
         second.description = 'Test'
         session.add(second)

     session.commit()

     session = smaker()

     first = session.query(Test).get(1)
     first.zip = 2512
     first.description = 'Test'

     second = session.query(Test).get(2)
     second.zip = 2512
     second.description = 'Test'

     session.commit()

Note that I'm assigning an *integer* value to the `zip` column, equivalent to
the *string* value stored in the database: it seems that SA is "fooled" by
that, and it emits the UPDATE statement (slightly edited log):

     09:38:29,819 INFO sql...Engine SELECT test1.id AS test1_id,
                                           test1.zip AS test1_zip,
                                           test1.description AS 
test1_description
                                    FROM test1
                                    WHERE test1.id = %s
     09:38:29,819 INFO sql...Engine (2,)
     09:38:29,820 INFO sql...Engine UPDATE test1 SET zip=%s WHERE test1.id = %s
     09:38:29,820 INFO sql...Engine ((2512, 1), (2512, 2))

and the database accepts it (MySQL is silly, we all know!) but (I guess)
recognizes that nothing changed and does not return a `rowcount`.
rowcount is still 1 in this case. We set CLIENT_FLAGS against CLIENT_FLAGS.FOUND_ROWS and that value should never be None if an UPDATE/DELETE were emitted in any case. bug is with cymysql.


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