Skip to content

Commit 0690c3d

Browse files
Correct handling of case when the number of batch errors exceeds 65535
(#262).
1 parent 527c4d5 commit 0690c3d

File tree

3 files changed

+26
-12
lines changed

3 files changed

+26
-12
lines changed

doc/src/release_notes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ Common Changes
6868
database
6969
(`issue 99 <https://github.com/oracle/python-oracledb/issues/99>`__).
7070
#) Fixed bug with getting unknown attributes from DbObject instances.
71+
#) Error ``DPY-4029: errors in array DML exceed 65535`` is now raised when the
72+
number of batch errors exceeds 65535 when calling
73+
:meth:`Cursor.executemany()` with the parameter ``batcherrors`` set to the
74+
value ``True``. Note that in thick mode this error is not raised unless the
75+
number of batch errors is a multiple of 65536; instead, the number of batch
76+
errors returned is modulo 65536.
7177
#) Errors that have entries in the
7278
:ref:`troubleshooting documentation <troubleshooting>` now have links to
7379
that documentation included in the message text.

src/oracledb/errors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def _raise_from_string(exc_type: Exception, message: str) -> None:
269269
ERR_TNS_NAMES_FILE_MISSING = 4026
270270
ERR_NO_CONFIG_DIR = 4027
271271
ERR_INVALID_SERVER_TYPE = 4028
272+
ERR_TOO_MANY_BATCH_ERRORS = 4029
272273

273274
# error numbers that result in InternalError
274275
ERR_MESSAGE_TYPE_UNKNOWN = 5000
@@ -308,6 +309,7 @@ def _raise_from_string(exc_type: Exception, message: str) -> None:
308309
24459: ERR_POOL_NO_CONNECTION_AVAILABLE,
309310
24496: ERR_POOL_NO_CONNECTION_AVAILABLE,
310311
24338: ERR_INVALID_REF_CURSOR,
312+
38902: ERR_TOO_MANY_BATCH_ERRORS,
311313
}
312314

313315
# ODPI-C error number cross reference
@@ -553,6 +555,9 @@ def _raise_from_string(exc_type: Exception, message: str) -> None:
553555
ERR_TOO_MANY_CURSORS_TO_CLOSE: (
554556
"internal error: attempt to close more than {num_cursors} cursors"
555557
),
558+
ERR_TOO_MANY_BATCH_ERRORS: (
559+
"the number of batch errors from executemany() exceeds 65535"
560+
),
556561
ERR_UNEXPECTED_DATA: "unexpected data received: {data}",
557562
ERR_UNEXPECTED_END_OF_DATA: (
558563
"unexpected end of data: want {num_bytes_wanted} bytes but "

src/oracledb/impl/thin/messages.pyx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ cdef class Message:
9090

9191
cdef int _process_error_info(self, ReadBuffer buf) except -1:
9292
cdef:
93+
uint32_t num_bytes, i, offset, num_offsets
9394
_OracleErrorInfo info = self.error_info
94-
uint16_t num_entries, error_code
95-
uint32_t num_bytes, i, offset
95+
uint16_t temp16, num_errors, error_code
9696
uint8_t first_byte
9797
int16_t error_pos
9898
str error_msg
@@ -122,11 +122,11 @@ cdef class Message:
122122
self.processed_error = True
123123

124124
# batch error codes
125-
buf.read_ub2(&num_entries) # batch error codes array
126-
if num_entries > 0:
125+
buf.read_ub2(&num_errors) # batch error codes array
126+
if num_errors > 0:
127127
info.batcherrors = []
128128
buf.read_ub1(&first_byte)
129-
for i in range(num_entries):
129+
for i in range(num_errors):
130130
if first_byte == TNS_LONG_LENGTH_INDICATOR:
131131
buf.skip_ub4() # chunk length ignored
132132
buf.read_ub2(&error_code)
@@ -135,22 +135,25 @@ cdef class Message:
135135
buf.skip_raw_bytes(1) # ignore end marker
136136

137137
# batch error offsets
138-
buf.read_ub2(&num_entries) # batch error row offset array
139-
if num_entries > 0:
138+
buf.read_ub4(&num_offsets) # batch error row offset array
139+
if num_offsets > 0:
140+
if num_offsets > 65535:
141+
errors._raise_err(errors.ERR_TOO_MANY_BATCH_ERRORS)
140142
buf.read_ub1(&first_byte)
141-
for i in range(num_entries):
143+
for i in range(num_offsets):
142144
if first_byte == TNS_LONG_LENGTH_INDICATOR:
143145
buf.skip_ub4() # chunk length ignored
144146
buf.read_ub4(&offset)
145-
info.batcherrors[i].offset = offset
147+
if i < num_errors:
148+
info.batcherrors[i].offset = offset
146149
if first_byte == TNS_LONG_LENGTH_INDICATOR:
147150
buf.skip_raw_bytes(1) # ignore end marker
148151

149152
# batch error messages
150-
buf.read_ub2(&num_entries) # batch error messages array
151-
if num_entries > 0:
153+
buf.read_ub2(&temp16) # batch error messages array
154+
if temp16 > 0:
152155
buf.skip_raw_bytes(1) # ignore packed size
153-
for i in range(num_entries):
156+
for i in range(temp16):
154157
buf.skip_ub2() # skip chunk length
155158
info.batcherrors[i].message = \
156159
buf.read_str(TNS_CS_IMPLICIT).rstrip()

0 commit comments

Comments
 (0)