diff --git a/py-polars/polars/io/database/functions.py b/py-polars/polars/io/database/functions.py index eb4ebdac5af6..87d0f44c2a06 100644 --- a/py-polars/polars/io/database/functions.py +++ b/py-polars/polars/io/database/functions.py @@ -3,10 +3,8 @@ import re from typing import TYPE_CHECKING, Any, Iterable, Literal, overload -from polars._utils.deprecation import issue_deprecation_warning from polars.datatypes import N_INFER_DEFAULT from polars.dependencies import import_optional -from polars.exceptions import InvalidOperationError from polars.io.database._cursor_proxies import ODBCCursorProxy from polars.io.database._executor import ConnectionExecutor @@ -37,7 +35,6 @@ def read_database( schema_overrides: SchemaDict | None = ..., infer_schema_length: int | None = ..., execute_options: dict[str, Any] | None = ..., - **kwargs: Any, ) -> DataFrame: ... @@ -51,7 +48,6 @@ def read_database( schema_overrides: SchemaDict | None = ..., infer_schema_length: int | None = ..., execute_options: dict[str, Any] | None = ..., - **kwargs: Any, ) -> Iterable[DataFrame]: ... @@ -65,11 +61,10 @@ def read_database( schema_overrides: SchemaDict | None = ..., infer_schema_length: int | None = ..., execute_options: dict[str, Any] | None = ..., - **kwargs: Any, ) -> DataFrame | Iterable[DataFrame]: ... -def read_database( # noqa: D417 +def read_database( query: str | Selectable, connection: ConnectionOrCursor | str, *, @@ -78,7 +73,6 @@ def read_database( # noqa: D417 schema_overrides: SchemaDict | None = None, infer_schema_length: int | None = N_INFER_DEFAULT, execute_options: dict[str, Any] | None = None, - **kwargs: Any, ) -> DataFrame | Iterable[DataFrame]: """ Read the results of a SQL query into a DataFrame, given a connection object. @@ -147,7 +141,7 @@ def read_database( # noqa: D417 * The `read_database_uri` function can be noticeably faster than `read_database` if you are using a SQLAlchemy or DBAPI2 connection, as `connectorx` and `adbc` - optimises translation of the result set into Arrow format. Note that you can + optimise translation of the result set into Arrow format. Note that you can determine a connection's URI from a SQLAlchemy engine object by calling `conn.engine.url.render_as_string(hide_password=False)`. @@ -155,9 +149,10 @@ def read_database( # noqa: D417 query then that cursor will be automatically closed when the query completes; however, Polars will *never* close any other open connection or cursor. - * We are able to support more than just relational databases and SQL queries - through this function. For example, we can load graph database results from - a `KùzuDB` connection in conjunction with a Cypher query. + * Polars is able to support more than just relational databases and SQL queries + through this function. For example, you can load local graph database results + from a `KùzuDB` connection in conjunction with a Cypher query, or use SurrealQL + with SurrealDB. See Also -------- @@ -245,30 +240,13 @@ def read_database( # noqa: D417 err_suffix="package", ) connection = ODBCCursorProxy(connection) + elif "://" in connection: + # otherwise looks like a mistaken call to read_database_uri + msg = "Use of string URI is invalid here; call `read_database_uri` instead" + raise ValueError(msg) else: - # otherwise looks like a call to read_database_uri - issue_deprecation_warning( - message="Use of a string URI with 'read_database' is deprecated; use `read_database_uri` instead", - version="0.19.0", - ) - if iter_batches or batch_size: - msg = "Batch parameters are not supported for `read_database_uri`" - raise InvalidOperationError(msg) - if not isinstance(query, (list, str)): - msg = f"`read_database_uri` expects one or more string queries; found {type(query)}" - raise TypeError(msg) - return read_database_uri( - query, - uri=connection, - schema_overrides=schema_overrides, - **kwargs, - ) - - # note: can remove this check (and **kwargs) once we drop the - # pass-through deprecation support for read_database_uri - if kwargs: - msg = f"`read_database` **kwargs only exist for passthrough to `read_database_uri`: found {kwargs!r}" - raise ValueError(msg) + msg = "unable to identify string connection as valid ODBC (no driver)" + raise ValueError(msg) # return frame from arbitrary connections using the executor abstraction with ConnectionExecutor(connection) as cx: diff --git a/py-polars/tests/unit/io/database/test_read.py b/py-polars/tests/unit/io/database/test_read.py index 0e0bc21389aa..2fb495203def 100644 --- a/py-polars/tests/unit/io/database/test_read.py +++ b/py-polars/tests/unit/io/database/test_read.py @@ -593,17 +593,6 @@ def test_read_database_mocked( ), id="Invalid statement type", ), - pytest.param( - *ExceptionTestParams( - read_method="read_database", - query="SELECT * FROM test_data", - protocol=sqlite3.connect(":memory:"), - errclass=ValueError, - errmsg=r"`read_database` \*\*kwargs only exist for passthrough to `read_database_uri`", - kwargs={"partition_on": "id"}, - ), - id="Invalid kwargs", - ), pytest.param( *ExceptionTestParams( read_method="read_database", @@ -621,12 +610,23 @@ def test_read_database_mocked( engine="adbc", query="SELECT * FROM test_data", protocol=sqlite3.connect(":memory:"), - errclass=ValueError, - errmsg=r"`read_database` \*\*kwargs only exist for passthrough to `read_database_uri`", + errclass=TypeError, + errmsg=r"unexpected keyword argument 'partition_on'", kwargs={"partition_on": "id"}, ), id="Invalid kwargs", ), + pytest.param( + *ExceptionTestParams( + read_method="read_database", + engine="adbc", + query="SELECT * FROM test_data", + protocol="{not:a, valid:odbc_string}", + errclass=ValueError, + errmsg=r"unable to identify string connection as valid ODBC", + ), + id="Invalid ODBC string", + ), ], ) def test_read_database_exceptions(