假设此MySQL表格架:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uuid` binary(16) NOT NULL,
`email` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`photo` binary(16) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4;
当我使用SQLalchemy连接类中的execute()
API时:
with self.engine.begin() as connection:
user_uuid = uuid.UUID("...")
result = connection.execute("SELECT email, name, photo FROM user WHERE uuid=%s", user_uuid.bytes)
如果UUID为F393A167-A919-4B50-BBB7-4AD356E89E6B
,则Sqlalchemy会打印此警告:
/site-packages/sqlalchemy/engine/default.py:450:警告:无效的UTF8MB4字符串:'f393a1'
uuid
列是BINARY
列,那么为什么SQLalchemy认为此参数为文本而不是二进制参数,以及如何预防此参数?
实际上在MySQL中的Bug报告中说明和解决方案:
替换:
cursor.execute("" 插入
user
(UUID) 值(%s) ",my_uuid)cursor.execute("" 插入
user
(UUID) 值(_binary%s) ",my_uuid)注意下划线。它是" _binary",而不是"二进制"。 这个" _binary"告诉mySQL,以下字符串应解释为二进制,而不是解释/验证为utf8。
问题在Python 3上没有发生,所以我认为问题是数据库驱动程序无法区分给定给Python 2 str
类型的字节。
无论如何,它似乎使用sqlalchemy core直接工作正常工作,大概是因为它直接知道列类型。
from sqlalchemy import MetaData, Table, select
meta = MetaData()
user = Table('user', meta, autoload_with=engine)
result = select([user]).where(user.c.uuid == user_uuid.bytes)
如果您想继续执行字符串,则可以像sqlalchemy这样的bytesarray施放:
with self.engine.begin() as connection:
user_uuid = uuid.UUID("...")
result = connection.execute(
"SELECT email, name, photo FROM user WHERE uuid=%s",
bytearray(user_uuid.bytes))
或告诉sqlalchemy自动获取它的界参数是什么类型:
from sqlalchemy import text, bindparam, BINARY
with self.engine.begin() as connection:
user_uuid = uuid.UUID("...")
stmt = text("SELECT email, name, photo FROM user WHERE uuid = :uuid")
stmt = stmt.bindparams(bindparam('uuid', user_uuid.bytes, type_=BINARY))
result = connection.execute(stmt)