【问题标题】:Python MySQL connector returns bytearray instead of string for prepared statementsPython MySQL 连接器为准备好的语句返回字节数组而不是字符串
【发布时间】:2021-01-05 13:02:05
【问题描述】:

给定这样的 MySQL 表:

create table demo (id int, name varchar(100)) collate utf8mb4_general_ci;
insert into demo values (1,'abcdef');

还有 Python 脚本:

import mysql.connector
db = mysql.connector.connect(host='xx', user='xx', password='xx', database='xx')
cursor = db.cursor()
cursor.execute('select * from demo')
for row in cursor:
    print(row)

这会产生预期的结果:

(1, 'abcdef')

如果我将光标更改为准备好的光标:

cursor = db.cursor(prepared=True)

结果出乎意料:

(1, bytearray(b'abcdef'))

我正在使用 Python 3.8.0 和 mysql.connecter 版本 2.2.9 在 MySQL 连接器 2.1.8 (https://dev.mysql.com/doc/relnotes/connector-python/en/news-2-1-8.html) 的发行说明中,我阅读了

使用准备好的语句时,字符串列作为字节数组而不是字符串返回。返回的值现在是使用连接的字符集(默认为 'utf8')解码的字符串,或者如果此转换失败,则作为字节数组。 (错误号 27364914)

所以我没想到我正在使用的版本中的行为。

我错过了什么?

【问题讨论】:

  • 这似乎是一个错误。 FWIW 该行为在最新版本(8.0.22)中不可重现。注意这个版本必须安装pip install mysql-connector-pythondev.mysql.com/doc/connector-python/en/…
  • 感谢@snakecharmerb 的这个指针,这解决了我的问题,作为奖励,现在性能要好得多。但是就像下面的 Booboo 注意到的,使用准备好的语句的性能似乎比没有准备好的要差。令人费解。

标签: python mysql


【解决方案1】:

文字应该是这样的:

返回值现在是使用连接的字符集(默认为 'utf8')编码的字符串,或者如果转换失败,则作为字节数组。 (错误号 27364914)。

>>> 'abcdef'.encode('utf8') == b'abcdef'
True
>>>

因此,当使用cursor = db.cursor(prepared=True) 时,驱动程序正在执行文档中所说的操作(如果编码失败,那么它只会返回一个bytearray,否则会期望一个字节字符串 -- this是正在描述的变化d)。但是我认为没有理由指定prepared=True,因为您可以在没有它的情况下使用准备好的语句并获得预期的结果,如果您只使用准备好的语句作为避免 SQL 注入攻击的机制并且不使用它进行重复执行。

更新

我做了一个小的基准测试,使用和不使用 prepared=True 检索 10,882 行:

import mysql.connector

def foo(db):
    cursor = db.cursor(prepared=True)
    for i in range(10883):
        cursor.execute('select Company from company where PK_Company = %s', (i,))
        rows = cursor.fetchall()
    print(rows[0][0]) # print last fetched row

db = mysql.connector.connect(user='xx', password='xx', database='xx')
foo(db)

结果:

With `prepared=True`: 2.0 seconds for function `foo`
Without `prepeared=True`: 1.6 seconds for function `foo`
Using pymysql: 1.5 seconds for function `foo`

prepared=True 似乎运行得更慢。 ????

【讨论】:

  • 如何在没有prepared=True的情况下使用prepared statements?据我了解,如果没有该 sql 语句将在每次执行中得到解释。因此,如果我用不同的参数调用同一个 sql 语句 100 万次,它的效率远远不如使用真正的准备好的语句。
  • 我使用pymysql 进行MySql 访问,并且使用该驱动程序,我认为您从准备好的语句中获得的唯一好处是避免sql 注入和数据转换,但mysql.connector 使用@ 可能并非如此987654331@。希望至少我解释了为什么你会得到你得到的结果。我会更新答案并修改最后一句。
猜你喜欢
  • 2017-04-16
  • 2016-11-12
  • 2019-03-25
  • 2015-08-15
  • 2022-06-15
  • 1970-01-01
  • 2017-01-19
  • 2021-08-02
  • 2019-07-22
相关资源
最近更新 更多