Bug #78308 Executing several statements is "allowed" by default
Submitted: 2 Sep 2015 18:09 Modified: 11 Jan 2023 20:21
Reporter: Sebastien Celles Email Updates:
Status: Not a Bug Impact on me:
None 
Category:Connector / Python Severity:S3 (Non-critical)
Version:2.0.4 OS:Any
Assigned to: CPU Architecture:Any

[2 Sep 2015 18:09] Sebastien Celles
Description:
mysql-connector-python execute several statements but raises InterfaceError

How to repeat:
Hello,

I noticed a strange behavior with mysql-connector-python

I think it's more on mysql-connector-python side than on Pandas side
but I think you should have a look.

import sqlalchemy
import pandas as pd
import numpy as np

db_uri = 'mysql+mysqlconnector://root:root@localhost:3306/test'
engine = sqlalchemy.create_engine(db_uri)
df = pd.DataFrame(np.random.randn(4,3), columns=['a','b','c'])
df.to_sql("df", engine)
df.to_sql("test_table", engine)

#pd.read_sql("SELECT * from df where a>0", engine)
pd.read_sql("SELECT * from df where a>0; DROP TABLE test_table", engine)

raises

    InterfaceError: (mysql.connector.errors.InterfaceError) Use multi=True when executing multiple statements [SQL: 'SELECT * from df where a>0; DROP TABLE test_table']

but test_table is dropped

same with SQLite

db_uri = 'sqlite:///test.db'
engine = sqlalchemy.create_engine(db_uri)
df = pd.DataFrame(np.random.randn(4,3), columns=['a','b','c'])
df.to_sql("df", engine)
df.to_sql("test_table", engine)

raises

    Warning: You can only execute one statement at a time.

and test_table is (fortunately) not dropped with SQLite.

I haven't test with MySQL and other driver such as MySQLdb driver.

Kind regards
[3 Sep 2015 8:09] MySQL Verification Team
Hello Sebastien Celles,

Thank you for the report.

Thanks,
Umesh
[11 Jan 2023 20:21] Oscar Pacheco
Posted by developer:
 
Hello Sebastien, 

After analyzing the report carefully, I found there isn't a bug from connector-python.

SQLAlchemy does not support multi-query/command statements based on my research, and even if it does, the problem does not come from connector-python (see script below).

Regarding connector-python, I created an example very similar to the one you provided, I see no issues.

--------- bug78308.py (python script) ---------
"""
$ pip list
Package                Version
---------------------- -------
mysql-connector-python 8.0.31
numpy                  1.24.1
pandas                 1.5.2
SQLAlchemy             1.4.46
"""

import os
import sqlalchemy
import pandas as pd
import numpy as np
import mysql.connector

password = os.environ.get("mypass")
db_uri = f"mysql+mysqlconnector://root:{password}@localhost:3306/test"
engine = sqlalchemy.create_engine(db_uri, connect_args={"use_pure": True})
with engine.connect() as cnx:
    df = pd.DataFrame(np.random.randn(4, 3), columns=["a", "b", "c"])
    df.to_sql("df", con=cnx, if_exists="replace")
    df.to_sql("test_table", con=cnx, if_exists="replace")

    """ This code block works fine """
    # res = pd.read_sql("SELECT * from df where a>0", con=cnx)
    # print(res)

    """ This code block fails with error:
    sqlalchemy.exc.InterfaceError: (mysql.connector.errors.InterfaceError) Use multi=True when executing
    multiple statements [SQL: SELECT * from df where a>0; SELECT @@version]

    because SQLAlchemy does not support multi-query/command statements.
    """
    # res = pd.read_sql("SELECT * from df where a>0; SELECT @@version", con=cnx)
    # print(res)

config = {
    "username": "root",
    "password": password,
    "use_pure": True,
    "port": 3306,
    "database": "test",
}
with mysql.connector.connect(**config) as cnx:
    with cnx.cursor() as cur:
        cur.execute("SELECT * from df where a>0")
        single_query_res_1 = cur.fetchall()

        cur.execute("SELECT @@version")
        single_query_res_2 = cur.fetchall()

        """ for recent versions of connector python, multi=False by default """
        try:
            multi_query_res = [
                res_cursor.fetchall()
                for res_cursor in cur.execute(
                    "SELECT * from df where a>0; SELECT @@version; DROP TABLE test_table; DROP TABLE df",
                    multi=True,
                )
            ]
        except mysql.connector.errors.InterfaceError:
            print("Bug report confirmed!")
        else:
            if multi_query_res[:2] == [single_query_res_1, single_query_res_2]:
                print(
                    "Execute with multi=True is working!"
                )

--------- Terminal ---------
$ python bug78308.py
Execute with multi=True is working!

Finally, after taking a look at the MySQL console I found both tables `df` and `test_table` were dropped successfully.
--------- MySQL Console ---------
mysql> USE test;
mysql> SHOW TABLES;
Empty set (0.00 sec)
[11 Jan 2023 20:23] Oscar Pacheco
Posted by developer:
 
Hello Sebastien, 

After analyzing the report carefully, I found there isn't a bug from connector-python.

SQLAlchemy does not support multi-query/command statements based on my research, and even if it does, the problem does not come from connector-python (see script below).

Regarding connector-python, I created an example very similar to the one you provided, I see no issues.

--------- bug78308.py (python script) ---------
"""
$ pip list
Package                Version
---------------------- -------
mysql-connector-python 8.0.31
numpy                  1.24.1
pandas                 1.5.2
SQLAlchemy             1.4.46
"""

import os
import sqlalchemy
import pandas as pd
import numpy as np
import mysql.connector

password = os.environ.get("mypass")
db_uri = f"mysql+mysqlconnector://root:{password}@localhost:3306/test"
engine = sqlalchemy.create_engine(db_uri, connect_args={"use_pure": True})
with engine.connect() as cnx:
    df = pd.DataFrame(np.random.randn(4, 3), columns=["a", "b", "c"])
    df.to_sql("df", con=cnx, if_exists="replace")
    df.to_sql("test_table", con=cnx, if_exists="replace")

    """ This code block works fine """
    # res = pd.read_sql("SELECT * from df where a>0", con=cnx)
    # print(res)

    """ This code block fails with error:
    sqlalchemy.exc.InterfaceError: (mysql.connector.errors.InterfaceError) Use multi=True when executing
    multiple statements [SQL: SELECT * from df where a>0; SELECT @@version]

    because SQLAlchemy does not support multi-query/command statements.
    """
    # res = pd.read_sql("SELECT * from df where a>0; SELECT @@version", con=cnx)
    # print(res)

config = {
    "username": "root",
    "password": password,
    "use_pure": True,
    "port": 3306,
    "database": "test",
}
with mysql.connector.connect(**config) as cnx:
    with cnx.cursor() as cur:
        cur.execute("SELECT * from df where a>0")
        single_query_res_1 = cur.fetchall()

        cur.execute("SELECT @@version")
        single_query_res_2 = cur.fetchall()

        """ for recent versions of connector python, multi=False by default """
        try:
            multi_query_res = [
                res_cursor.fetchall()
                for res_cursor in cur.execute(
                    "SELECT * from df where a>0; SELECT @@version; DROP TABLE test_table; DROP TABLE df",
                    multi=True,
                )
            ]
        except mysql.connector.errors.InterfaceError:
            print("Bug report confirmed!")
        else:
            if multi_query_res[:2] == [single_query_res_1, single_query_res_2]:
                print(
                    "Execute with multi=True is working!"
                )

--------- Terminal ---------
$ python bug78308.py
Execute with multi=True is working!

Finally, after taking a look at the MySQL console I found both tables `df` and `test_table` were dropped successfully.
--------- MySQL Console ---------
mysql> USE test;
mysql> SHOW TABLES;
Empty set (0.00 sec)
[11 Jan 2023 20:23] Oscar Pacheco
Posted by developer:
 
Hello Sebastien, 

After analyzing the report carefully, I found there isn't a bug from connector-python.

SQLAlchemy does not support multi-query/command statements based on my research, and even if it does, the problem does not come from connector-python (see script below).

Regarding connector-python, I created an example very similar to the one you provided, I see no issues.

--------- bug78308.py (python script) ---------
"""
$ pip list
Package                Version
---------------------- -------
mysql-connector-python 8.0.31
numpy                  1.24.1
pandas                 1.5.2
SQLAlchemy             1.4.46
"""

import os
import sqlalchemy
import pandas as pd
import numpy as np
import mysql.connector

password = os.environ.get("mypass")
db_uri = f"mysql+mysqlconnector://root:{password}@localhost:3306/test"
engine = sqlalchemy.create_engine(db_uri, connect_args={"use_pure": True})
with engine.connect() as cnx:
    df = pd.DataFrame(np.random.randn(4, 3), columns=["a", "b", "c"])
    df.to_sql("df", con=cnx, if_exists="replace")
    df.to_sql("test_table", con=cnx, if_exists="replace")

    """ This code block works fine """
    # res = pd.read_sql("SELECT * from df where a>0", con=cnx)
    # print(res)

    """ This code block fails with error:
    sqlalchemy.exc.InterfaceError: (mysql.connector.errors.InterfaceError) Use multi=True when executing
    multiple statements [SQL: SELECT * from df where a>0; SELECT @@version]

    because SQLAlchemy does not support multi-query/command statements.
    """
    # res = pd.read_sql("SELECT * from df where a>0; SELECT @@version", con=cnx)
    # print(res)

config = {
    "username": "root",
    "password": password,
    "use_pure": True,
    "port": 3306,
    "database": "test",
}
with mysql.connector.connect(**config) as cnx:
    with cnx.cursor() as cur:
        cur.execute("SELECT * from df where a>0")
        single_query_res_1 = cur.fetchall()

        cur.execute("SELECT @@version")
        single_query_res_2 = cur.fetchall()

        """ for recent versions of connector python, multi=False by default """
        try:
            multi_query_res = [
                res_cursor.fetchall()
                for res_cursor in cur.execute(
                    "SELECT * from df where a>0; SELECT @@version; DROP TABLE test_table; DROP TABLE df",
                    multi=True,
                )
            ]
        except mysql.connector.errors.InterfaceError:
            print("Bug report confirmed!")
        else:
            if multi_query_res[:2] == [single_query_res_1, single_query_res_2]:
                print(
                    "Execute with multi=True is working!"
                )

--------- Terminal ---------
$ python bug78308.py
Execute with multi=True is working!

Finally, after taking a look at the MySQL console I found both tables `df` and `test_table` were dropped successfully.
--------- MySQL Console ---------
mysql> USE test;
mysql> SHOW TABLES;
Empty set (0.00 sec)