我正在尝试使用UnixODBC和pyodbc 3.07从Ubuntu 16客户端连接到旧的MySQL 3.23服务器。我尝试了三个(3)版本的MySQL Connector / ODBC和两个(2)来自MariaDB:
MySQL-ODBC 5.3.9仅支持新的mysql身份验证方法。因此无法连接。
MySQL-ODBC 5.1.13有一个验证方法的开关,但告诉我pyodbc.connect(dsn)
:[MySQL][ODBC 5.1 Driver]Driver does not support server versions under 4.1.1
MySQL-ODBC 3.51有两个问题:
[MySQL][ODBC 3.51 Driver]Transactions are not enabled (4000) (SQLSetConnnectAttr(SQL_ATTR_AUTOCOMMIT))
失败,因为pyodbc将autocommit设置为false作为默认值。pyodbc.connect(dsn, autocommit=True)
联系时,给我一个联系。连接给了我一个游标,但所有cursor.execute(sql)抛出异常('HY000', 'The driver did not supply an error!')
。通过isql -v [dsn]
从shell测试与shell的连接给了我一个会话但是在使用[ISQL]ERROR: Could not SQLExecute
的所有语句上都失败了。所以这似乎是一个unixodbc问题。
我安装了mysql-client。但程序mysql无法连接服务器。
mariadb-client可以连接到数据库甚至执行语句。这看起来更有希望。
我下载了MariaDB ODBC-Driver 3.0.2。使用该驱动程序与isql返回错误:[S1000][unixODBC][ma-3.0.2]Plugin old_password could not be loaded: lib/mariadb/plugin/old_password.so: cannot open shared object file: No such file or directory
。这是一个可以使用的响应。有一个ODBC-Option PLUGIN_DIR,但我不知道从哪里获取插件。
MariaDB ODBC-Driver 2.0.13在连接时给了我('HY000', "[HY000] [unixODBC][ma-2.0.13]You have an error in your SQL syntax near 'SQL_AUTO_IS_NULL=0' at line 1 (1064) (SQLDriverConnect)")
。因为似乎没有办法改变这一点。在这里干。
我想知道是否有办法通过unixodbc / pyodbc访问这个旧的MySql?
或者有人知道哪里可以获得MariaDB的插件old_password.so?
通过apt-get安装的mariadb-client可以连接,所以必须有一种方法。
我花了一天左右的时间来讨论这个问题,并且如果没有对驱动程序代码进行重大更改,或者对于旧版本来说非常难以创建的构建环境,我认为这是不可能的。
我把它放在一个答案中,以便其他人不会掉下我所做的那个兔子洞(或者,更好的是,所以其他人可以在我离开的地方找到并实际解决问题!)......然后不符合评论。
抱歉,这将是一个很大的内容。
我能够使用一对Ubuntu 16.04容器,Oracle提供的MySQL 3.23 download以及您提到的所有客户端库来重现您在帖子中提到的每个错误条件(感谢一个彻底而优秀的问题!)很少人。
以下是我在尝试在您提到的每个地方找到其他解决方案时发现的内容,然后是一些“后续步骤” - 类型信息以及一些关于故事道德的改编。
所有这些测试均使用最新版本的Python 2,UnixODBC和pyodbc
(通过pip
)进行,可用于库存Ubuntu 16.04 Docker容器,截止日期为2017年11月26日。
所有使用的URL都是链接的,但是,如果历史记录是任何迹象,它们可能会随着时间的推移而死亡,考虑到很多这个软件已经开始使用了二十年。如果您愿意,我也很乐意发布我的任何/所有shellcripts / Dockerfiles /修改过的驱动程序源代码;只是在评论中ping我。
你是对的,这是最具潜力的故障排除选项。这是我做的:
首先,我安装了Connector/ODBC 3.0.2二进制文件并尝试通过Python连接到它。在为名为“maria”的数据源配置ODBC .ini
文件后,我遇到了同样的错误,即:
> pyodbc.connect('DRIVER={maria};Server=mysql;Database=mysql;User=admin;Password=admin')
pyodbc.Error: ('HY000', u'[HY000] [unixODBC][ma-3.0.2]Plugin old_password could not be loaded: lib/mariadb/plugin/old_password.so: cannot open shared object file: No such file or directory (2059) (SQLDriverConnect)')
当ODBC服务器宣布一个足够老的认证协议时,它会尝试加载为Connector/C MariaDB driver构建的编译插件。 strace
ing ODBC连接尝试的输出确定了这一点。
old_password.so
原来是Connector / C MariaDB驱动程序的一个组件,但它不是该驱动程序的二进制版本中包含的库。有趣。
事实证明,有一些类似于old_password
的插件模块包含在Connector / C驱动程序的源代码中。我下载了Connector/C 3.0.2 sources并打开了那些“auth”类型插件的文档,源代码和构建系统,这些插件作为.so
文件分发,以查看我能找到的内容。
我发现Connector / C的各种组件可以编译为“静态”链接到主驱动程序库的插件,或者作为动态库本身。我在引号中说“静态”,因为C驱动程序的构建过程同时创建了.a
的静态(.so
)和动态(mariadbclient
)版本,但如果在构建系统中将特定插件声明为静态,则该插件的代码为静态地包含在两个mariadbclient
文物中。
old_password.so
文件的来源似乎是在plugins/auth/old_password.c
的一个小型源文件中。
似乎可以更改构建系统(CMake)以生成old_password
插件的动态库。在Connector / C源代码中有一个cmake/plugins.cmake
文件,它充当所有插件的“注册表”。它包含一个cmake宏REGISTER_PLUGIN
,它采用STATIC
或DYNAMIC
参数。我在该文件中搜索old_password
并找到以下行:
REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CC_SOURCE_DIR}/plugins/auth/old_password.c" "old_password_client_plugin" "STATIC" "" 0)
看起来很有希望。使用为其插件生成.so
文件的类似行进行建模,我将该行更改为以下内容并运行构建:
REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CC_SOURCE_DIR}/plugins/auth/old_password.c" "old_password_client_plugin" "DYNAMIC" "old_password" 1)
由于缺少依赖性,构建失败了几次。我不得不安装一些-dev
软件包和其他工具,但最后我能够干净地构建(只是插件,事实证明你不需要CURL或OpenSSL)。果然,在mysql_old_password.so
目录中创建了一个名为plugins/auth
的文件作为构建工件。 - 现在,我需要我的Python代码才能找到该插件;它仍然给了我关于未能找到lib/mariadb/plugin/old_password.so
的错误。我将您在问题中提到的PLUGIN_DIR
参数提供给ODBC连接字符串,将我编译的mysql_old_password.so
重命名为old_password.so
,并运行以下代码。 。 。并得到一个新的错误!进展!
conn = pyodbc.connect('DRIVER={maria};Server=mysql;Database=mysql;User=admin;Password=admin;PLUGIN_DIR=/home/mysql/zclient/mdb-c/plugins/auth')
pyodbc.Error: ('HY000', u'[HY000] [unixODBC][ma-3.0.2]Plugin old_password could not be loaded: /home/mysql/zclient/mdb-c/plugins/auth/old_password.so: undefined symbol: ma_scramble_323 (2059) (SQLDriverConnect)')
看起来编译的工件被破坏,缺少ma_scramble_323
函数定义。由于插件是在运行时动态加载的,程序仍然会启动,但是当它尝试dload
插件时它会爆炸。更糟糕的是,该功能看起来像是“旧的”MySQL协议认证机制的主要密码哈希入口点,所以我不能放弃它。在Connector / C源代码中,我找到了该函数的声明和标题(mariadb_com.h
),但include
ing在old_password.c
源文件中的各个地方似乎没有做到这一点。我的预感是,这是两种不幸行为的相互作用。首先,设置Connector / C构建系统编译的插件,假设它们只能通过Connector / C插件或类似的东西进行链接。这意味着插件本身在编译时不会链接到“通用”Connector / C功能,因为加载插件的东西应该已经可用了。由于我们使用的是Connector / ODBC,而不是Connector / C,因此这些常用功能不存在或无法访问。现在,从源代码构建Connector / ODBC需要Connector / C,因此Iit可能以一种包含正确函数的方式编译新的Connector / ODBC库,但我不想开始那个兔子洞。其次,即使被告知要独立构建old_password
插件(不编译其他任何东西)模式,CMake的依赖性分析也没有发现或链接描述ma_scramble_323
的文件。这可能是一个CMake问题,但可能是因为如上所述,构建系统没有配置这个用例。
在这里,我很幸运。 ma_scramble_323
函数在libmariadb/ma_password.c
中定义,old_password
是一个非常小的简单源文件,对于连接器/ C项目中尚未依赖ma_scramble_323
插件的任何其他库没有重大依赖性。我做了“穷人的链接”(哎呀),只是将old_password.c
函数的来源复制到ma_password.c
文件中。那些函数在ma_password.c
文件中调用了其他函数,所以我将它们复制到了。再次,这只是简单(或根本没有选择),因为mysqladmin flush-hosts
文件非常简单。如果它本身有依赖关系或者更复杂,我将不得不停下来,放弃并学习高级CMake-fu以“正确”方式解决问题。我绝对相信有更好的方法可以做到这一点。
(旁白)此时我不得不在我的数据库服务器上定期运行mysql_old_password.so
,因为我的测试导致了很多失败的尝试,我不得不经常这样做。也许有更好的解决方法,但我不知道,我知道cron。
使用新的“内联”源,编译了pyodbc.Error: ('HY000', u'[HY000] [unixODBC][ma-3.0.2]Plugin old_password could not be loaded: name mismatch (2059) (SQLDriverConnect)')
库,我将其重命名,并再次运行我的测试脚本。这一次,我得到了:
old_password.so
我认为这与我重命名文件的事实有关,所以ODBC可以找到它(它正在寻找mysql_old_password.so
而不是plugins/auth/CMakeLists.txt
)。我尝试了霰弹枪的方法。在mysql_old_password
构建系统配置中,我用old_password
替换了所有old_password.c
实例并编译。编译成功,但仍然无法正常工作。
事实证明,插件源本身(在这种情况下为mysql_old_password
)在顶部有一个结构声明,宣布了它们的名字,而且这个声明的名字是s/mysql_old_password/old_password/
。这可能是一个预先存在的问题(也就是说这从来没有用过),我开始觉得有点冷淡:当你构建的代码感觉就像没有人构建它或者在给定的配置中测试它之前,你的成功几率并不好。无论如何,我也在源文件上做了相同的old_password.so
,并进行了编译。这次它生成了一个具有正确的conn = pyodbc.connect('DRIVER={maria};Server=mysql;Database=mysql;User=admin;Password=admin;PLUGIN_DIR=/home/mysql/zclient/mdb-c/plugins/auth')
pyodbc.Error: ('HY000', u"[HY000] [unixODBC][ma-3.0.2]Access denied for user: 'admin@hostname' (Using password: NO) (1045) (SQLDriverConnect)")
名称的工件。我再次运行我的测试脚本并得到:
mysql
这很奇怪。我的客户端测试盒上安装了3.23服务器(通过tarball,而不是系统库路径)附带的isql
命令行客户端,它可以很好地连接这些凭据(我无法使用PLUGIN_DIR
进行测试,因为我无法正确使用/usr
并且无法弄清楚它希望我放置插件的位置;它不在系统GRANT
目录中,也不是相关的目录)。我无法想出办法解决这个问题。对于localhost
和%
,admin
和mysql
,pyodbc.Error: ('HY000', u'[HY000] [unixODBC][ma-3.0.2]Error in server handshake (2012) (SQLDriverConnect)')
用户和同名密码,我已经为所有常用的“超混杂,仅测试”this GitHub issue设置了我的MySQL服务器。
我放弃并设置密码为空/ null,禁用密码验证,确保我仍然可以通过命令行上的old_password.so
登录,并最后一次尝试:
SQL_AUTO_IS_NULL
事实证明这是丧钟。研究这个错误,我找到了This bug thread,其中人们似乎相信这代表了基本的客户端/服务器协议不兼容。在这一点上,我放弃了ODBC Connector parameters list方法。似乎3.0%版本的MariaDB驱动程序代码(C或ODBC)并没有说出MySQL的协议可以使用的方法,尽管在这个过程中可能有很多可能的修复。
我尝试了你在问题中提到的其他一些事情,我将简要介绍一下:
Option=8388608
行为效果不佳。 .ini
和MySQL archive site有关于如何禁用该字段设置的一些建议(hand-install system identification files from 1999显而易见,对吧?),但这些强制禁用或启用标志的尝试都没有改变行为,无论它们是否在连接字符串或ODBC third party MySQL ODBC connectors文件。libmysqlclient.so.10
具有旧版本的ODBC连接器。不幸的是,他们所有的编译版本都是针对32位Linux的,我没有。我尝试从源代码构建,即使配置工具链也是一项繁重的工作。在我不得不errno
的地方,我知道这可能是一个失败的原因,但我安装了所有的deps和古代版本并尝试编译它。编译错误的数量和种类使我放弃了这种方法(C标准不匹配,加上缺乏与UnixODBC几乎每个部分的兼容性)。完全有可能对我错过的这些问题进行简单的修复;我不是C编码器或旧Linux构建系统专家。#define
;与5. *系列相同的错误。extern int errno
文件。这需要更改3.23服务器的源代码来解决一些与my_sys.h
相关的问题(删除/usr/share/libtool/build-aux/config.{guess,sub}
中.
的mit-pthreads
子句),将libtool操作系统定义文件复制到源目录中的各个位置(mit-pthreads/config/
被复制到libmysqlclient
,--with-mit-threads --without-server --without-docs --without-bench
,和mysql
,如果重要的话)。之后,我能够使用.so
配置开关编译和构建libmysqlclient
库。在此之后评估libmysqlclient.so.10
客户端程序的宏时,编译失败了几个不可思议的错误,但2.50.39 version of Connector/ODBC from the archive的asm/atomic.h
文件已经生成,所以我抓住它们继续前进。在iodbc
库编译之后,我建立了libiodbc2-dev
。这需要更改主要MySQL包含文件(删除对/usr/include
的引用)的一些来源,以及与其他库相同的系统标识libtool hack。它未能找到/usr/local/include
库(通过--with-mysql-includes=$path_to_3.23_mysql_binary_dir/include --with-mysql-libs=$path_to_compiled_libmysqlclient.so.10_files_from_mysql_server_3.23_sources --with-iodbc-includes=/usr/include/iodbc
包安装在Ubuntu上),因为它们现在位于atomic.h
而不是libmyodbc.so
。我最后用开关gdb
配置它,除了前面提到的shimming问题之外它没有问题。然而,毕竟,通过我新编译的i386
连接导致Python / UnixODBC中的段错误。 Valgrind,myodbc
和其他工具在确定原因方面没有用;也许更善于调试编译库互操作性问题的人可以解决这个问题。dlopen
架构和所需的库来尝试dlopen
这些文件。 64位Python / UnixODBC无法成功加载i386
插件,返回通用的“找不到文件”错误,我最终追溯到对x86-64
的失败调用。 Libtool的pyodbc
包装器(由UnixODBC使用)被大多数人认为不是非常可调试的,并且经过一次重大的麻烦后,我的基本Valgrind技巧似乎表明,正如我所料,动态加载架构是不可能的(不兼容) pyodbc
vs pyodbc
)ODBC后端。一般来说,重写代码可能会更容易。例如,您可以创建一个包含遗留Python非ODBC MySQL驱动程序的Python模块(如问题评论中建议的@FlipperPA),将pyodbc
界面的“足够”修复到您不需要的模块上重构太多调用它的代码,并在部署之前彻底测试。我知道这很糟糕并且风险很大,但这可能是你最好的选择。在编写这样的模块时,您可以使用ln -s /usr/bin/mariadb_config mysql_config
pip install MySQL-python
中的一些内部代码来处理通用的ODBC语法/等等。
您甚至可以为qazxswpoi开发一个“假的”ODBC后端,它只是称为非ODBC Python MySQL驱动程序,但我怀疑这很难,因为qazxswpoi的后端可插拔性似乎主要面向编译库而不是“虚拟”填充码。
我不是这方面的专家,所以完全可能有我错过的解决方案!
我考虑并放弃了一些其他可能性:
您可以向MariaDB人员提交错误,但可能会修复。我不太清楚我最终遇到的协议错误是“这在每个级别都是根本不兼容的”或“auth系统只需要调整然后一切都会工作”。这可能值得一试。
由于版本为2.50 Connector / ODBC代码的32位RPM可用(它们不会加载到64位Python / UnixODBC环境中),因此您可以将整个堆栈(甚至操作系统分发)转换为32-位代码。但是,如果您使用任何非常见的编译内容,这可能是一个重大的麻烦。虽然Ubuntu / Debian特别擅长在旧架构上提供软件包,但这仍然可能很棘手。即使你已经完成所有转换,一些行为也可能会发生变化,而旧的32位特性对于在您的应用上工作的任何人来说都是一种持续的陌生感。而且只有当2.50驱动程序从32位运行时访问时才能工作;之后可能会出现其他问题。如果将来所有客户端代码的维护负担可能非常低(如果项目很小或不太可能改变),我建议尝试这样做。
软件快速腐烂。除非项目不断致力于维护向后兼容性,否则事情将很快停止,尤其是在Web软件中。
这并不是说产品本身就会破裂,而是宇宙在数百万个方面从它下面变化。除非有人足够通才并且已经足够长时间知道所有这些微小变化以及如何扭转这些变化,否则很难将所有事情及时调整到一个“正常工作”的地方。
如果你得到某些东西的二进制文件,即使它像MySQL驱动程序一样被称为“常见”,也要保留它们。理想情况下,与互联网分享。
如果您有某些内容的来源,请严格记录他们需要的整个依赖项/工具链列表,并为人类记录。假设读取程序化依赖性列表所需的工具,例如autotools本身就会过时。没有什么东西太“明显”无法记录;不是架构,内核ABI,libc行为 - 没什么。现在我们已经“在任何内核的盒子里”这样的东西,比如Docker,你可能能够以编程方式存储更多的依赖项,但不要指望它。
正如Zac B所示,没有真正的方法可以将来自Ubuntu 16客户端的MySQL 3.23服务器与UnixODBC和pyodbc 3.07连接起来。
FlipperPA建议使用pypi.python.org/pypi/MySQL-python/1.2.4作为下一个解决方案。所以我试了一下:
通过apt安装的MySQL-python无法连接到那个旧的MySQL。它使用的系统mysql-client libs不能与MySQL 3.23一起使用。
通过pip安装的MySQL-python是另一回事:它从libmysqlclient-dev包中包含的mysql_config获取libs。也不适用于MySQL 3.23。但是有机会在这里修改一些东西。
当我安装libmariadb-client-lgpl-dev时,我得到一个看起来很像mysql_config的mariadb_config。 Ubuntu 16的mariadb-client使用MySQL 3.23(如上所示)。
qazxswpoi
这样做了。我可以从Python连接那个旧的MySQL服务器。
数据类型存在一些问题,但在这种情况下我并不挑剔。