diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst index d6a00022efb79..fa834552585d1 100644 --- a/doc/source/user_guide/io.rst +++ b/doc/source/user_guide/io.rst @@ -5785,21 +5785,6 @@ Specifying this will return an iterator through chunks of the query result: for chunk in pd.read_sql_query("SELECT * FROM data_chunks", engine, chunksize=5): print(chunk) -You can also run a plain query without creating a ``DataFrame`` with -:func:`~pandas.io.sql.execute`. This is useful for queries that don't return values, -such as INSERT. This is functionally equivalent to calling ``execute`` on the -SQLAlchemy engine or db connection object. Again, you must use the SQL syntax -variant appropriate for your database. - -.. code-block:: python - - from pandas.io import sql - - sql.execute("SELECT * FROM table_name", engine) - sql.execute( - "INSERT INTO table_name VALUES(?, ?, ?)", engine, params=[("id", 1, 12.2, True)] - ) - Engine connection examples '''''''''''''''''''''''''' diff --git a/pandas/io/sql.py b/pandas/io/sql.py index e3510f71bd0cd..bccb582451032 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -169,9 +169,7 @@ def execute(sql, con, params=None): ---------- sql : string SQL query to be executed. - con : SQLAlchemy connectable(engine/connection) or sqlite3 connection - Using SQLAlchemy makes it possible to use any DB supported by the - library. + con : SQLAlchemy connection or sqlite3 connection If a DBAPI2 object, only sqlite3 is supported. params : list or tuple, optional, default: None List of parameters to pass to execute method. @@ -180,6 +178,10 @@ def execute(sql, con, params=None): ------- Results Iterable """ + sqlalchemy = import_optional_dependency("sqlalchemy", errors="ignore") + + if sqlalchemy is not None and isinstance(con, (str, sqlalchemy.engine.Engine)): + raise TypeError("pandas.io.sql.execute requires a connection") # GH50185 with pandasSQL_builder(con, need_transaction=True) as pandas_sql: args = _convert_params(sql, params) return pandas_sql.execute(*args) diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 394fceb69b788..bbd9364475c2a 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -659,6 +659,11 @@ def psql_insert_copy(table, conn, keys, data_iter): tm.assert_frame_equal(result, expected) +def test_execute_typeerror(sqlite_iris_engine): + with pytest.raises(TypeError, match="pandas.io.sql.execute requires a connection"): + sql.execute("select * from iris", sqlite_iris_engine) + + class MixInBase: def teardown_method(self): # if setup fails, there may not be a connection to close. @@ -952,7 +957,8 @@ def test_roundtrip_chunksize(self, test_frame1): def test_execute_sql(self): # drop_sql = "DROP TABLE IF EXISTS test" # should already be done - iris_results = sql.execute("SELECT * FROM iris", con=self.conn) + with sql.pandasSQL_builder(self.conn) as pandas_sql: + iris_results = pandas_sql.execute("SELECT * FROM iris") row = iris_results.fetchone() tm.equalContents(row, [5.1, 3.5, 1.4, 0.2, "Iris-setosa"]) @@ -2692,7 +2698,8 @@ def format_query(sql, *args): def tquery(query, con=None): """Replace removed sql.tquery function""" - res = sql.execute(query, con=con).fetchall() + with sql.pandasSQL_builder(con) as pandas_sql: + res = pandas_sql.execute(query).fetchall() return None if res is None else list(res) @@ -2757,7 +2764,8 @@ def test_execute(self, sqlite_buildin): ins = "INSERT INTO test VALUES (?, ?, ?, ?)" row = frame.iloc[0] - sql.execute(ins, sqlite_buildin, params=tuple(row)) + with sql.pandasSQL_builder(sqlite_buildin) as pandas_sql: + pandas_sql.execute(ins, tuple(row)) sqlite_buildin.commit() result = sql.read_sql("select * from test", sqlite_buildin) @@ -2792,11 +2800,12 @@ def test_execute_fail(self, sqlite_buildin): cur = sqlite_buildin.cursor() cur.execute(create_sql) - sql.execute('INSERT INTO test VALUES("foo", "bar", 1.234)', sqlite_buildin) - sql.execute('INSERT INTO test VALUES("foo", "baz", 2.567)', sqlite_buildin) + with sql.pandasSQL_builder(sqlite_buildin) as pandas_sql: + pandas_sql.execute('INSERT INTO test VALUES("foo", "bar", 1.234)') + pandas_sql.execute('INSERT INTO test VALUES("foo", "baz", 2.567)') - with pytest.raises(sql.DatabaseError, match="Execution failed on sql"): - sql.execute('INSERT INTO test VALUES("foo", "bar", 7)', sqlite_buildin) + with pytest.raises(sql.DatabaseError, match="Execution failed on sql"): + pandas_sql.execute('INSERT INTO test VALUES("foo", "bar", 7)') def test_execute_closed_connection(self): create_sql = """ @@ -2812,7 +2821,8 @@ def test_execute_closed_connection(self): cur = conn.cursor() cur.execute(create_sql) - sql.execute('INSERT INTO test VALUES("foo", "bar", 1.234)', conn) + with sql.pandasSQL_builder(conn) as pandas_sql: + pandas_sql.execute('INSERT INTO test VALUES("foo", "bar", 1.234)') msg = "Cannot operate on a closed database." with pytest.raises(sqlite3.ProgrammingError, match=msg):