Соедините MySQL 3.23 с pyodbc 3.07

Я пытаюсь подключиться к старому серверу MySQL 3.23 от клиента Ubuntu 16 с помощью UnixODBC и pyodbc 3.07. Я пробовал три (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 из оболочки через isql -v [dsn] дает мне сеанс, но не выполняется при всех операторах с [ISQL]ERROR: Could not SQLExecute. Так что это проблема unixodbc.

Я установил mysql-client. Но программа mysql не может подключить сервер.

mariadb-client может подключаться к базе данных и даже выполнять инструкции. Это выглядит более перспективным.

Я загрузил файл 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)") при подключении. Поскольку, похоже, нет возможности изменить это. Отложено здесь.

Я хотел бы знать, есть ли способ получить доступ к этому старому MySql через unixodbc/pyodbc?

Или кто-нибудь знает, где взять плагин old_password.so для MariaDB?

Mariadb-клиент, установленный через apt-get, может подключаться, поэтому должен быть способ.

Ответ 1

Я потратил целый день на то, чтобы подумать об этом, и не думаю, что это возможно без существенных изменений кода драйвера или чрезвычайно сложной для сборки среды сборки для старых версий.

Я помещаю это в ответ, чтобы другие люди не падали с той же самой кроличьей норой, что и я (или, еще лучше, чтобы другие люди могли забрать, где я остановился, и на самом деле решить проблему!).. и он не вписывался в комментарий.

Это будет немного тома, извините.

Обзор

Я смог воспроизвести каждый из условий ошибки, упомянутых в вашем сообщении (спасибо за тщательный и отличный вопрос!), используя пару контейнеров Ubuntu 16.04, MySQL 3.23 скачать, доступный от Oracle, и все клиентские библиотеки, которые вы упомянули, и несколько других.

Ниже вы найдете то, что я нашел, пытаясь найти дополнительные решения в каждом упоминаемом вами месте, а затем некоторые "следующие шаги" - информация типа и некоторые прозелитизирующие о нравственности рассказа.

Все эти тесты проводились с последними версиями Python 2, UnixODBC и pyodbc (через pip), доступными для контейнера Ubuntu 16.04 Docker по состоянию на 26/11/2017.

Все используемые URL-адреса связаны, но если история - это какой-либо признак, они могут умереть с течением времени, учитывая, что многие из этих программ граничат с двумя десятилетиями. Я также рад опубликовать любые/все мои файлы shellscripts/Dockerfiles/измененные источники драйверов, если вам нравится; просто пингуйте меня в комментариях.

old_password.so и соединитель MariaDB/ODBC 3.0.2

Вы были правы, что это был вариант устранения неполадок с наибольшим потенциалом. Вот что я сделал:

Сначала я установил двоичный файл Connector/ODBC 3.0.2 и попытался подключиться к нему через Python. Я сделал ту же ошибку, что и после настройки моих файлов ODBC .ini для источника данных с именем "maria", а именно:

> 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 пытается при представлении сервером MySQL, который достаточно давно сообщает о протоколе аутентификации, для загрузки скомпилированных плагинов, построенных для Connector/C MariaDB driver. strace для вывода попыток подключения ODBC это определено.

old_password.so оказывается компонентом драйвера Connector/C MariaDB, но не является библиотекой, включенной в эти двоичные версии драйверов. Интересно.

Оказывается, есть куча модулей плагина, похожих на old_password, включенных в источник драйвера Connector/C. Я загрузил Источники Connector/C 3.0.2 и открыл документацию, источники и систему сборки для этих плагинов типа "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, которая является очень маленьким, простым исходным файлом без существенных зависимостей от каких-либо других библиотек проекта Connector/C, которые еще не зависели от плагина old_password. Я сделал "плохое соединение человека" (yuck) и просто скопировал источники функции ma_scramble_323 в файл old_password.c. Эти функции называли другие функции в файле ma_password.c, поэтому я скопировал их. Опять же, это было просто (или вариант вообще), поскольку файл ma_password.c был настолько прост. Если бы он сам имел зависимости или был более сложным, мне пришлось бы остановиться, бросить и изучить продвинутый CMake-fu, чтобы решить проблему "правильным" способом. Я абсолютно уверен, что есть лучший способ сделать это.

(Помимо этого), я должен был запустить регулярный прогон mysqladmin flush-hosts на моем сервере БД, так как мое тестирование вызывало так много неудачных попыток, что мне приходилось делать это часто. Вероятно, есть и лучший способ обойти это, но я этого не знаю, и я знаю cron.

С новыми "встроенными" источниками библиотека mysql_old_password.so, скомпилированная, переименовала ее и снова провела мой тест script. На этот раз я получил:

pyodbc.Error: ('HY000', u'[HY000] [unixODBC][ma-3.0.2]Plugin old_password could not be loaded: name mismatch (2059) (SQLDriverConnect)')

Я полагал, что это связано с тем, что я переименовал файл, чтобы ODBC мог его найти (он ищет old_password.so not 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. Я снова проверил свой тест script и получил:

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: '[email protected]' (Using password: NO) (1045) (SQLDriverConnect)")

Это было странно. У меня был клиент командной строки mysql, который поставлялся с установленным сервером 3.23 (через tarball, а не в пути к системной библиотеке) в моем ящике для тестирования клиентов, и он мог нормально связываться с этими учетными данными (я не мог проверить с помощью isql, потому что я не мог заставить его правильно использовать PLUGIN_DIR и не мог понять, где он хотел, чтобы я поместил плагины: он не был в каталоге /usr, а не в относительных). Я не мог понять, как это сделать. Я установил свой MySQL-сервер со всеми обычными "ультра-неразборчивыми, тестируемыми только" GRANT s, для localhost и % для каждой базы данных для пользователя admin и одноименного пароля.

Я отказался и установил пароль для пустого/нулевого значения, отключив пароль auth, убедившись, что я все еще могу войти в систему через mysql в командной строке и пробовать в последний раз:

pyodbc.Error: ('HY000', u'[HY000] [unixODBC][ma-3.0.2]Error in server handshake (2012) (SQLDriverConnect)')

Это оказалось смертельным звоном. Исследуя эту ошибку, я нашел эту проблему GitHub, в которой люди, казалось, были убеждены, что это представляет собой фундаментальную несовместимость между протоколом клиент/сервер. В этот момент я отказался от подхода old_password.so. Кажется, версия 3.0.2 кода драйвера MariaDB (C или ODBC) не говорит о достаточно большем диалекте протокола MySQL для работы, хотя, вероятно, есть много возможных исправлений, которые я пропустил в этом процессе.

Другие пути попробовали

Я попробовал несколько других вещей, которые вы упомянули в своем вопросе, которые я кратко рассмотрю здесь:

  • Как вы, вероятно, обнаружили, попытка отключить поведение SQL_AUTO_IS_NULL в семействе драйверов ODBC от MariaDB 2.0 не работает. Этот поток ошибок и Список параметров соединителя ODBC несколько предложений о том, как отключить настройку этого поля (Option=8388608 очевидна и понятна, правда?), но ни одна из этих попыток принудительно отключить или включить флаг не изменила поведение, независимо от того, были ли они в строке соединения или файлы ODBC .ini.

  • сайт архивации MySQL имеет старые версии соединителя ODBC. К сожалению, все их скомпилированные версии предназначены для 32-разрядных Linux, которых у меня нет. Я попытался построить исходный код, и это было серьезной задачей даже для настройки конфигурации инструментальной цепочки. В тот момент, когда я должен был файлы с идентификацией системы вручную с 1999 года Я знал, что это, вероятно, потерянное дело, но я получил все отпечатки и установлены древние версии и пытались их скомпилировать. Исключительное количество и разнообразие ошибок компиляции заставило меня отказаться от этого подхода (стандартные несоответствия C, а также отсутствие совместимости с тем, что, казалось, было почти любой частью UnixODBC). Вполне возможно, что есть простые исправления этих проблем, которые я пропустил; Я не эксперт по C-коду или старому-linux-build-system.

  • Я пробовал несколько сторонних соединителей MySQL ODBC, которые не работали; те же ошибки, что и в семействе 5. *.

  • Я скомпилировал версию библиотеки Connector/ODBC версии 2.50.39 (только источники были доступны в архиве). Для этого я сначала скомпилировал файлы libmysqlclient.so.10 для версии 3.23 сервера. Это потребовало изменения источников сервера 3.23 для решения некоторых связанных с errno проблем (удалите предложение #define для extern int errno в my_sys.h), скопировав файлы определения ОС libtool в разные места в исходном каталоге (/usr/share/libtool/build-aux/config.{guess,sub} скопированы в ., mit-pthreads и mit-pthreads/config/, если это имеет значение). После этого я смог скомпилировать и построить библиотеки libmysqlclient с помощью переключателей --with-mit-threads --without-server --without-docs --without-bench configure. Ошибка компиляции с несколькими непостижимыми ошибками при оценке макросов для клиентской программы mysql после этого, но файлы .so для libmysqlclient уже были сгенерированы, поэтому я схватил их и перешел. После компиляции библиотеки libmysqlclient.so.10 я создал 2.50.39 версию Connector/ODBC из архива. Это потребовало изменения некоторых источников из основных файлов MySQL include (удаление ссылок на asm/atomic.h) и такой же системный идентификатор libtool, как и другие библиотеки. Он не смог найти iodbc libs (установленный на Ubuntu через пакет libiodbc2-dev), так как теперь он находится в /usr/include, а не /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, и он был создан без проблем, кроме вышеупомянутой проблемы atomic.h. Однако после этого подключение через мой недавно скомпилированный libmyodbc.so вызвало segfault в Python/UnixODBC. Valgrind, gdb и другие инструменты не были полезны при определении причины; возможно, кто-то, кто лучше разбирается в отладке проблем с компиляцией библиотеки, может решить эту проблему.

  • В архиве MySQL есть старые, двоичные версии RPM для Connector/ODBC. Все они 32-битные, и почти все современные Linux - это 64-разрядные. Я попробовал shimmingэти файлы, установив архитектуру i386 и необходимые библиотеки. 64-разрядный Python/UnixODBC не смог успешно загрузить плагины myodbc, возвращая общую ошибку "файл не найден", в результате чего я, в конце концов, вернулся к неудачному вызову dlopen. Обертки Libtool dlopen (используемые UnixODBC) считаются не очень отлаживаемыми большинством людей, и после значительных хлопот мои рудиментарные трюки Valgrind, как я и ожидал, показали, что невозможно динамически загружать архитектуру, несовместимый (i386 vs x86-64) ODBC-сервер.

Решения/Оставшиеся опции

В целом, вероятно, будет легче переписать код на вашем конце. Например, вы можете сделать модуль Python, который обертывает устаревший драйвер Python без ODBC MySQL (как @FlipperPA, предложенный в комментариях к вопросу), взломать "достаточно" интерфейса pyodbc на этот модуль, который вы не используете придется реорганизовать слишком много кода, который его вызывает, и тщательно протестировать перед развертыванием. Я знаю, что это отстой и рискованно, но, вероятно, это ваш лучший выбор. При написании такого модуля вы можете использовать некоторый внутренний код в pyodbc, который обрабатывает общий синтаксис ODBC/et cetera.

Возможно, вы даже разработали "поддельный" ODBC-сервер для pyodbc, который только что назвал драйвер ODBC Python MySQL, но я подозреваю, что это будет сложно, так как pyodbc backend-pluggability кажется в основном ориентированным на скомпилированные библиотеки чем "dummy" код прокладки.

Я не эксперт в этом, поэтому вполне могут быть решения, которые я пропустил!

Есть несколько других возможностей, которые я рассмотрел и отказался от:

Вы можете указать ошибку с людьми MariaDB, и это может быть исправлено. У меня нет четкого представления о том, была ли ошибка протокола, в которой я закончилась, "это принципиально несовместимо на каждом уровне" или "система auth просто нуждается в настройке, тогда все будет работать". Это может стоить того.

Поскольку существуют 32-разрядные RPM-версии кода версии 2.50 Connector/ODBC (они не будут загружаться в 64-разрядную среду Python/UnixODBC), вы могли бы преобразовать весь ваш стек (или даже распределение операционной системы) до 32-битного кода. Однако, если вы используете какие-либо несовместимые скомпилированные материалы, это, вероятно, будет серьезной проблемой. В то время как Ubuntu/Debian особенно хорошо разбирается в том, что пакеты доступны на старых архитектурах, это все еще может быть сложно. Даже если вы все преобразуете, некоторые изменения могут измениться, и старые 32-битные характеристики будут постоянной частью странности для всех, кто работает над вашим приложением. И это только в том случае, если драйвер 2,50 работает при доступе из 32-разрядной среды выполнения; после этого могут возникнуть другие проблемы. Я бы рекомендовал попробовать это, если бремя обслуживания для всего вашего клиентского кода, вероятно, будет очень низким в будущем (если проект небольшой или вряд ли изменится).

Мораль истории

Программное обеспечение гибнет быстро. Если проект не постоянно стремится выполнять работу для обеспечения обратной совместимости, все быстро прекратит работу, особенно в веб-программном обеспечении.

Это не то, что сам продукт ломается, это то, что Вселенная изменяется из-под нее миллионными способами. Если кому-то не хватает генералитета и он был достаточно длинным, чтобы узнать все эти небольшие изменения и как их отменить, очень сложно переместить все назад во времени/ревизии в место, где вещи "просто работают".

Если вы получаете двоичные файлы для чего-то, даже если это что-то якобы "обычное", как драйвер MySQL, держите их вокруг. В идеале, поделитесь им с Интернетом.

Если у вас есть источники чего-то, строго документируйте весь список зависимостей/инструментальной цепочки, в которых они нуждаются, и документируйте его для людей. Предположим, что инструменты, необходимые для чтения программных списков зависимостей, например, autotools сами по себе устарели. Ничто не является слишком "очевидным" для документирования; не архитектура, ядро ​​ABI, поведение libc - ничего. Теперь, когда у нас есть "в ящике на любом ядре", например Docker, вы можете хранить больше своих зависимостей программным способом, но не рассчитывайте на него.

Ответ 2

Как показал Zac B, нет реального способа подключения сервера MySQL 3.23 от клиента Ubuntu 16 с помощью UnixODBC и pyodbc 3.07.

FlipperPA предложила использовать pypi.python.org/pypi/MySQL-python/1.2.4 в качестве следующего решения. Поэтому я попробовал:

MySQL-python, установленный с помощью apt, не может подключиться к этому старому MySQL. Он использует системы mysql-client libs, которые не работают с MySQL 3.23.

MySQL-python, установленный с помощью pip, - это еще одна вещь: он получает библиотеки из mysql_config, включенных в libmysqlclient-dev. Также не работает с MySQL 3.23. Но здесь есть шанс что-то изменить.

Когда я устанавливаю libmariadb-client-lgpl-dev, я получаю mariadb_config, который похож на mysql_config. И mariadb-клиент Ubuntu 16 работал с MySQL 3.23 (как показано выше).

ln -s /usr/bin/mariadb_config mysql_config
pip install MySQL-python

Это делает работу. Я могу подключить этот старый сервер MySQL от Python.

Есть некоторые проблемы с типами данных, но в этом случае я не придирчив.