Skip to content

Commit 14f2ce7

Browse files
committed
Make close() idempotent for already-closed connections
Return True when ibm_db.close() or ibm_db_dbi.Connection.close() is called on an already-closed connection instead of raising. Add a DBI regression test for double-close behavior.
1 parent 100df91 commit 14f2ce7

File tree

3 files changed

+61
-20
lines changed

3 files changed

+61
-20
lines changed

ibm_db.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4274,7 +4274,8 @@ static PyObject *ibm_db_bind_param(PyObject *self, PyObject *args)
42744274
* Specifies an active DB2 client connection.
42754275
*
42764276
* ===Return Values
4277-
* Returns TRUE on success or FALSE on failure.
4277+
* Returns TRUE on success. If the connection is already closed, returns TRUE.
4278+
* On failure, an exception is raised and NULL is returned.
42784279
*/
42794280
static PyObject *ibm_db_close(PyObject *self, PyObject *args)
42804281
{
@@ -4314,9 +4315,10 @@ static PyObject *ibm_db_close(PyObject *self, PyObject *args)
43144315

43154316
if (!conn_res->handle_active)
43164317
{
4317-
LogMsg(EXCEPTION, "Connection is not active");
4318-
PyErr_SetString(PyExc_Exception, "Connection is not active");
4319-
return NULL;
4318+
LogMsg(INFO, "Connection already closed; no action required");
4319+
Py_INCREF(Py_True);
4320+
LogMsg(INFO, "exit close()");
4321+
return Py_True;
43204322
}
43214323

43224324
if (conn_res->handle_active && !conn_res->flag_pconnect)

ibm_db_dbi.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -923,20 +923,22 @@ def close(self):
923923
"""This method closes the Database connection associated with
924924
the Connection object. It takes no arguments.
925925
926+
If the connection is already closed, this method is a no-op and
927+
returns True.
928+
926929
"""
927930
LogMsg(INFO, "entry close()")
928-
self.rollback()
929-
try:
930-
if self.conn_handler is None:
931-
LogMsg(ERROR, "Connection cannot be closed; connection is no longer active.")
932-
raise ProgrammingError("Connection cannot be closed; "
933-
"connection is no longer active.")
934-
else:
931+
if self.conn_handler is None:
932+
return_value = True
933+
LogMsg(INFO, "Connection already closed; no action required.")
934+
else:
935+
self.rollback()
936+
try:
935937
return_value = ibm_db.close(self.conn_handler)
936938
LogMsg(INFO, "Connection closed.")
937-
except Exception as inst:
938-
LogMsg(EXCEPTION, f"An exception occurred while closing connection: {inst}")
939-
raise _get_exception(inst)
939+
except Exception as inst:
940+
LogMsg(EXCEPTION, f"An exception occurred while closing connection: {inst}")
941+
raise _get_exception(inst)
940942
self.conn_handler = None
941943
for index in range(len(self._cursor_list)):
942944
if (self._cursor_list[index]() != None):
@@ -969,12 +971,7 @@ def rollback(self):
969971
970972
"""
971973
LogMsg(INFO, "entry rollback()")
972-
try:
973-
return_value = ibm_db.rollback(self.conn_handler)
974-
LogMsg(INFO, "Transaction rolled back.")
975-
except Exception as inst:
976-
LogMsg(EXCEPTION, f"An exception occurred while rolling back transaction: {inst}")
977-
raise _get_exception(inst)
974+
return_value = ibm_db.rollback(self.conn_handler)
978975
LogMsg(INFO, "exit rollback()")
979976
return return_value
980977

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#
2+
# Licensed Materials - Property of IBM
3+
#
4+
# (c) Copyright IBM Corp. 2026
5+
#
6+
7+
from __future__ import print_function
8+
import sys
9+
import unittest
10+
import ibm_db_dbi
11+
import config
12+
from testfunctions import IbmDbTestFunctions
13+
14+
15+
class IbmDbTestCase(unittest.TestCase):
16+
17+
def test_072_CloseTwice_DBI(self):
18+
obj = IbmDbTestFunctions()
19+
obj.assert_expect(self.run_test_072)
20+
21+
def run_test_072(self):
22+
conn = ibm_db_dbi.connect(config.database, config.user, config.password)
23+
24+
rc1 = conn.close()
25+
rc2 = conn.close()
26+
27+
print(rc1)
28+
print(rc2)
29+
30+
#__END__
31+
#__LUW_EXPECTED__
32+
#True
33+
#True
34+
#__ZOS_EXPECTED__
35+
#True
36+
#True
37+
#__SYSTEMI_EXPECTED__
38+
#True
39+
#True
40+
#__IDS_EXPECTED__
41+
#True
42+
#True

0 commit comments

Comments
 (0)