diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-08-03 11:55:12 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-08-03 11:55:12 -0400 |
| commit | ddf4210552fdff864a885b1ec6aa238105b16e8f (patch) | |
| tree | ae6e1c4231385954a8dd5252ffb715b5da6f5a4b /lib/sqlalchemy | |
| parent | 5cd1d43bec73db77d26e6f663e2449e911520737 (diff) | |
| download | sqlalchemy-ddf4210552fdff864a885b1ec6aa238105b16e8f.tar.gz | |
- Calling fetchone() or similar on a result that
has already been exhausted, has been closed,
or is not a result-returning result now
raises ResourceClosedError, a subclass of
InvalidRequestError, in all cases, regardless
of backend. Previously, some DBAPIs would
raise ProgrammingError (i.e. pysqlite), others
would return None leading to downstream breakages
(i.e. MySQL-python).
- Connection, ResultProxy, as well as Session use
ResourceClosedError for all "this
connection/transaction/result is closed" types of
errors.
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/dialects/sqlite/base.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 38 | ||||
| -rw-r--r-- | lib/sqlalchemy/exc.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 5 |
4 files changed, 38 insertions, 11 deletions
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 2d9e56baf..b84b18e68 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -600,5 +600,5 @@ def _pragma_cursor(cursor): """work around SQLite issue whereby cursor.description is blank when PRAGMA returns no rows.""" if cursor.closed: - cursor._fetchone_impl = lambda: None + cursor.fetchone = lambda: None return cursor diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index cf459f9e6..684df51ca 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -908,7 +908,7 @@ class Connection(Connectable): self.__connection = self.engine.raw_connection() self.__invalid = False return self.__connection - raise exc.InvalidRequestError("This Connection is closed") + raise exc.ResourceClosedError("This Connection is closed") @property def info(self): @@ -956,7 +956,7 @@ class Connection(Connectable): """ if self.closed: - raise exc.InvalidRequestError("This Connection is closed") + raise exc.ResourceClosedError("This Connection is closed") if self.__connection.is_valid: self.__connection.invalidate(exception) @@ -2279,7 +2279,9 @@ class ResultProxy(object): if _autoclose_connection and \ self.connection.should_close_with_result: self.connection.close() - + # allow consistent errors + self.cursor = None + def __iter__(self): while True: row = self.fetchone() @@ -2361,14 +2363,32 @@ class ResultProxy(object): return self.dialect.supports_sane_multi_rowcount def _fetchone_impl(self): - return self.cursor.fetchone() + try: + return self.cursor.fetchone() + except AttributeError: + self._non_result() def _fetchmany_impl(self, size=None): - return self.cursor.fetchmany(size) + try: + return self.cursor.fetchmany(size) + except AttributeError: + self._non_result() def _fetchall_impl(self): - return self.cursor.fetchall() - + try: + return self.cursor.fetchall() + except AttributeError: + self._non_result() + + def _non_result(self): + if self._metadata is None: + raise exc.ResourceClosedError( + "This result object does not return rows. " + "It has been closed automatically.", + ) + else: + raise exc.ResourceClosedError("This result object is closed.") + def process_rows(self, rows): process_row = self._process_row metadata = self._metadata @@ -2425,7 +2445,6 @@ class ResultProxy(object): Else the cursor is automatically closed and None is returned. """ - try: row = self._fetchone_impl() if row is not None: @@ -2445,6 +2464,9 @@ class ResultProxy(object): Returns None if no row is present. """ + if self._metadata is None: + self._non_result() + try: row = self._fetchone_impl() except Exception, e: diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py index 31826f44f..1c412824c 100644 --- a/lib/sqlalchemy/exc.py +++ b/lib/sqlalchemy/exc.py @@ -60,6 +60,10 @@ class InvalidRequestError(SQLAlchemyError): """ +class ResourceClosedError(InvalidRequestError): + """An operation was requested from a connection, cursor, or other + object that's in a closed state.""" + class NoSuchColumnError(KeyError, InvalidRequestError): """A nonexistent column is requested from a ``RowProxy``.""" diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index d092375a6..8516a22de 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -235,7 +235,7 @@ class SessionTransaction(object): def _assert_is_open(self, error_msg="The transaction is closed"): if self.session is None: - raise sa_exc.InvalidRequestError(error_msg) + raise sa_exc.ResourceClosedError(error_msg) @property def _is_transaction_boundary(self): @@ -427,7 +427,8 @@ class SessionTransaction(object): return self def __exit__(self, type, value, traceback): - self._assert_is_open("Cannot end transaction context. The transaction was closed from within the context") + self._assert_is_open("Cannot end transaction context. The transaction " + "was closed from within the context") if self.session.transaction is None: return if type is None: |
