mysql.connector.cursor.execute() выполняется автоматически, но не вносит никаких изменений, несмотря на фиксацию()

#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 будет исправлена.