SQLAlchemy报告“;无效的utf8mb4字符串";对于二进制列

SQLAlchemy报告“;无效的utf8mb4字符串";对于二进制列,sqlalchemy,mysql-python,Sqlalchemy,Mysql Python,假设此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`

假设此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打印此警告:

/站点包/sqlalchemy/engine/default.py:450:警告:无效的utf8mb4字符串:“F393A1”


uuid
列是一个
BINARY
列,那么为什么SQLAlchemy将此参数视为文本参数而不是二进制参数,以及如何防止此情况发生?

解释和解决方案实际上如下所示:

替换:

cursor.execute(“”) 插入到
用户中
(uuid) 值(%s) “”,我的uuuid)

cursor.execute(“”) 插入到
用户中
(uuid) 值(_二进制%s) “”,我的uuuid)

注意下划线。它是“二进制的”,而不是“二进制的”。 这个“_binary”告诉MySQL以下字符串将被解释为二进制,而不是被解释/验证为utf8


这个问题在Python3上没有发生,所以我认为问题在于数据库驱动程序无法区分Python2
str
类型下应该包含哪些字节

不管怎样,似乎直接使用SQLAlchemy核心是正确的,大概是因为它直接知道列类型

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所做的那样:

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)

关于Python2
str
vs
unicode
问题,您很可能是对的。如果您记录SQLAlchemy Core发出的SQL命令,它使用什么?它在Python 2上使用
bytearray
,在Python 3上使用
bytes
。我的意思是,SQLAlchemy Core生成并传递给MySQL驱动程序的实际SQL是什么(通过在引擎上设置
echo=True
可以看到)?@Pol我知道,这就是我使用的。它是
从tbl中选择tbl.uuid,其中tbl.uuid=%(uuid_1)s
,唯一的区别是下一个日志行带有传递的值(
bytes
vs
bytearray
),因此它不会将
\u binary
放入SQL中。我不知道它为什么会起作用。也许MySQL驱动程序会看到
bytearray
类型并自动添加它。。。