MySQLdb.указатель.execute не может запускать несколько запросов

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

конфигурация MySQLd:

sql_mode = STRICT_ALL_TABLES
default-storage-engine = innodb

Python-кода:

from contextlib import closing
import MySQLdb
database_connection = MySQLdb.connect(host="127.0.0.1", user="root")
with closing(database_connection.cursor()) as cursor:
    database_connection.begin()
    cursor.execute('DROP DATABASE IF EXISTS db_name')
    cursor.execute('CREATE DATABASE db_name')
    cursor.execute('USE db_name')
    cursor.execute('CREATE TABLE table_name(first_field INTEGER)')
with closing(database_connection.cursor()) as cursor:
    try:
        database_connection.begin()
        cursor.execute('USE db_name')
        cursor.execute('INSERT INTO table_name VALUES (1)')
        cursor.execute('INSERT INTO table_name VALUES ("non-integer value")')
        database_connection.commit()
    except Exception as error:
        print("Exception thrown: {0}".format(error))
        database_connection.rollback()
        print("Rolled back")
with closing(database_connection.cursor()) as cursor:
    try:
        database_connection.begin()
        cursor.execute('USE db_name')
        cursor.execute('INSERT INTO table_name VALUES (1); INSERT INTO table_name VALUES ("non-integer value")')
        database_connection.commit()
    except:
        print("Exception thrown: {0}".format(error))
        database_connection.rollback()
        print("Rolled back")

ожидаемый результат:" исключение брошено "и" откат " напечатаны дважды.

фактический результат с MySQL-python 1.2.4:

Exception thrown: (1366, "Incorrect integer value: 'non-integer value' for column 'first_field' at row 1")
Rolled back
Exception thrown: (1366, "Incorrect integer value: 'non-integer value' for column 'first_field' at row 1")
Traceback (most recent call last):
  File "test.py", line 30, in <module>
    print("Rolled back")
  File ".../python-2.7/lib/python2.7/contextlib.py", line 154, in __exit__
    self.thing.close()
  File ".../virtualenv-python-2.7/lib/python2.7/site-packages/MySQLdb/cursors.py", line 100, in close
    while self.nextset(): pass
  File ".../virtualenv-python-2.7/lib/python2.7/site-packages/MySQLdb/cursors.py", line 132, in nextset
    nr = db.next_result()
_mysql_exceptions.OperationalError: (1366, "Incorrect integer value: 'non-integer value' for column 'first_field' at row 1")

что это дает? Действительно ли нам нужно разбирать SQL для разделения операторы (со всей обработкой escape и quote, которая влечет за собой), чтобы запустить их в нескольких executes?

4 ответов


вроде все питон ДБ-интерфейс API 2.0 на cursor.execute() разработан метод take only один оператор, потому что он гарантирует состояние курсора после этого.

использовать cursor.executemany() метод. Заметьте это,согласно спецификации DB-API 2.0:

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

через несколько INSERT заявления должны быть просто прекрасны:

cursor.executemany('INSERT INTO table_name VALUES (%s)',
    [(1,), ("non-integer value",)]
)

Если вам нужно выполнить ряд разрозненных операторов, таких как из скрипта, то в большинстве случаев вы можете просто разделить операторы на ; и кормить каждое заявление cursor.execute() отдельно.


Я думаю, вам нужно пройти multi=True до execute при использовании нескольких операторов см. http://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html

обновление: это относится к mysql.connector модуль, а не MySQLdb использовать в этом случае.


по-видимому, нет способа сделать это в MySQLdb (он же. MySQL-python), поэтому мы закончили тем, что просто communicateing данные в subprocess.Popen([mysql, ...], stdin=subprocess.PIPE) и проверив returncode.


С помощью mysql программа через Popen определенно будет работать, но если вы хотите просто использовать существующее соединение (и курсор),sqlparse пакета split функция, которая будет разделена на операторы. Я не уверен, что такое совместимость, но у меня есть скрипт, который делает:

with open('file.sql', 'rb') as f:
    for statement in sqlparse.split(f.read()):
        if not statement:
            continue
        cur.execute(statement)

это только когда-либо кормили падение таблицы и создавать таблицы операторов, но работает для меня.

https://pypi.python.org/pypi/sqlparse