面向iSeries的pyODBC + unixodbc + Db2 = UnicodeDecodeError,非法的UTF-16替代

时间:2019-07-02 19:28:09

标签: python sqlalchemy db2 ibm-midrange pyodbc

我能够在docker容器中成功使用pyODBC和SQLAlchemy连接到DB2 for iSeries(版本7.2)。它可以工作,但我会间歇地运行查询并获取以下回溯:

>>> Groups.query.get(group_id)

Traceback (most recent call last):
...
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 154, in reraise
    raise value
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1244, in _execute_context
    cursor, statement, parameters, context
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 550, in do_execute
    cursor.execute(statement, parameters)
UnicodeDecodeError: 'utf-16-le' codec can't decode bytes in position 64-65: illegal UTF-16 surrogate

有时,这会连续发生多次,然后突然停止。并非总是在同一组查询中发生这种情况,我尝试了两个几乎完全相同的DB2服务器并获得了相同的结果。

同一查询的“位置64-65”始终相同(即使有时查询返回正确的结果)。

版本:

  • pyodbc:4.0.26
  • unixodbc:2.3.4-1(Debian版)
  • SQLAlchemy:1.3.5
  • iAccess驱动程序:ibm-iaccess-1.1.0.11-1.0

1 个答案:

答案 0 :(得分:1)

最后找到了。

在堆栈的某些地方,列名别名最多可以包含30个字符。我的猜测是pyodbc(db2支持128个长度的列名和别名),我已经raised an issue on GitHub来跟踪此问题。

当超过30个字符的限制时,pyodbc仍尝试解码具有列名原始长度的字符串,因此它尝试解码垃圾数据,有时会导致UnicodeDecodeError(以及所有其他时间返回垃圾数据)

这是特定于列名的(所以cursor.keys()将显示垃圾列名)。

我的解决方法是强制SQLAlchemy使用自定义方言截断列别名。

customdb2.py:

from ibm_db_sa.pyodbc import AS400Dialect_pyodbc

class CustomAS400Dialect(AS400Dialect_pyodbc):
    max_identifier_length = 30
registry.register('db2.pyodbc400_custom', 'customdb2', 'CustomAS400Dialect')