Skip to content

Commit ec6a57b

Browse files
authored
Merge pull request #67 from fdcastel/fix-issue-65
Fix for #65 Segfault when closing cursor/connection after Statement.free()
2 parents ea14774 + 447b5fd commit ec6a57b

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

src/firebird/driver/core.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3965,7 +3965,13 @@ def _execute(self, operation: str | Statement,
39653965
in_meta.release()
39663966
def _clear(self) -> None:
39673967
if self._result is not None:
3968-
self._result.close()
3968+
if self._stmt is not None and self._stmt._istmt is not None:
3969+
self._result.close()
3970+
else:
3971+
# Statement was already freed; the result set is invalidated
3972+
# at the Firebird API level, so we must not call close() on it.
3973+
# Also prevent __del__ from calling release() on the invalid interface.
3974+
self._result._refcnt = 0
39693975
self._result = None
39703976
self._name = None
39713977
self._last_fetch_status = None

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def pytest_configure(config):
122122
client_lib = Path(client_lib)
123123
if not client_lib.is_file():
124124
pytest.exit(f"Client library '{client_lib}' not found!")
125-
driver_config.fb_client_library.value = client_lib
125+
driver_config.fb_client_library.value = str(client_lib)
126126
#
127127
if host := config.getoption('host'):
128128
_vars_['host'] = host

tests/test_issues.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,29 @@ def test_issue_53(db_connection):
3939
numeric_val_exponent = numeric_val.as_tuple()[2]
4040
db_connection.commit()
4141
assert numeric_val_exponent == -2
42+
43+
def test_issue_65_prepare_ctx_mgr(db_connection):
44+
"""Freeing a Statement via context manager must not crash when cursor/connection closes."""
45+
with db_connection.cursor() as cur:
46+
with cur.prepare('select count(*) from country where 1 < ?') as stmt:
47+
row = cur.execute(stmt, (2,)).fetchone()
48+
assert row is not None
49+
50+
def test_issue_65_free_then_cursor_close(db_connection):
51+
"""Explicit stmt.free() followed by cursor.close() must not crash."""
52+
cur = db_connection.cursor()
53+
stmt = cur.prepare('select count(*) from country where 1 < ?')
54+
row = cur.execute(stmt, (2,)).fetchone()
55+
assert row is not None
56+
stmt.free()
57+
cur.close()
58+
59+
def test_issue_65_free_then_conn_close(dsn):
60+
"""stmt.free() followed by connection close must not crash."""
61+
from firebird.driver import connect
62+
with connect(dsn) as conn:
63+
cur = conn.cursor()
64+
stmt = cur.prepare('select count(*) from country where 1 < ?')
65+
row = cur.execute(stmt, (2,)).fetchone()
66+
assert row is not None
67+
stmt.free()

0 commit comments

Comments
 (0)