#python #mysql
#python #mysql
Вопрос:
Мне нужно выполнить определенные команды MySQL в скрипте python, что является простой задачей. В целях тестирования я свел команды к следующему:
import mysql.connector
script = """
CREATE DATABASE `new_project`;
CREATE TABLE `new_project`.`category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
UNIQUE KEY `unq_name` (`name`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
"""
connection = mysql.connector.connect(
host="localhost",
port="3306",
user="root",
passwd="somepassword",
)
cursor = connection.cursor()
try:
print("begin execution")
cursor.execute(script, multi=True)
warnings = cursor.fetchwarnings()
if warnings:
for warning in warnings:
print(warning)
connection.commit()
cursor.close()
connection.close()
print("connection closed")
except mysql.connector.Error as err:
print(err.msg)
Учетные данные пользователя заменяются правильной информацией при запуске скрипта.
Вывод этого скрипта
begin execution
connection closed
без ошибок, предупреждений или других выходных данных. База данных new_project
не создана. Когда я запускаю те же команды MySQL в другом интерфейсе, они работают должным образом и создают базу данных и таблицу.
Должно быть, я упускаю из виду что-то очень простое.
Ответ №1:
В документации execute
упоминается, что метод возвращает итератор с результатами для каждого запроса, когда multi=True
. Кажется, запросы ничего не делают, пока итератор не будет обработан, независимо от commit()
. Однако CREATE
инструкции не дают никаких результатов, и попытка выполнить итерацию по возвращаемому значению execute
приводит к исключению: generator raised StopIteration
. Это связано с ошибкой в модуле connector и было исправлено в версии 8.0.13 с поддержкой python 3.7.
Теперь решение состоит в том, чтобы всегда повторять возвращаемое значение execute
, даже если возвращаемых данных не ожидается, и обновлять модуль connector. Если обновление невозможно, можно перехватить неудачную итерацию и продолжить.
Исправленный код (включая часть для более ранних версий модуля connector) теперь выглядит примерно так:
try:
results = cursor.execute(script, multi=True)
try:
for result in results:
pass
except Exception as e:
pass
warnings = cursor.fetchwarnings()
if warnings:
for warning in warnings:
# handle warning
connection.commit()
cursor.close()
connection.close()
except mysql.connector.Error as err:
# handle error
Ответ №2:
Попробуйте использовать password=»somepassword» вместо passwd=»somepassword». И удалите multi=True. Это создаст предупреждение, но все равно выполнит оба ваших оператора.
Комментарии:
1.
passwd
Хорошая уловка, спасибо. В документации говорится, что это синоним, но также упоминается, что на него не следует полагаться. Отключениеmulti
опции, вопреки ожиданиям, работает, даже еслиcomiit()
она никогда не вызывается. Однако я думаю, что нашел причину, по которой исходный код не работает, и у меня есть решение, которое работает сmulti
и, надеюсь, не сломается, как только ошибка в модуле connector будет исправлена.