Skip to content

Commit 8131b20

Browse files
SQL: don't reflect full database GH7396
1 parent 4538372 commit 8131b20

File tree

2 files changed

+44
-18
lines changed

2 files changed

+44
-18
lines changed

pandas/io/sql.py

+26-18
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ def _parse_date_columns(data_frame, parse_dates):
7676
return data_frame
7777

7878

79+
def _is_sqlalchemy_engine(con):
80+
try:
81+
import sqlalchemy
82+
if isinstance(con, sqlalchemy.engine.Engine):
83+
return True
84+
else:
85+
return False
86+
except ImportError:
87+
return False
88+
89+
7990
def execute(sql, con, cur=None, params=None):
8091
"""
8192
Execute the given SQL query using the provided connection object.
@@ -262,7 +273,15 @@ def read_sql_table(table_name, con, index_col=None, coerce_float=True,
262273
263274
264275
"""
265-
pandas_sql = PandasSQLAlchemy(con)
276+
import sqlalchemy
277+
from sqlalchemy.schema import MetaData
278+
meta = MetaData(con)
279+
try:
280+
meta.reflect(only=[table_name])
281+
except sqlalchemy.exc.InvalidRequestError:
282+
raise ValueError("Table %s not found" % table_name)
283+
284+
pandas_sql = PandasSQLAlchemy(con, meta=meta)
266285
table = pandas_sql.read_table(
267286
table_name, index_col=index_col, coerce_float=coerce_float,
268287
parse_dates=parse_dates, columns=columns)
@@ -380,6 +399,7 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
380399
coerce_float=coerce_float, parse_dates=parse_dates)
381400

382401
if pandas_sql.has_table(sql):
402+
pandas_sql.meta.reflect(only=[sql])
383403
return pandas_sql.read_table(
384404
sql, index_col=index_col, coerce_float=coerce_float,
385405
parse_dates=parse_dates, columns=columns)
@@ -471,17 +491,9 @@ def pandasSQL_builder(con, flavor=None, meta=None, is_cursor=False):
471491
"""
472492
# When support for DBAPI connections is removed,
473493
# is_cursor should not be necessary.
474-
try:
475-
import sqlalchemy
476-
477-
if isinstance(con, sqlalchemy.engine.Engine):
478-
return PandasSQLAlchemy(con, meta=meta)
479-
else:
480-
if flavor == 'mysql':
481-
warnings.warn(_MYSQL_WARNING, FutureWarning)
482-
return PandasSQLLegacy(con, flavor, is_cursor=is_cursor)
483-
484-
except ImportError:
494+
if _is_sqlalchemy_engine(con):
495+
return PandasSQLAlchemy(con, meta=meta)
496+
else:
485497
if flavor == 'mysql':
486498
warnings.warn(_MYSQL_WARNING, FutureWarning)
487499
return PandasSQLLegacy(con, flavor, is_cursor=is_cursor)
@@ -767,7 +779,6 @@ def __init__(self, engine, meta=None):
767779
if not meta:
768780
from sqlalchemy.schema import MetaData
769781
meta = MetaData(self.engine)
770-
meta.reflect(self.engine)
771782

772783
self.meta = meta
773784

@@ -812,19 +823,16 @@ def tables(self):
812823
return self.meta.tables
813824

814825
def has_table(self, name):
815-
if self.meta.tables.get(name) is not None:
816-
return True
817-
else:
818-
return False
826+
return self.engine.has_table(name)
819827

820828
def get_table(self, table_name):
821829
return self.meta.tables.get(table_name)
822830

823831
def drop_table(self, table_name):
824832
if self.engine.has_table(table_name):
833+
self.meta.reflect(only=[table_name])
825834
self.get_table(table_name).drop()
826835
self.meta.clear()
827-
self.meta.reflect()
828836

829837
def _create_sql_schema(self, frame, table_name):
830838
table = PandasSQLTable(table_name, self, frame=frame)

pandas/io/tests/test_sql.py

+18
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,22 @@ def test_read_sql_delegate(self):
623623
iris_frame2 = sql.read_sql('iris', self.conn)
624624
tm.assert_frame_equal(iris_frame1, iris_frame2)
625625

626+
def test_not_reflect_all_tables(self):
627+
# create invalid table
628+
qry = """CREATE TABLE invalid (x INTEGER, y UNKNOWN);"""
629+
self.conn.execute(qry)
630+
qry = """CREATE TABLE other_table (x INTEGER, y INTEGER);"""
631+
self.conn.execute(qry)
632+
633+
with warnings.catch_warnings(record=True) as w:
634+
# Cause all warnings to always be triggered.
635+
warnings.simplefilter("always")
636+
# Trigger a warning.
637+
sql.read_sql_table('other_table', self.conn)
638+
sql.read_sql_query('SELECT * FROM other_table', self.conn)
639+
# Verify some things
640+
self.assertEqual(len(w), 0, "Warning triggered for other table")
641+
626642

627643
class TestSQLLegacyApi(_TestSQLApi):
628644
"""
@@ -736,6 +752,8 @@ def setup_connect(self):
736752
try:
737753
self.conn = self.connect()
738754
self.pandasSQL = sql.PandasSQLAlchemy(self.conn)
755+
# to test if connection can be made:
756+
self.conn.connect()
739757
except sqlalchemy.exc.OperationalError:
740758
raise nose.SkipTest("Can't connect to {0} server".format(self.flavor))
741759

0 commit comments

Comments
 (0)