Skip to content

Commit 1ede3a4

Browse files
Fix error message when idle time is exceeded by the connection.
1 parent c513952 commit 1ede3a4

File tree

8 files changed

+45
-24
lines changed

8 files changed

+45
-24
lines changed

doc/src/release_notes.rst

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ Thin Mode Changes
2222
(`issue 368 <https://github.com/oracle/python-oracledb/issues/368>`__).
2323
#) Fixed bug resulting in an inability to connect to Oracle Database 23ai
2424
instances which have fast authentication disabled.
25+
#) Fixed error message when idle time is exceeded by a connection. The error
26+
``DPY-4033: the database closed the connection because the connection's
27+
idle time has been exceeded`` is now raised when this situation is
28+
detected.
2529
#) Reworked connection string parser:
2630

2731
- Fixed parsing an :ref:`Easy Connect <easyconnect>` string starting

src/oracledb/errors.py

+5
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ def _raise_not_supported(feature: str) -> None:
320320
ERR_IFILE_CYCLE_DETECTED = 4030
321321
ERR_INVALID_VECTOR = 4031
322322
ERR_INVALID_SSL_VERSION = 4032
323+
ERR_EXCEEDED_IDLE_TIME = 4033
323324

324325
# error numbers that result in InternalError
325326
ERR_MESSAGE_TYPE_UNKNOWN = 5000
@@ -496,6 +497,10 @@ def _raise_not_supported(feature: str) -> None:
496497
ERR_DUPLICATED_PARAMETER: (
497498
'"{deprecated_name}" and "{new_name}" cannot be specified together'
498499
),
500+
ERR_EXCEEDED_IDLE_TIME: (
501+
"the database closed the connection because the connection's idle "
502+
"time has been exceeded"
503+
),
499504
ERR_EXECUTE_MODE_ONLY_FOR_DML: (
500505
'parameters "batcherrors" and "arraydmlrowcounts" may only be '
501506
"true when used with insert, update, delete and merge statements"

src/oracledb/impl/thin/connection.pyx

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ cdef class BaseThinConnImpl(BaseConnImpl):
229229

230230
def get_is_healthy(self):
231231
return self._protocol._transport is not None \
232-
and not self._protocol._read_buf._session_needs_to_be_closed
232+
and self._protocol._read_buf._pending_error_num == 0
233233

234234
def get_ltxid(self):
235235
return self._ltxid or b''

src/oracledb/impl/thin/constants.pxi

+1
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ cdef enum:
390390
TNS_ERR_NO_DATA_FOUND = 1403
391391
TNS_ERR_SESSION_SHUTDOWN = 12572
392392
TNS_ERR_ARRAY_DML_ERRORS = 24381
393+
TNS_ERR_EXCEEDED_IDLE_TIME = 2396
393394

394395
# message types
395396
cdef enum:

src/oracledb/impl/thin/messages.pyx

+1-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ cdef class Message:
8080
to avoid overhead using the constructor, a special hook method is used
8181
instead.
8282
"""
83-
if conn_impl._protocol._transport is None:
84-
errors._raise_err(errors.ERR_NOT_CONNECTED)
83+
conn_impl._protocol._read_buf._check_connected()
8584
self.conn_impl = conn_impl
8685
self.message_type = TNS_MSG_TYPE_FUNCTION
8786
self.error_info = _OracleErrorInfo.__new__(_OracleErrorInfo)

src/oracledb/impl/thin/packet.pyx

+26-13
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ cdef class ReadBuffer(Buffer):
199199
cdef:
200200
ssize_t _saved_packet_pos, _next_packet_pos, _saved_pos
201201
ChunkedBytesBuffer _chunked_bytes_buf
202-
bint _session_needs_to_be_closed
203202
const char_type _split_data[255]
203+
uint32_t _pending_error_num
204204
Packet _current_packet
205205
Transport _transport
206206
list _saved_packets
@@ -214,6 +214,23 @@ cdef class ReadBuffer(Buffer):
214214
self._caps = caps
215215
self._chunked_bytes_buf = ChunkedBytesBuffer()
216216

217+
cdef int _check_connected(self):
218+
"""
219+
Checks to see if the transport is connected and throws the appropriate
220+
exception if not.
221+
"""
222+
if self._pending_error_num != 0:
223+
if self._transport is not None:
224+
self._transport.disconnect()
225+
self._transport = None
226+
if self._pending_error_num == TNS_ERR_EXCEEDED_IDLE_TIME:
227+
errors._raise_err(errors.ERR_EXCEEDED_IDLE_TIME)
228+
else:
229+
errors._raise_err(errors.ERR_UNSUPPORTED_INBAND_NOTIFICATION,
230+
err_num=self._pending_error_num)
231+
elif self._transport is None:
232+
errors._raise_err(errors.ERR_NOT_CONNECTED)
233+
217234
cdef int _get_int_length_and_sign(self, uint8_t *length,
218235
bint *is_negative,
219236
uint8_t max_length) except -1:
@@ -311,7 +328,6 @@ cdef class ReadBuffer(Buffer):
311328
"""
312329
cdef:
313330
uint16_t control_type
314-
uint32_t error_num
315331
Buffer buf
316332
buf = Buffer.__new__(Buffer)
317333
buf._populate_from_bytes(packet.buf)
@@ -321,16 +337,11 @@ cdef class ReadBuffer(Buffer):
321337
self._caps.supports_oob = False
322338
elif control_type == TNS_CONTROL_TYPE_INBAND_NOTIFICATION:
323339
buf.skip_raw_bytes(4) # skip first integer
324-
buf.read_uint32(&error_num)
325-
if error_num == TNS_ERR_SESSION_SHUTDOWN \
326-
or error_num == TNS_ERR_INBAND_MESSAGE:
327-
self._session_needs_to_be_closed = True
328-
else:
329-
errors._raise_err(errors.ERR_UNSUPPORTED_INBAND_NOTIFICATION,
330-
err_num=error_num)
340+
buf.read_uint32(&self._pending_error_num)
331341

332342
cdef int _process_packet(self, Packet packet,
333-
bint *notify_waiter) except -1:
343+
bint *notify_waiter,
344+
bint check_connected) except -1:
334345
"""
335346
Processes a packet. If the packet is a control packet it is processed
336347
immediately and discarded; otherwise, it is added to the list of saved
@@ -341,6 +352,8 @@ cdef class ReadBuffer(Buffer):
341352
if packet.packet_type == TNS_PACKET_TYPE_CONTROL:
342353
self._process_control_packet(packet)
343354
notify_waiter[0] = False
355+
if check_connected:
356+
self._check_connected()
344357
else:
345358
self._saved_packets.append(packet)
346359
notify_waiter[0] = \
@@ -379,7 +392,7 @@ cdef class ReadBuffer(Buffer):
379392
if self._current_packet.packet_type == TNS_PACKET_TYPE_DATA:
380393
self.read_uint16(&data_flags)
381394
if data_flags == TNS_DATA_FLAGS_EOF:
382-
self._session_needs_to_be_closed = True
395+
self._pending_error_num = TNS_ERR_SESSION_SHUTDOWN
383396

384397
cdef int notify_packet_received(self) except -1:
385398
"""
@@ -618,7 +631,7 @@ cdef class ReadBuffer(Buffer):
618631
bint notify_waiter
619632
Packet packet
620633
packet = self._transport.read_packet()
621-
self._process_packet(packet, &notify_waiter)
634+
self._process_packet(packet, &notify_waiter, False)
622635
if notify_waiter:
623636
self._start_packet()
624637

@@ -698,7 +711,7 @@ cdef class ReadBuffer(Buffer):
698711
raise OutOfPackets()
699712
while True:
700713
packet = self._transport.read_packet()
701-
self._process_packet(packet, &notify_waiter)
714+
self._process_packet(packet, &notify_waiter, True)
702715
if notify_waiter:
703716
break
704717
self._start_packet()

src/oracledb/impl/thin/pool.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -846,12 +846,12 @@ cdef class PooledConnRequest:
846846
double elapsed_time
847847
bint has_data_ready
848848
if not buf._transport._is_async:
849-
while not buf._session_needs_to_be_closed:
849+
while buf._pending_error_num == 0:
850850
buf._transport.has_data_ready(&has_data_ready)
851851
if not has_data_ready:
852852
break
853853
buf.check_control_packet()
854-
if buf._session_needs_to_be_closed:
854+
if buf._pending_error_num != 0:
855855
self.pool_impl._drop_conn_impl(conn_impl)
856856
self._open_count -= 1
857857
else:

src/oracledb/impl/thin/protocol.pyx

+5-6
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ cdef class BaseProtocol:
7474
"""
7575
buf.start_request(TNS_PACKET_TYPE_DATA, 0, TNS_DATA_FLAGS_EOF)
7676
buf.end_request()
77-
self._transport.disconnect()
78-
self._transport = None
77+
self._force_close()
7978

8079
cdef int _force_close(self) except -1:
8180
"""
@@ -100,7 +99,7 @@ cdef class BaseProtocol:
10099
established.
101100
"""
102101
conn_impl.warning = auth_message.warning
103-
self._read_buf._session_needs_to_be_closed = False
102+
self._read_buf._pending_error_num = 0
104103
self._in_connect = False
105104

106105
cdef int _release_drcp_session(self, BaseThinConnImpl conn_impl,
@@ -163,7 +162,7 @@ cdef class Protocol(BaseProtocol):
163162

164163
# if the session was marked as needing to be closed, force it
165164
# closed immediately (unless it was already closed)
166-
if self._read_buf._session_needs_to_be_closed \
165+
if self._read_buf._pending_error_num != 0 \
167166
and self._transport is not None:
168167
self._force_close()
169168

@@ -520,7 +519,7 @@ cdef class BaseAsyncProtocol(BaseProtocol):
520519

521520
# if the session was marked as needing to be closed, force it
522521
# closed immediately (unless it was already closed)
523-
if self._read_buf._session_needs_to_be_closed \
522+
if self._read_buf._pending_error_num != 0 \
524523
and self._transport is not None:
525524
self._force_close()
526525

@@ -886,7 +885,7 @@ cdef class BaseAsyncProtocol(BaseProtocol):
886885
Packet packet
887886
packet = self._transport.extract_packet(data)
888887
while packet is not None:
889-
self._read_buf._process_packet(packet, &notify_waiter)
888+
self._read_buf._process_packet(packet, &notify_waiter, False)
890889
if notify_waiter:
891890
self._read_buf.notify_packet_received()
892891
packet = self._transport.extract_packet()

0 commit comments

Comments
 (0)