SQLAlchemy报告BINARY列的“无效的utf8mb4字符串”

时间:2016-06-18 17:42:24

标签: 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` (`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将此参数视为文本而不是二进制,以及如何防止这种情况?

2 个答案:

答案 0 :(得分:2)

解释和解决方案实际上在bug report in MySQL

  

取代:

     

cursor.execute("""       INSERT INTO user(uuid)       价值(%s)   """,my_uuid)

     

     

cursor.execute("""       INSERT INTO user(uuid)       VALUES(_binary%s)   """,my_uuid)

     

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

答案 1 :(得分:1)

问题在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那样强制转换为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)