These two queries are semantically identical, but one of them succeeds and the other one fails. The only difference is in the WHERE clause, where the two operands of the OR operator have been switched.
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://user:pwd@host:port/db',
# Query succeeds (and does what's expected)
with engine.connect() as cn:
cn.exec_driver_sql(
f'UPDATE table SET column = "value" WHERE id = %s OR id IN %s',
(3, (1, 2))
)
# Query fails
with engine.connect() as cn:
cn.exec_driver_sql(
f'UPDATE table SET column = "value" WHERE id IN %s OR id = %s',
((1, 2), 3)
)
Output of the failed query:
TypeError: 'int' object is not iterable
It seems that sqlalchemy argument parsing is dependant on the type of the first argument. If the first one if a tuple, the query fails, if it is a int/float/str, it succeeds.
The workaround I've found so far is to use named arguments:
# Query succeeds
with engine.connect() as cn:
cn.exec_driver_sql(
f'UPDATE table SET column = "value" WHERE id IN %(arg1)s OR id = %(arg2)s',
{'arg1': (1, 2), 'arg2': 3}
)
However it is more verbose and I don't want to use this everywhere. Also note that PyMySQL cursor's execute method accepted both queries.
Is there a reason for this behaviour?