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.