|
47 | 47 | from pandas.util.version import Version |
48 | 48 |
|
49 | 49 |
|
50 | | -class SQLAlchemyRequired(ImportError): |
51 | | - pass |
52 | | - |
53 | | - |
54 | 50 | class DatabaseError(IOError): |
55 | 51 | pass |
56 | 52 |
|
57 | 53 |
|
58 | 54 | # ----------------------------------------------------------------------------- |
59 | 55 | # -- Helper functions |
60 | 56 |
|
61 | | -_SQLALCHEMY_INSTALLED: bool | None = None |
62 | | - |
63 | | - |
64 | | -def _is_sqlalchemy_connectable(con): |
65 | | - global _SQLALCHEMY_INSTALLED |
66 | | - if _SQLALCHEMY_INSTALLED is None: |
67 | | - try: |
68 | | - import sqlalchemy |
69 | | - |
70 | | - _SQLALCHEMY_INSTALLED = True |
71 | | - except ImportError: |
72 | | - _SQLALCHEMY_INSTALLED = False |
73 | | - |
74 | | - if _SQLALCHEMY_INSTALLED: |
75 | | - import sqlalchemy # noqa: F811 |
76 | | - |
77 | | - return isinstance(con, sqlalchemy.engine.Connectable) |
78 | | - else: |
79 | | - return False |
80 | | - |
81 | 57 |
|
82 | 58 | def _gt14() -> bool: |
83 | 59 | """ |
@@ -303,21 +279,14 @@ def read_sql_table( |
303 | 279 | -------- |
304 | 280 | >>> pd.read_sql_table('table_name', 'postgres:///db_name') # doctest:+SKIP |
305 | 281 | """ |
306 | | - con = _engine_builder(con) |
307 | | - if not _is_sqlalchemy_connectable(con): |
308 | | - raise NotImplementedError( |
309 | | - "read_sql_table only supported for SQLAlchemy connectable." |
310 | | - ) |
311 | | - import sqlalchemy |
312 | | - from sqlalchemy.schema import MetaData |
| 282 | + from sqlalchemy.exc import InvalidRequestError |
313 | 283 |
|
314 | | - meta = MetaData(con, schema=schema) |
| 284 | + pandas_sql = pandasSQL_builder(con, schema=schema) |
315 | 285 | try: |
316 | | - meta.reflect(only=[table_name], views=True) |
317 | | - except sqlalchemy.exc.InvalidRequestError as err: |
| 286 | + pandas_sql.meta.reflect(only=[table_name], views=True) |
| 287 | + except InvalidRequestError as err: |
318 | 288 | raise ValueError(f"Table {table_name} not found") from err |
319 | 289 |
|
320 | | - pandas_sql = SQLDatabase(con, meta=meta) |
321 | 290 | table = pandas_sql.read_table( |
322 | 291 | table_name, |
323 | 292 | index_col=index_col, |
@@ -752,37 +721,29 @@ def has_table(table_name: str, con, schema: str | None = None): |
752 | 721 | table_exists = has_table |
753 | 722 |
|
754 | 723 |
|
755 | | -def _engine_builder(con): |
756 | | - """ |
757 | | - Returns a SQLAlchemy engine from a URI (if con is a string) |
758 | | - else it just return con without modifying it. |
759 | | - """ |
760 | | - global _SQLALCHEMY_INSTALLED |
761 | | - if isinstance(con, str): |
762 | | - try: |
763 | | - import sqlalchemy |
764 | | - except ImportError: |
765 | | - _SQLALCHEMY_INSTALLED = False |
766 | | - else: |
767 | | - con = sqlalchemy.create_engine(con) |
768 | | - return con |
769 | | - |
770 | | - return con |
771 | | - |
772 | | - |
773 | | -def pandasSQL_builder(con, schema: str | None = None, meta=None): |
| 724 | +def pandasSQL_builder(con, schema: str | None = None): |
774 | 725 | """ |
775 | 726 | Convenience function to return the correct PandasSQL subclass based on the |
776 | 727 | provided parameters. |
777 | 728 | """ |
778 | | - con = _engine_builder(con) |
779 | | - if _is_sqlalchemy_connectable(con): |
780 | | - return SQLDatabase(con, schema=schema, meta=meta) |
781 | | - elif isinstance(con, str): |
782 | | - raise ImportError("Using URI string without sqlalchemy installed.") |
783 | | - else: |
| 729 | + import sqlite3 |
| 730 | + |
| 731 | + if isinstance(con, sqlite3.Connection) or con is None: |
784 | 732 | return SQLiteDatabase(con) |
785 | 733 |
|
| 734 | + sqlalchemy = import_optional_dependency("sqlalchemy") |
| 735 | + |
| 736 | + if isinstance(con, str): |
| 737 | + con = sqlalchemy.create_engine(con) |
| 738 | + |
| 739 | + if isinstance(con, sqlalchemy.engine.Connectable): |
| 740 | + return SQLDatabase(con, schema=schema) |
| 741 | + |
| 742 | + raise ValueError( |
| 743 | + "pandas only support SQLAlchemy connectable(engine/connection) or" |
| 744 | + "database string URI or sqlite3 DBAPI2 connection" |
| 745 | + ) |
| 746 | + |
786 | 747 |
|
787 | 748 | class SQLTable(PandasObject): |
788 | 749 | """ |
@@ -1387,21 +1348,14 @@ class SQLDatabase(PandasSQL): |
1387 | 1348 | schema : string, default None |
1388 | 1349 | Name of SQL schema in database to write to (if database flavor |
1389 | 1350 | supports this). If None, use default schema (default). |
1390 | | - meta : SQLAlchemy MetaData object, default None |
1391 | | - If provided, this MetaData object is used instead of a newly |
1392 | | - created. This allows to specify database flavor specific |
1393 | | - arguments in the MetaData object. |
1394 | 1351 |
|
1395 | 1352 | """ |
1396 | 1353 |
|
1397 | | - def __init__(self, engine, schema: str | None = None, meta=None): |
1398 | | - self.connectable = engine |
1399 | | - if not meta: |
1400 | | - from sqlalchemy.schema import MetaData |
1401 | | - |
1402 | | - meta = MetaData(self.connectable, schema=schema) |
| 1354 | + def __init__(self, engine, schema: str | None = None): |
| 1355 | + from sqlalchemy.schema import MetaData |
1403 | 1356 |
|
1404 | | - self.meta = meta |
| 1357 | + self.connectable = engine |
| 1358 | + self.meta = MetaData(self.connectable, schema=schema) |
1405 | 1359 |
|
1406 | 1360 | @contextmanager |
1407 | 1361 | def run_transaction(self): |
|
0 commit comments