Skip to content

add try / except to address issue #10154 to_datetime, Inconsistent be… #10216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/requirements-2.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ boto=2.36.0
bottleneck=0.8.0
psycopg2=2.5.2
patsy
pymysql=0.6.1
pymysql=0.6.3
html5lib=1.0b2
beautiful-soup=4.2.1
httplib2=0.8
Expand Down
9 changes: 8 additions & 1 deletion doc/source/io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3555,9 +3555,16 @@ below and the SQLAlchemy `documentation <http://docs.sqlalchemy.org/en/rel_0_9/c
.. ipython:: python

from sqlalchemy import create_engine
# Create your connection.
# Create your engine.
engine = create_engine('sqlite:///:memory:')

If you want to manage your own connections you can pass one of those instead:

.. ipython:: python

with engine.connect() as conn, conn.begin():
data = pd.read_sql_table('data', conn)

Writing DataFrames
~~~~~~~~~~~~~~~~~~

Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.17.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Check the :ref:`API Changes <whatsnew_0170.api>` and :ref:`deprecations <whatsne
New features
~~~~~~~~~~~~

- SQL io functions now accept a SQLAlchemy connectable. (:issue:`7877`)

.. _whatsnew_0170.enhancements.other:

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ def to_hdf(self, path_or_buf, key, **kwargs):

Parameters
----------
path_or_buf : the path (string) or buffer to put the store
path_or_buf : the path (string) or HDFStore object
key : string
indentifier for the group in the store
mode : optional, {'a', 'w', 'r', 'r+'}, default 'a'
Expand Down
83 changes: 49 additions & 34 deletions pandas/io/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class DatabaseError(IOError):
_SQLALCHEMY_INSTALLED = None


def _is_sqlalchemy_engine(con):
def _is_sqlalchemy_connectable(con):
global _SQLALCHEMY_INSTALLED
if _SQLALCHEMY_INSTALLED is None:
try:
Expand All @@ -62,7 +62,7 @@ def compile_big_int_sqlite(type_, compiler, **kw):

if _SQLALCHEMY_INSTALLED:
import sqlalchemy
return isinstance(con, sqlalchemy.engine.Engine)
return isinstance(con, sqlalchemy.engine.Connectable)
else:
return False

Expand Down Expand Up @@ -139,7 +139,7 @@ def execute(sql, con, cur=None, params=None):
----------
sql : string
Query to be executed
con : SQLAlchemy engine or sqlite3 DBAPI2 connection
con : SQLAlchemy connectable(engine/connection) or sqlite3 DBAPI2 connection
Using SQLAlchemy makes it possible to use any DB supported by that
library.
If a DBAPI2 object, only sqlite3 is supported.
Expand Down Expand Up @@ -282,14 +282,14 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
chunksize=None):
"""Read SQL database table into a DataFrame.

Given a table name and an SQLAlchemy engine, returns a DataFrame.
Given a table name and an SQLAlchemy connectable, returns a DataFrame.
This function does not support DBAPI connections.

Parameters
----------
table_name : string
Name of SQL table in database
con : SQLAlchemy engine
con : SQLAlchemy connectable
Sqlite DBAPI connection mode not supported
schema : string, default None
Name of SQL schema in database to query (if database flavor
Expand Down Expand Up @@ -328,9 +328,9 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
read_sql

"""
if not _is_sqlalchemy_engine(con):
if not _is_sqlalchemy_connectable(con):
raise NotImplementedError("read_sql_table only supported for "
"SQLAlchemy engines.")
"SQLAlchemy connectable.")
import sqlalchemy
from sqlalchemy.schema import MetaData
meta = MetaData(con, schema=schema)
Expand Down Expand Up @@ -362,7 +362,7 @@ def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
----------
sql : string
SQL query to be executed
con : SQLAlchemy engine or sqlite3 DBAPI2 connection
con : SQLAlchemy connectable(engine/connection) or sqlite3 DBAPI2 connection
Using SQLAlchemy makes it possible to use any DB supported by that
library.
If a DBAPI2 object, only sqlite3 is supported.
Expand Down Expand Up @@ -420,7 +420,7 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
----------
sql : string
SQL query to be executed or database table name.
con : SQLAlchemy engine or DBAPI2 connection (fallback mode)
con : SQLAlchemy connectable(engine/connection) or DBAPI2 connection (fallback mode)
Using SQLAlchemy makes it possible to use any DB supported by that
library.
If a DBAPI2 object, only sqlite3 is supported.
Expand Down Expand Up @@ -504,14 +504,14 @@ def to_sql(frame, name, con, flavor='sqlite', schema=None, if_exists='fail',
frame : DataFrame
name : string
Name of SQL table
con : SQLAlchemy engine or sqlite3 DBAPI2 connection
con : SQLAlchemy connectable(engine/connection) or sqlite3 DBAPI2 connection
Using SQLAlchemy makes it possible to use any DB supported by that
library.
If a DBAPI2 object, only sqlite3 is supported.
flavor : {'sqlite', 'mysql'}, default 'sqlite'
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
The flavor of SQL to use. Ignored when using SQLAlchemy connectable.
'mysql' is deprecated and will be removed in future versions, but it
will be further supported through SQLAlchemy engines.
will be further supported through SQLAlchemy connectables.
schema : string, default None
Name of SQL schema in database to write to (if database flavor
supports this). If None, use default schema (default).
Expand Down Expand Up @@ -557,14 +557,14 @@ def has_table(table_name, con, flavor='sqlite', schema=None):
----------
table_name: string
Name of SQL table
con: SQLAlchemy engine or sqlite3 DBAPI2 connection
con: SQLAlchemy connectable(engine/connection) or sqlite3 DBAPI2 connection
Using SQLAlchemy makes it possible to use any DB supported by that
library.
If a DBAPI2 object, only sqlite3 is supported.
flavor: {'sqlite', 'mysql'}, default 'sqlite'
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
The flavor of SQL to use. Ignored when using SQLAlchemy connectable.
'mysql' is deprecated and will be removed in future versions, but it
will be further supported through SQLAlchemy engines.
will be further supported through SQLAlchemy connectables.
schema : string, default None
Name of SQL schema in database to write to (if database flavor supports
this). If None, use default schema (default).
Expand All @@ -581,7 +581,7 @@ def has_table(table_name, con, flavor='sqlite', schema=None):

_MYSQL_WARNING = ("The 'mysql' flavor with DBAPI connection is deprecated "
"and will be removed in future versions. "
"MySQL will be further supported with SQLAlchemy engines.")
"MySQL will be further supported with SQLAlchemy connectables.")


def pandasSQL_builder(con, flavor=None, schema=None, meta=None,
Expand All @@ -592,7 +592,7 @@ def pandasSQL_builder(con, flavor=None, schema=None, meta=None,
"""
# When support for DBAPI connections is removed,
# is_cursor should not be necessary.
if _is_sqlalchemy_engine(con):
if _is_sqlalchemy_connectable(con):
return SQLDatabase(con, schema=schema, meta=meta)
else:
if flavor == 'mysql':
Expand Down Expand Up @@ -637,7 +637,7 @@ def exists(self):

def sql_schema(self):
from sqlalchemy.schema import CreateTable
return str(CreateTable(self.table).compile(self.pd_sql.engine))
return str(CreateTable(self.table).compile(self.pd_sql.connectable))

def _execute_create(self):
# Inserting table into database, add to MetaData object
Expand Down Expand Up @@ -982,11 +982,11 @@ class PandasSQL(PandasObject):
"""

def read_sql(self, *args, **kwargs):
raise ValueError("PandasSQL must be created with an SQLAlchemy engine"
raise ValueError("PandasSQL must be created with an SQLAlchemy connectable"
" or connection+sql flavor")

def to_sql(self, *args, **kwargs):
raise ValueError("PandasSQL must be created with an SQLAlchemy engine"
raise ValueError("PandasSQL must be created with an SQLAlchemy connectable"
" or connection+sql flavor")


Expand All @@ -997,8 +997,8 @@ class SQLDatabase(PandasSQL):

Parameters
----------
engine : SQLAlchemy engine
Engine to connect with the database. Using SQLAlchemy makes it
engine : SQLAlchemy connectable
Connectable to connect with the database. Using SQLAlchemy makes it
possible to use any DB supported by that library.
schema : string, default None
Name of SQL schema in database to write to (if database flavor
Expand All @@ -1011,19 +1011,24 @@ class SQLDatabase(PandasSQL):
"""

def __init__(self, engine, schema=None, meta=None):
self.engine = engine
self.connectable = engine
if not meta:
from sqlalchemy.schema import MetaData
meta = MetaData(self.engine, schema=schema)
meta = MetaData(self.connectable, schema=schema)

self.meta = meta

@contextmanager
def run_transaction(self):
return self.engine.begin()
with self.connectable.begin() as tx:
if hasattr(tx, 'execute'):
yield tx
else:
yield self.connectable

def execute(self, *args, **kwargs):
"""Simple passthrough to SQLAlchemy engine"""
return self.engine.execute(*args, **kwargs)
"""Simple passthrough to SQLAlchemy connectable"""
return self.connectable.execute(*args, **kwargs)

def read_table(self, table_name, index_col=None, coerce_float=True,
parse_dates=None, columns=None, schema=None,
Expand Down Expand Up @@ -1191,7 +1196,13 @@ def to_sql(self, frame, name, if_exists='fail', index=True,
table.create()
table.insert(chunksize)
# check for potentially case sensitivity issues (GH7815)
if name not in self.engine.table_names(schema=schema or self.meta.schema):
engine = self.connectable.engine
with self.connectable.connect() as conn:
table_names = engine.table_names(
schema=schema or self.meta.schema,
connection=conn,
)
if name not in table_names:
warnings.warn("The provided table name '{0}' is not found exactly "
"as such in the database after writing the table, "
"possibly due to case sensitivity issues. Consider "
Expand All @@ -1202,7 +1213,11 @@ def tables(self):
return self.meta.tables

def has_table(self, name, schema=None):
return self.engine.has_table(name, schema or self.meta.schema)
return self.connectable.run_callable(
self.connectable.dialect.has_table,
name,
schema or self.meta.schema,
)

def get_table(self, table_name, schema=None):
schema = schema or self.meta.schema
Expand All @@ -1221,7 +1236,7 @@ def get_table(self, table_name, schema=None):

def drop_table(self, table_name, schema=None):
schema = schema or self.meta.schema
if self.engine.has_table(table_name, schema):
if self.has_table(table_name, schema):
self.meta.reflect(only=[table_name], schema=schema)
self.get_table(table_name, schema).drop()
self.meta.clear()
Expand Down Expand Up @@ -1610,12 +1625,12 @@ def get_schema(frame, name, flavor='sqlite', keys=None, con=None, dtype=None):
name : string
name of SQL table
flavor : {'sqlite', 'mysql'}, default 'sqlite'
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
The flavor of SQL to use. Ignored when using SQLAlchemy connectable.
'mysql' is deprecated and will be removed in future versions, but it
will be further supported through SQLAlchemy engines.
keys : string or sequence
columns to use a primary key
con: an open SQL database connection object or an SQLAlchemy engine
con: an open SQL database connection object or a SQLAlchemy connectable
Using SQLAlchemy makes it possible to use any DB supported by that
library.
If a DBAPI2 object, only sqlite3 is supported.
Expand Down Expand Up @@ -1673,8 +1688,8 @@ def write_frame(frame, name, con, flavor='sqlite', if_exists='fail', **kwargs):

- With ``to_sql`` the index is written to the sql database by default. To
keep the behaviour this function you need to specify ``index=False``.
- The new ``to_sql`` function supports sqlalchemy engines to work with
different sql flavors.
- The new ``to_sql`` function supports sqlalchemy connectables to work
with different sql flavors.

See also
--------
Expand Down
Loading