SQLAlchemy 报告 BINARY 列的 "Invalid utf8mb4 character string"
SQLAlchemy reports "Invalid utf8mb4 character string" for BINARY column
假设此 MySQL table 架构:
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 连接 class 的 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: Warning: Invalid utf8mb4 character string: 'F393A1'
uuid
列是 BINARY
列,那么为什么 SQLAlchemy 将此参数视为文本参数而不是二进制参数?如何防止这种情况?
解释和解决其实就在这个bug report in MySQL:
replace:
cursor.execute("""
INSERT INTO user
(uuid)
VALUES (%s)
""", my_uuid)
with
cursor.execute("""
INSERT INTO user
(uuid)
VALUES (_binary %s)
""", my_uuid)
Mind the underscore. It's "_binary", not "binary".
This "_binary" tells MySQL that the following string is to be interpreted as binary, not to be interpreted/validated as utf8.
问题在 Python 3 上没有发生,所以我认为问题是数据库驱动程序无法区分给定 Python 2 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)
假设此 MySQL table 架构:
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 连接 class 的 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: Warning: Invalid utf8mb4 character string: 'F393A1'
uuid
列是 BINARY
列,那么为什么 SQLAlchemy 将此参数视为文本参数而不是二进制参数?如何防止这种情况?
解释和解决其实就在这个bug report in MySQL:
replace:
cursor.execute(""" INSERT INTO
user
(uuid) VALUES (%s) """, my_uuid)with
cursor.execute(""" INSERT INTO
user
(uuid) VALUES (_binary %s) """, my_uuid)Mind the underscore. It's "_binary", not "binary". This "_binary" tells MySQL that the following string is to be interpreted as binary, not to be interpreted/validated as utf8.
问题在 Python 3 上没有发生,所以我认为问题是数据库驱动程序无法区分给定 Python 2 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)