Skip to content

Commit a677217

Browse files
Merge pull request #10666 from scls19fr/issue_10654
read_sql/to_sql can accept database URI as con parameter
2 parents 18928c8 + 3793da0 commit a677217

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

doc/source/whatsnew/v0.17.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Releasing of the GIL could benefit an application that uses threads for user int
6060
Other enhancements
6161
^^^^^^^^^^^^^^^^^^
6262

63+
- `read_sql` and `to_sql` can accept database URI as con parameter (:issue:`10214`)
64+
6365
- Enable `read_hdf` to be used without specifying a key when the HDF file contains a single dataset (:issue:`10443`)
6466

6567
- ``DatetimeIndex`` can be instantiated using strings contains ``NaT`` (:issue:`7599`)

pandas/io/sql.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
289289
----------
290290
table_name : string
291291
Name of SQL table in database
292-
con : SQLAlchemy connectable
292+
con : SQLAlchemy connectable (or database string URI)
293293
Sqlite DBAPI connection mode not supported
294294
schema : string, default None
295295
Name of SQL schema in database to query (if database flavor
@@ -328,6 +328,8 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
328328
read_sql
329329
330330
"""
331+
332+
con = _engine_builder(con)
331333
if not _is_sqlalchemy_connectable(con):
332334
raise NotImplementedError("read_sql_table only supported for "
333335
"SQLAlchemy connectable.")
@@ -362,7 +364,8 @@ def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
362364
----------
363365
sql : string
364366
SQL query to be executed
365-
con : SQLAlchemy connectable(engine/connection) or sqlite3 DBAPI2 connection
367+
con : SQLAlchemy connectable(engine/connection) or database string URI
368+
or sqlite3 DBAPI2 connection
366369
Using SQLAlchemy makes it possible to use any DB supported by that
367370
library.
368371
If a DBAPI2 object, only sqlite3 is supported.
@@ -420,7 +423,8 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
420423
----------
421424
sql : string
422425
SQL query to be executed or database table name.
423-
con : SQLAlchemy connectable(engine/connection) or DBAPI2 connection (fallback mode)
426+
con : SQLAlchemy connectable(engine/connection) or database string URI
427+
or DBAPI2 connection (fallback mode)
424428
Using SQLAlchemy makes it possible to use any DB supported by that
425429
library.
426430
If a DBAPI2 object, only sqlite3 is supported.
@@ -504,7 +508,8 @@ def to_sql(frame, name, con, flavor='sqlite', schema=None, if_exists='fail',
504508
frame : DataFrame
505509
name : string
506510
Name of SQL table
507-
con : SQLAlchemy connectable(engine/connection) or sqlite3 DBAPI2 connection
511+
con : SQLAlchemy connectable(engine/connection) or database string URI
512+
or sqlite3 DBAPI2 connection
508513
Using SQLAlchemy makes it possible to use any DB supported by that
509514
library.
510515
If a DBAPI2 object, only sqlite3 is supported.
@@ -584,6 +589,22 @@ def has_table(table_name, con, flavor='sqlite', schema=None):
584589
"MySQL will be further supported with SQLAlchemy connectables.")
585590

586591

592+
def _engine_builder(con):
593+
"""
594+
Returns a SQLAlchemy engine from a URI (if con is a string)
595+
else it just return con without modifying it
596+
"""
597+
if isinstance(con, string_types):
598+
try:
599+
import sqlalchemy
600+
con = sqlalchemy.create_engine(con)
601+
return con
602+
603+
except ImportError:
604+
_SQLALCHEMY_INSTALLED = False
605+
606+
return con
607+
587608
def pandasSQL_builder(con, flavor=None, schema=None, meta=None,
588609
is_cursor=False):
589610
"""
@@ -592,6 +613,7 @@ def pandasSQL_builder(con, flavor=None, schema=None, meta=None,
592613
"""
593614
# When support for DBAPI connections is removed,
594615
# is_cursor should not be necessary.
616+
con = _engine_builder(con)
595617
if _is_sqlalchemy_connectable(con):
596618
return SQLDatabase(con, schema=schema, meta=meta)
597619
else:

pandas/io/tests/test_sql.py

+17
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,23 @@ def test_sqlalchemy_type_mapping(self):
918918
table = sql.SQLTable("test_type", db, frame=df)
919919
self.assertTrue(isinstance(table.table.c['time'].type, sqltypes.DateTime))
920920

921+
def test_to_sql_read_sql_with_database_uri(self):
922+
923+
# Test read_sql and .to_sql method with a database URI (GH10654)
924+
test_frame1 = self.test_frame1
925+
#db_uri = 'sqlite:///:memory:' # raises sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "iris": syntax error [SQL: 'iris']
926+
with tm.ensure_clean() as name:
927+
db_uri = 'sqlite:///' + name
928+
table = 'iris'
929+
test_frame1.to_sql(table, db_uri, if_exists='replace', index=False)
930+
test_frame2 = sql.read_sql(table, db_uri)
931+
test_frame3 = sql.read_sql_table(table, db_uri)
932+
query = 'SELECT * FROM iris'
933+
test_frame4 = sql.read_sql_query(query, db_uri)
934+
tm.assert_frame_equal(test_frame1, test_frame2)
935+
tm.assert_frame_equal(test_frame1, test_frame3)
936+
tm.assert_frame_equal(test_frame1, test_frame4)
937+
921938

922939
class _EngineToConnMixin(object):
923940
"""

0 commit comments

Comments
 (0)