Description
When a Firebird connection is interrupted or times out while a cursor is streaming records, firebird-driver 2.0.3 correctly raises a DatabaseError for the original connection failure.
My application catches that exception and explicitly attempts to close the cursor, roll back the transaction, close the transaction manager, and close the connection.
However, if one or more of these cleanup operations fail because the connection is already unavailable, the affected driver objects remain in a partially open state. When they are subsequently garbage-collected, their __del__() methods attempt the same cleanup again and raise additional DatabaseError exceptions.
Because these exceptions occur inside object finalizers, they cannot be caught by my application's normal try/except blocks. Python reports them through sys.unraisablehook as:
Exception ignored in: <function Cursor.__del__ ...>
Exception ignored in: <function Statement.__del__ ...>
Exception ignored in: <function Connection.__del__ ...>
This still occurs with firebird-driver 2.0.3.
Environment
- Operating system: Windows 11
- Python: 3.12.10
- firebird-driver: 2.0.3
- Firebird server: 3.0.x
- Multiprocessing start method:
spawn
- Connection failure type: connection timeout or interrupted network connection
- Observed SQLSTATE:
08006
- Observed SQLCODE:
-902
- Observed GDS codes:
335544726, error reading data from the connection
335544727, error writing data to the connection
Observed behavior
The original connection failure is caught normally:
Firebird database error while streaming table 'xxxxx':
Error reading data from the connection.;
SQLSTATE='08006';
SQLCODE=-902;
GDS=(335544726,)
Explicit cursor cleanup then also fails and is caught by the application:
Failed to close the Firebird streaming cursor:
Error writing data to the connection.;
SQLSTATE='08006';
SQLCODE=-902;
GDS=(335544727,)
After the application has caught both errors, garbage collection produces additional tracebacks:
Exception ignored in: <function Cursor.__del__ at 0x...>
Traceback (most recent call last):
File "...firebird\driver\core.py", line 3363, in __del__
self.close()
File "...firebird\driver\core.py", line 4111, in close
self._stmt.free()
File "...firebird\driver\core.py", line 3043, in free
self._istmt.free()
File "...firebird\driver\interfaces.py", line 830, in free
self._check()
File "...firebird\driver\interfaces.py", line 141, in _check
raise self.__report(DatabaseError, self.status.get_errors())
firebird.driver.types.DatabaseError:
Error writing data to the connection.
The same behavior occurs for the connection:
Exception ignored in: <function Connection.__del__ at 0x...>
Traceback (most recent call last):
File "...firebird\driver\core.py", line 1800, in __del__
self._close()
File "...firebird\driver\core.py", line 1832, in _close
self.main_transaction._finish(DefaultAction.ROLLBACK)
File "...firebird\driver\core.py", line 2632, in _finish
self.rollback()
File "...firebird\driver\core.py", line 2716, in rollback
self._tra.rollback()
File "...firebird\driver\interfaces.py", line 466, in rollback
self._check()
File "...firebird\driver\interfaces.py", line 141, in _check
raise self.__report(DatabaseError, self.status.get_errors())
firebird.driver.types.DatabaseError:
connection shutdown
A statement finalizer produces another traceback:
Exception ignored in: <function Statement.__del__ at 0x...>
Traceback (most recent call last):
File "...firebird\driver\core.py", line 3024, in __del__
self.free()
File "...firebird\driver\core.py", line 3043, in free
self._istmt.free()
File "...firebird\driver\interfaces.py", line 830, in free
self._check()
File "...firebird\driver\interfaces.py", line 141, in _check
raise self.__report(DatabaseError, self.status.get_errors())
firebird.driver.types.DatabaseError:
connection shutdown
Depending on which driver objects remain alive, the same issue may also affect TransactionManager.__del__().
Description
When a Firebird connection is interrupted or times out while a cursor is streaming records,
firebird-driver2.0.3 correctly raises aDatabaseErrorfor the original connection failure.My application catches that exception and explicitly attempts to close the cursor, roll back the transaction, close the transaction manager, and close the connection.
However, if one or more of these cleanup operations fail because the connection is already unavailable, the affected driver objects remain in a partially open state. When they are subsequently garbage-collected, their
__del__()methods attempt the same cleanup again and raise additionalDatabaseErrorexceptions.Because these exceptions occur inside object finalizers, they cannot be caught by my application's normal
try/exceptblocks. Python reports them throughsys.unraisablehookas:This still occurs with
firebird-driver2.0.3.Environment
spawn08006-902335544726, error reading data from the connection335544727, error writing data to the connectionObserved behavior
The original connection failure is caught normally:
Explicit cursor cleanup then also fails and is caught by the application:
After the application has caught both errors, garbage collection produces additional tracebacks:
The same behavior occurs for the connection:
A statement finalizer produces another traceback:
Depending on which driver objects remain alive, the same issue may also affect
TransactionManager.__del__().