From b4e97f7505a4f488d5f7261a69acdf7ce97fa545 Mon Sep 17 00:00:00 2001 From: MarcoGorelli <> Date: Fri, 23 Sep 2022 15:19:47 +0100 Subject: [PATCH 1/4] sqlalchemy.inspect shouldnt shadow builtin inspect --- doc/source/whatsnew/v1.5.1.rst | 2 +- pandas/io/sql.py | 8 ++++---- pandas/tests/io/test_sql.py | 15 +++++++++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v1.5.1.rst b/doc/source/whatsnew/v1.5.1.rst index 3f4265245bcb6..0c4e098262dd8 100644 --- a/doc/source/whatsnew/v1.5.1.rst +++ b/doc/source/whatsnew/v1.5.1.rst @@ -17,7 +17,7 @@ Fixed regressions - Fixed Regression in :meth:`DataFrame.loc` when setting values as a :class:`DataFrame` with all ``True`` indexer (:issue:`48701`) - Regression in :func:`.read_csv` causing an ``EmptyDataError`` when using an UTF-8 file handle that was already read from (:issue:`48646`) - Fixed performance regression in :func:`factorize` when ``na_sentinel`` is not ``None`` and ``sort=False`` (:issue:`48620`) -- +- Fixed regression in :meth:`SQLDatabase.check_case_sensitive` causing an ``AttributeError`` when table name can't be found (:issue:`48733`) .. --------------------------------------------------------------------------- diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 9beafb9032676..d718a0ad4882b 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -1645,10 +1645,10 @@ def check_case_sensitive( if not name.isdigit() and not name.islower(): # check for potentially case sensitivity issues (GH7815) # Only check when name is not a number and name is not lower case - from sqlalchemy import inspect + sqlalchemy = import_optional_dependency("sqlalchemy", errors="ignore") with self.connectable.connect() as conn: - insp = inspect(conn) + insp = sqlalchemy.inspect(conn) table_names = insp.get_table_names(schema=schema or self.meta.schema) if name not in table_names: msg = ( @@ -1757,9 +1757,9 @@ def tables(self): return self.meta.tables def has_table(self, name: str, schema: str | None = None): - from sqlalchemy import inspect + sqlalchemy = import_optional_dependency("sqlalchemy", errors="ignore") - insp = inspect(self.connectable) + insp = sqlalchemy.inspect(self.connectable) return insp.has_table(name, schema or self.meta.schema) def get_table(self, table_name: str, schema: str | None = None) -> Table: diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index ee55837324f20..1bfb85f369415 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -1357,10 +1357,17 @@ def test_not_reflect_all_tables(self): def test_warning_case_insensitive_table_name(self, test_frame1): # see gh-7815 - # - # We can't test that this warning is triggered, a the database - # configuration would have to be altered. But here we test that - # the warning is certainly NOT triggered in a normal case. + with tm.assert_produces_warning( + UserWarning, + match=( + r"The provided table name 'TABLE1' is not found exactly as such in " + r"the database after writing the table, possibly due to case " + r"sensitivity issues. Consider using lower case table names." + ), + ): + sql.SQLDatabase(self.conn).check_case_sensitive("TABLE1", "") + + # Test that the warning is certainly NOT triggered in a normal case. with tm.assert_produces_warning(None): test_frame1.to_sql("CaseSensitive", self.conn) From c59c8c275a32c297996b5b033709aa0d23b4134c Mon Sep 17 00:00:00 2001 From: MarcoGorelli <> Date: Fri, 23 Sep 2022 15:43:33 +0100 Subject: [PATCH 2/4] :label: --- pandas/io/sql.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/io/sql.py b/pandas/io/sql.py index d718a0ad4882b..3ff88dcb92645 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -1635,8 +1635,8 @@ def prep_table( def check_case_sensitive( self, - name, - schema, + name: str, + schema: str | None, ) -> None: """ Checks table name for issues with case-sensitivity. @@ -1666,11 +1666,11 @@ def check_case_sensitive( def to_sql( self, frame, - name, + name: str, if_exists: Literal["fail", "replace", "append"] = "fail", index: bool = True, index_label=None, - schema=None, + schema: str | None = None, chunksize=None, dtype: DtypeArg | None = None, method=None, From 8d8da1d0989d653b1660be23d2234b0fa4cdf226 Mon Sep 17 00:00:00 2001 From: MarcoGorelli <> Date: Sat, 24 Sep 2022 14:13:42 +0100 Subject: [PATCH 3/4] reword in terms of to_sql --- doc/source/whatsnew/v1.5.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.1.rst b/doc/source/whatsnew/v1.5.1.rst index 0c4e098262dd8..ffce5bcd4e7fc 100644 --- a/doc/source/whatsnew/v1.5.1.rst +++ b/doc/source/whatsnew/v1.5.1.rst @@ -17,7 +17,7 @@ Fixed regressions - Fixed Regression in :meth:`DataFrame.loc` when setting values as a :class:`DataFrame` with all ``True`` indexer (:issue:`48701`) - Regression in :func:`.read_csv` causing an ``EmptyDataError`` when using an UTF-8 file handle that was already read from (:issue:`48646`) - Fixed performance regression in :func:`factorize` when ``na_sentinel`` is not ``None`` and ``sort=False`` (:issue:`48620`) -- Fixed regression in :meth:`SQLDatabase.check_case_sensitive` causing an ``AttributeError`` when table name can't be found (:issue:`48733`) +- Fixed regression causing an ``AttributeError`` during warning emitted if the provided table name in :meth:`DataFrame.to_sql` and the table name actually used in the database do not match (:issue:`48733`) .. --------------------------------------------------------------------------- From b780065511acedf6ce68209a5bc7da3c2c4e3b85 Mon Sep 17 00:00:00 2001 From: MarcoGorelli <> Date: Mon, 26 Sep 2022 10:14:22 +0100 Subject: [PATCH 4/4] import sqlalchemy.inspect as sqlalchemy_inspect --- pandas/io/sql.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 3ff88dcb92645..f5ad0787ff8df 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -1645,10 +1645,10 @@ def check_case_sensitive( if not name.isdigit() and not name.islower(): # check for potentially case sensitivity issues (GH7815) # Only check when name is not a number and name is not lower case - sqlalchemy = import_optional_dependency("sqlalchemy", errors="ignore") + from sqlalchemy import inspect as sqlalchemy_inspect with self.connectable.connect() as conn: - insp = sqlalchemy.inspect(conn) + insp = sqlalchemy_inspect(conn) table_names = insp.get_table_names(schema=schema or self.meta.schema) if name not in table_names: msg = ( @@ -1757,9 +1757,9 @@ def tables(self): return self.meta.tables def has_table(self, name: str, schema: str | None = None): - sqlalchemy = import_optional_dependency("sqlalchemy", errors="ignore") + from sqlalchemy import inspect as sqlalchemy_inspect - insp = sqlalchemy.inspect(self.connectable) + insp = sqlalchemy_inspect(self.connectable) return insp.has_table(name, schema or self.meta.schema) def get_table(self, table_name: str, schema: str | None = None) -> Table: