Skip to content

Commit e82f4b7

Browse files
Errors raised when calling Cursor.executemany() with PL/SQL now
have the offset attribute populated with the last iteration that succeeded (#283).
1 parent adc0a0b commit e82f4b7

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed

doc/src/release_notes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ Common Changes
5050
columns which have the check constraint ``IS JSON FORMAT OSON`` enabled.
5151
#) Added boolean property :data:`FetchInfo.is_oson` which is set when a column
5252
has the check constraint "IS JSON FORMAT OSON" enabled.
53+
#) Errors raised when calling :meth:`Cursor.executemany()` with PL/SQL now
54+
have the :data:`oracledb._Error.offset` attribute populated with the last
55+
iteration that succeeded
56+
(`issue 283 <https://github.com/oracle/python-oracledb/issues/283>`__).
5357
#) A number of performance improvements were made.
5458
#) Error ``DPY-2045: arraysize must be an integer greater than zero`` is now
5559
raised when an invalid value is specified for the attribute

src/oracledb/impl/thick/cursor.pyx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,11 +337,14 @@ cdef class ThickCursorImpl(BaseCursorImpl):
337337
with nogil:
338338
status = dpiStmt_executeMany(self._handle, mode, num_execs_int)
339339
dpiContext_getError(driver_context, &error_info)
340-
if not self._stmt_info.isPLSQL:
341-
dpiStmt_getRowCount(self._handle, &rowcount)
342-
self.rowcount = rowcount
340+
dpiStmt_getRowCount(self._handle, &rowcount)
341+
if not self._stmt_info.isPLSQL:
342+
self.rowcount = rowcount
343343
if status < 0:
344-
_raise_from_info(&error_info)
344+
error = _create_new_from_info(&error_info)
345+
if self._stmt_info.isPLSQL and error_info.offset == 0:
346+
error.offset = rowcount
347+
raise error.exc_type(error)
345348
elif error_info.isWarning:
346349
self.warning = _create_new_from_info(&error_info)
347350
if self._stmt_info.isReturning or self._stmt_info.isPLSQL:

src/oracledb/impl/thin/messages.pyx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ cdef class _OracleErrorInfo:
3434
cdef:
3535
uint32_t num
3636
uint16_t cursor_id
37-
uint16_t pos
37+
uint64_t pos
3838
uint64_t rowcount
3939
str message
4040
Rowid rowid
@@ -2160,6 +2160,8 @@ cdef class ExecuteMessage(MessageWithData):
21602160
"""
21612161
cdef Statement stmt = self.cursor_impl._statement
21622162
MessageWithData.process(self, buf)
2163+
if self.error_occurred and self.error_info.pos == 0 and stmt._is_plsql:
2164+
self.error_info.pos = self.error_info.rowcount + self.offset
21632165
if not self.parse_only:
21642166
stmt._executed = True
21652167
if stmt._requires_define and stmt._sql is not None:

tests/test_4000_cursor_executemany.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,23 @@ def test_4025(self):
412412
expected_value = [(67,)] * (len(values) + num_iterations)
413413
self.assertEqual(self.cursor.fetchall(), expected_value)
414414

415+
def test_4026(self):
416+
"4026 - test executemany error offset returned correctly"
417+
data = [(i,) for i in range(1, 11)]
418+
with self.assertRaises(oracledb.Error) as cm:
419+
self.cursor.executemany(
420+
"""
421+
declare
422+
t_Value number;
423+
begin
424+
t_Value := 10 / (4 - :1);
425+
end;
426+
""",
427+
data,
428+
)
429+
(error_obj,) = cm.exception.args
430+
self.assertEqual(error_obj.offset, 3)
431+
415432

416433
if __name__ == "__main__":
417434
test_env.run_test_cases()

0 commit comments

Comments
 (0)