Skip to content

Commit 743111e

Browse files
authored
BUG: read_sql_table unable to read views (pandas-dev#54185)
1 parent 8f1c56d commit 743111e

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

doc/source/whatsnew/v2.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ I/O
528528
- Bug in :func:`read_hdf` not properly closing store after a ``IndexError`` is raised (:issue:`52781`)
529529
- Bug in :func:`read_html`, style elements were read into DataFrames (:issue:`52197`)
530530
- Bug in :func:`read_html`, tail texts were removed together with elements containing ``display:none`` style (:issue:`51629`)
531+
- Bug in :func:`read_sql_table` raising an exception when reading a view (:issue:`52969`)
531532
- Bug in :func:`read_sql` when reading multiple timezone aware columns with the same column name (:issue:`44421`)
532533
- Bug in :func:`read_xml` stripping whitespace in string data (:issue:`53811`)
533534
- Bug in :meth:`DataFrame.to_html` where ``colspace`` was incorrectly applied in case of multi index columns (:issue:`53885`)

pandas/io/sql.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ def read_table(
16551655
SQLDatabase.read_query
16561656
16571657
"""
1658-
self.meta.reflect(bind=self.con, only=[table_name])
1658+
self.meta.reflect(bind=self.con, only=[table_name], views=True)
16591659
table = SQLTable(table_name, self, index=index_col, schema=schema)
16601660
if chunksize is not None:
16611661
self.returns_generator = True
@@ -1989,7 +1989,9 @@ def get_table(self, table_name: str, schema: str | None = None) -> Table:
19891989
def drop_table(self, table_name: str, schema: str | None = None) -> None:
19901990
schema = schema or self.meta.schema
19911991
if self.has_table(table_name, schema):
1992-
self.meta.reflect(bind=self.con, only=[table_name], schema=schema)
1992+
self.meta.reflect(
1993+
bind=self.con, only=[table_name], schema=schema, views=True
1994+
)
19931995
with self.run_transaction():
19941996
self.get_table(table_name, schema).drop(bind=self.con)
19951997
self.meta.clear()
@@ -2411,7 +2413,15 @@ def to_sql(
24112413

24122414
def has_table(self, name: str, schema: str | None = None) -> bool:
24132415
wld = "?"
2414-
query = f"SELECT name FROM sqlite_master WHERE type='table' AND name={wld};"
2416+
query = f"""
2417+
SELECT
2418+
name
2419+
FROM
2420+
sqlite_master
2421+
WHERE
2422+
type IN ('table', 'view')
2423+
AND name={wld};
2424+
"""
24152425

24162426
return len(self.execute(query, [name]).fetchall()) > 0
24172427

pandas/tests/io/test_sql.py

+63
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from io import StringIO
3030
from pathlib import Path
3131
import sqlite3
32+
import uuid
3233

3334
import numpy as np
3435
import pytest
@@ -932,6 +933,68 @@ def insert_on_conflict(table, conn, keys, data_iter):
932933
pandasSQL.drop_table("test_insert_conflict")
933934

934935

936+
@pytest.mark.db
937+
@pytest.mark.parametrize("conn", postgresql_connectable)
938+
def test_read_view_postgres(conn, request):
939+
# GH 52969
940+
conn = request.getfixturevalue(conn)
941+
942+
from sqlalchemy.engine import Engine
943+
from sqlalchemy.sql import text
944+
945+
table_name = f"group_{uuid.uuid4().hex}"
946+
view_name = f"group_view_{uuid.uuid4().hex}"
947+
948+
sql_stmt = text(
949+
f"""
950+
CREATE TABLE {table_name} (
951+
group_id INTEGER,
952+
name TEXT
953+
);
954+
INSERT INTO {table_name} VALUES
955+
(1, 'name');
956+
CREATE VIEW {view_name}
957+
AS
958+
SELECT * FROM {table_name};
959+
"""
960+
)
961+
if isinstance(conn, Engine):
962+
with conn.connect() as con:
963+
with con.begin():
964+
con.execute(sql_stmt)
965+
else:
966+
with conn.begin():
967+
conn.execute(sql_stmt)
968+
result = read_sql_table(view_name, conn)
969+
expected = DataFrame({"group_id": [1], "name": "name"})
970+
tm.assert_frame_equal(result, expected)
971+
972+
973+
def test_read_view_sqlite(sqlite_buildin):
974+
# GH 52969
975+
create_table = """
976+
CREATE TABLE groups (
977+
group_id INTEGER,
978+
name TEXT
979+
);
980+
"""
981+
insert_into = """
982+
INSERT INTO groups VALUES
983+
(1, 'name');
984+
"""
985+
create_view = """
986+
CREATE VIEW group_view
987+
AS
988+
SELECT * FROM groups;
989+
"""
990+
sqlite_buildin.execute(create_table)
991+
sqlite_buildin.execute(insert_into)
992+
sqlite_buildin.execute(create_view)
993+
result = pd.read_sql("SELECT * FROM group_view", sqlite_buildin)
994+
expected = DataFrame({"group_id": [1], "name": "name"})
995+
tm.assert_frame_equal(result, expected)
996+
997+
935998
def test_execute_typeerror(sqlite_iris_engine):
936999
with pytest.raises(TypeError, match="pandas.io.sql.execute requires a connection"):
9371000
with tm.assert_produces_warning(

0 commit comments

Comments
 (0)