diff --git a/src/firebird/driver/core.py b/src/firebird/driver/core.py index a5154ee..0615987 100644 --- a/src/firebird/driver/core.py +++ b/src/firebird/driver/core.py @@ -3965,7 +3965,13 @@ def _execute(self, operation: str | Statement, in_meta.release() def _clear(self) -> None: if self._result is not None: - self._result.close() + if self._stmt is not None and self._stmt._istmt is not None: + self._result.close() + else: + # Statement was already freed; the result set is invalidated + # at the Firebird API level, so we must not call close() on it. + # Also prevent __del__ from calling release() on the invalid interface. + self._result._refcnt = 0 self._result = None self._name = None self._last_fetch_status = None diff --git a/tests/conftest.py b/tests/conftest.py index 45776d9..14d72e9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -122,7 +122,7 @@ def pytest_configure(config): client_lib = Path(client_lib) if not client_lib.is_file(): pytest.exit(f"Client library '{client_lib}' not found!") - driver_config.fb_client_library.value = client_lib + driver_config.fb_client_library.value = str(client_lib) # if host := config.getoption('host'): _vars_['host'] = host diff --git a/tests/test_issues.py b/tests/test_issues.py index 8f27d58..cdfc3f2 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -39,3 +39,29 @@ def test_issue_53(db_connection): numeric_val_exponent = numeric_val.as_tuple()[2] db_connection.commit() assert numeric_val_exponent == -2 + +def test_issue_65_prepare_ctx_mgr(db_connection): + """Freeing a Statement via context manager must not crash when cursor/connection closes.""" + with db_connection.cursor() as cur: + with cur.prepare('select count(*) from country where 1 < ?') as stmt: + row = cur.execute(stmt, (2,)).fetchone() + assert row is not None + +def test_issue_65_free_then_cursor_close(db_connection): + """Explicit stmt.free() followed by cursor.close() must not crash.""" + cur = db_connection.cursor() + stmt = cur.prepare('select count(*) from country where 1 < ?') + row = cur.execute(stmt, (2,)).fetchone() + assert row is not None + stmt.free() + cur.close() + +def test_issue_65_free_then_conn_close(dsn): + """stmt.free() followed by connection close must not crash.""" + from firebird.driver import connect + with connect(dsn) as conn: + cur = conn.cursor() + stmt = cur.prepare('select count(*) from country where 1 < ?') + row = cur.execute(stmt, (2,)).fetchone() + assert row is not None + stmt.free()