Skip to content

Commit a20e756

Browse files
Fixed bug with some databases when a connection is killed.
1 parent 9f82725 commit a20e756

File tree

5 files changed

+32
-33
lines changed

5 files changed

+32
-33
lines changed

doc/src/release_notes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ oracledb 3.1.1 (TBD)
1717
Thin Mode Changes
1818
+++++++++++++++++
1919

20+
#) Fixed bug with some databases when a connection is killed. In some
21+
scenarios :meth:`Connection.is_healthy()` would have incorrectly returned
22+
the value *True* and in other cases a possible hang could occur.
23+
2024
Thick Mode Changes
2125
++++++++++++++++++
2226

src/oracledb/errors.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,21 @@ def _make_adjustments(self):
112112
args = {} if match is None else match.groupdict()
113113
else:
114114
driver_error_num = driver_error_info
115-
if driver_error_num == ERR_CONNECTION_CLOSED:
116-
self.is_session_dead = True
117115
driver_error = _get_error_text(driver_error_num, **args)
118116
self.message = f"{driver_error}\n{self.message}"
119117
self.full_code = f"{ERR_PREFIX}-{driver_error_num:04}"
120118

121119
# determine exception class to use when raising this error
120+
# also determine whether error is recoverable and whether the session
121+
# is deemed "dead"
122122
if self.full_code.startswith("DPY-"):
123123
driver_error_num = int(self.full_code[4:])
124+
if driver_error_num == ERR_CONNECTION_CLOSED:
125+
self.is_session_dead = self.isrecoverable = True
124126
self.exc_type = ERR_EXCEPTION_TYPES[driver_error_num // 1000]
125127
elif self.code != 0:
128+
if self.code in ERR_RECOVERABLE_ERROR_CODES:
129+
self.isrecoverable = True
126130
if self.code in ERR_INTEGRITY_ERROR_CODES:
127131
self.exc_type = exceptions.IntegrityError
128132
elif self.code in ERR_INTERFACE_ERROR_CODES:
@@ -485,6 +489,21 @@ def _raise_not_supported(feature: str) -> None:
485489
28511, # lost RPC connection to heterogeneous remote agent
486490
]
487491

492+
# Oracle error codes that are deemed recoverable
493+
# NOTE: this does not include the errors that are mapped to
494+
# ERR_CONNECTION_CLOSED since those are all deemed recoverable
495+
ERR_RECOVERABLE_ERROR_CODES = [
496+
376, # file %s cannot be read at this time
497+
1033, # ORACLE initialization or shutdown in progress
498+
1034, # the Oracle instance is not available for use
499+
1090, # shutdown in progress
500+
1115, # IO error reading block from file %s (block # %s)
501+
12514, # Service %s is not registered with the listener
502+
12571, # TNS:packet writer failure
503+
12757, # instance does not currently know of requested service
504+
16456, # missing or invalid value
505+
]
506+
488507
# driver error message exception types (multiples of 1000)
489508
ERR_EXCEPTION_TYPES = {
490509
1: exceptions.InterfaceError,

src/oracledb/impl/thin/messages/base.pyx

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -67,39 +67,10 @@ cdef class Message:
6767
connection" error is detected, the connection is forced closed
6868
immediately.
6969
"""
70-
cdef bint is_recoverable = False
7170
if self.error_occurred:
72-
if self.error_info.num in (
73-
28, # session has been terminated
74-
31, # session marked for kill
75-
376, # file %s cannot be read at this time
76-
603, # ORACLE server session terminated
77-
1012, # not logged on
78-
1033, # ORACLE initialization or shutdown in progress
79-
1034, # the Oracle instance is not available for use
80-
1089, # immediate shutdown or close in progress
81-
1090, # shutdown in progress
82-
1092, # ORACLE instance terminated
83-
1115, # IO error reading block from file %s (block # %s)
84-
2396, # exceeded maximum idle time
85-
3113, # end-of-file on communication channel
86-
3114, # not connected to ORACLE
87-
3135, # connection lost contact
88-
12153, # TNS:not connected
89-
12514, # Service %s is not registered with the listener
90-
12537, # TNS:connection closed
91-
12547, # TNS:lost contact
92-
12570, # TNS:packet reader failure
93-
12571, # TNS:packet writer failure
94-
12583, # TNS:no reader
95-
12757, # instance does not currently know of requested service
96-
16456, # missing or invalid value
97-
):
98-
is_recoverable = True
9971
error = errors._Error(self.error_info.message,
10072
code=self.error_info.num,
101-
offset=self.error_info.pos,
102-
isrecoverable=is_recoverable)
73+
offset=self.error_info.pos)
10374
if error.is_session_dead:
10475
self.conn_impl._protocol._force_close()
10576
raise error.exc_type(error)

src/oracledb/impl/thin/packet.pyx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ cdef class Packet:
6565
char *ptr
6666
ptr = cpython.PyBytes_AS_STRING(self.buf)
6767
flags = decode_uint16be(<const char_type*> &ptr[PACKET_HEADER_SIZE])
68-
if flags & TNS_DATA_FLAGS_END_OF_RESPONSE:
68+
if flags & TNS_DATA_FLAGS_END_OF_RESPONSE or flags & TNS_DATA_FLAGS_EOF:
6969
return True
7070
if self.packet_size == PACKET_HEADER_SIZE + 3 \
7171
and ptr[PACKET_HEADER_SIZE + 2] == TNS_MSG_TYPE_END_OF_RESPONSE:
@@ -231,6 +231,8 @@ cdef class ReadBuffer(Buffer):
231231
errors._raise_err(errors.ERR_UNSUPPORTED_INBAND_NOTIFICATION,
232232
err_num=self._pending_error_num)
233233
elif self._transport is None:
234+
if self._pending_error_num == TNS_ERR_SESSION_SHUTDOWN:
235+
errors._raise_err(errors.ERR_CONNECTION_CLOSED)
234236
errors._raise_err(errors.ERR_NOT_CONNECTED)
235237

236238
cdef int _get_int_length_and_sign(self, uint8_t *length,

src/oracledb/impl/thin/protocol.pyx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,9 @@ cdef class BaseAsyncProtocol(BaseProtocol):
902902
"""
903903
if not self._in_connect:
904904
self._transport = None
905+
self._read_buf._transport = None
906+
self._write_buf._transport = None
907+
self._read_buf._pending_error_num = TNS_ERR_SESSION_SHUTDOWN
905908
if self._read_buf._waiter is not None \
906909
and not self._read_buf._waiter.done():
907910
error = errors._create_err(errors.ERR_CONNECTION_CLOSED)

0 commit comments

Comments
 (0)