From 047b471b941996749d57dc88ecc7913ef2382bbd Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Thu, 10 Sep 2020 13:25:16 -0400 Subject: [PATCH 1/3] TST: add sql tests with parameters and percents --- pandas/tests/io/test_sql.py | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 1edcc937f72c3..4bb1a7bf3d73b 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -199,6 +199,26 @@ "mysql": "SELECT * FROM iris WHERE `Name` LIKE '%'", "postgresql": "SELECT * FROM iris WHERE \"Name\" LIKE '%'", }, + "read_no_parameters_with_doublepercent": { + "sqlite": "SELECT 1 %% 2 as x", + "mysql": "SELECT 1 %% 2 as x", + "postgresql": "SELECT 1 %% 2 as x", + }, + "read_parameters_with_percent": { + "sqlite": "SELECT ? as x, 2 % 3 as y", + "mysql": "SELECT %s as x, 2 % 3 as y", + "postgresql": "SELECT %s as x, 2 % 3 as y", + }, + "read_parameters_with_doublepercent": { + "sqlite": "SELECT ? as x, 2 %% 3 as y", + "mysql": "SELECT %s as x, 2 %% 3 as y", + "postgresql": "SELECT %s as x, 2 %% 3 as y", + }, + "read_named_parameters_with_doublepercent": { + "sqlite": "SELECT :name as x, 2 %% 3 as y", + "mysql": "SELECT %(name)s as x, 2 %% 3 as y", + "postgresql": "SELECT %(name)s as x, 2 %% 3 as y", + }, "create_view": { "sqlite": """ CREATE VIEW iris_view AS @@ -435,6 +455,42 @@ def _read_sql_iris_no_parameter_with_percent(self): iris_frame = self.pandasSQL.read_query(query, params=None) self._check_iris_loaded_frame(iris_frame) + def _read_sql_parameter_with_percent(self): + if self.flavor in {"postgresql", "mysql"}: + pytest.xfail("dialect DBI does not support parameters and single % ops") + query = SQL_STRINGS["read_parameters_with_percent"][self.flavor] + frame = self.pandasSQL.read_query(query, params=[1]) + tm.assert_frame_equal(frame, DataFrame({"x": [1], "y": [2]})) + + def _read_sql_iris_no_parameter_with_doublepercent(self): + query = SQL_STRINGS["read_no_parameters_with_doublepercent"][self.flavor] + frame = self.pandasSQL.read_query(query) + tm.assert_frame_equal(frame, DataFrame({"x": [1]})) + + def _read_sql_no_parameter_with_declarative_percent(self): + from sqlalchemy import sql + + query = sql.text("SELECT 1 % 2 as x") + frame = self.pandasSQL.read_query(query) + tm.assert_frame_equal(frame, DataFrame({"x": [1]})) + + @pytest.mark.xfail + def _read_sql_parameter_with_doublepercent(self): + if self.flavor == "sqlite": + pytest.xfail("sqlite DBI does not use doublepercent syntax") + + query = SQL_STRINGS["read_parameters_with_doublepercent"][self.flavor] + frame = self.pandasSQL.read_query(query, params=[1]) + tm.assert_frame_equal(frame, DataFrame({"x": [1], "y": [2]})) + + def _read_sql_named_parameter_with_doublepercent(self): + if self.flavor == "sqlite": + pytest.xfail("sqlite DBI does not use doublepercent syntax") + + query = SQL_STRINGS["read_named_parameters_with_doublepercent"][self.flavor] + frame = self.pandasSQL.read_query(query, params={"name": 1}) + tm.assert_frame_equal(frame, DataFrame({"x": [1], "y": [2]})) + def _to_sql(self, method=None): self.drop_table("test_frame1") @@ -1286,6 +1342,25 @@ def test_read_sql_parameter(self): def test_read_sql_named_parameter(self): self._read_sql_iris_named_parameter() + def test_read_sql_no_parameter_with_percent(self): + self._read_sql_iris_no_parameter_with_percent() + + @pytest.mark.xfail + def test_read_sql_no_parameter_with_doublepercent(self): + self._read_sql_iris_no_parameter_with_doublepercent() + + def test_read_sql_no_parameter_with_declarative_percent(self): + self._read_sql_no_parameter_with_declarative_percent() + + def test_read_sql_parameter_with_percent(self): + self._read_sql_parameter_with_percent() + + def test_read_sql_parameter_with_doublepercent(self): + self._read_sql_parameter_with_doublepercent() + + def test_read_sql_named_parameter_with_doublepercent(self): + self._read_sql_named_parameter_with_doublepercent() + def test_to_sql(self): self._to_sql() @@ -2169,6 +2244,15 @@ def test_read_sql_parameter(self): def test_read_sql_named_parameter(self): self._read_sql_iris_named_parameter() + def test_read_sql_parameter_with_percent(self): + self._read_sql_parameter_with_percent() + + def test_read_sql_parameter_with_doublepercent(self): + self._read_sql_parameter_with_doublepercent() + + def test_read_sql_named_parameter_with_doublepercent(self): + self._read_sql_named_parameter_with_doublepercent() + def test_to_sql(self): self._to_sql() From cc62a61db67e3f69e751e06af91a2fef5b3a291a Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Thu, 10 Sep 2020 13:26:11 -0400 Subject: [PATCH 2/3] BUG: allow sql using %% with postgres (#35484) --- pandas/io/sql.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 51888e5021d80..cbdbd800a2b3e 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -1159,9 +1159,7 @@ def run_transaction(self): def execute(self, *args, **kwargs): """Simple passthrough to SQLAlchemy connectable""" - return self.connectable.execution_options(no_parameters=True).execute( - *args, **kwargs - ) + return self.connectable.execution_options().execute(*args, **kwargs) def read_table( self, @@ -1291,6 +1289,11 @@ def read_query( read_sql """ + if isinstance(sql, str) and params is None: + from sqlalchemy.sql import text + + sql = text(sql) + args = _convert_params(sql, params) result = self.execute(*args) From 9f4280fded21a1e5590fa5bfdb2e81c884099678 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Thu, 10 Sep 2020 16:45:08 -0400 Subject: [PATCH 3/3] TST: clarify in test that its marked xfail --- pandas/tests/io/test_sql.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 4bb1a7bf3d73b..8924bb9e2559d 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -463,6 +463,7 @@ def _read_sql_parameter_with_percent(self): tm.assert_frame_equal(frame, DataFrame({"x": [1], "y": [2]})) def _read_sql_iris_no_parameter_with_doublepercent(self): + # Note that this is marked xfail in the method wrapping it... query = SQL_STRINGS["read_no_parameters_with_doublepercent"][self.flavor] frame = self.pandasSQL.read_query(query) tm.assert_frame_equal(frame, DataFrame({"x": [1]}))