Skip to content

Commit 67d3060

Browse files
Adjust handling of XMLType to manage the case when embedded LOBs are
used instead of direct strings.
1 parent 796634b commit 67d3060

File tree

5 files changed

+40
-4
lines changed

5 files changed

+40
-4
lines changed

src/oracledb/errors.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ def _raise_from_string(exc_type: Exception, message: str) -> None:
227227
ERR_UNEXPECTED_DATA = 5004
228228
ERR_UNEXPECTED_REFUSE = 5005
229229
ERR_UNEXPECTED_END_OF_DATA = 5006
230+
ERR_UNEXPECTED_XML_TYPE = 5007
230231

231232
# error numbers that result in OperationalError
232233
ERR_LISTENER_REFUSED_CONNECTION = 6000
@@ -476,6 +477,8 @@ def _raise_from_string(exc_type: Exception, message: str) -> None:
476477
ERR_UNEXPECTED_REFUSE:
477478
'the listener refused the connection but an unexpected error '
478479
'format was returned',
480+
ERR_UNEXPECTED_XML_TYPE:
481+
'unexpected XMLType with flag {flag}',
479482
ERR_UNSUPPORTED_INBAND_NOTIFICATION:
480483
'unsupported in-band notification with error number {err_num}',
481484
ERR_UNSUPPORTED_PYTHON_TYPE_FOR_DB_TYPE:

src/oracledb/impl/thin/buffer.pyx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,14 +791,20 @@ cdef class Buffer:
791791
cdef const char_type *ptr = self._get_raw(4)
792792
value[0] = unpack_uint32(ptr, byte_order)
793793

794-
cdef object read_xmltype(self):
794+
cdef object read_xmltype(self, ThinConnImpl conn_impl):
795795
"""
796796
Reads an XMLType value from the buffer and returns the string value.
797797
The XMLType object is a special DbObjectType and is handled separately
798798
since the structure is a bit different.
799799
"""
800800
cdef:
801+
uint8_t image_flags, image_version
802+
DbObjectPickleBuffer buf
803+
ThinLobImpl lob_impl
804+
const char_type *ptr
801805
uint32_t num_bytes
806+
ssize_t bytes_left
807+
uint32_t xml_flag
802808
bytes packed_data
803809
self.read_ub4(&num_bytes)
804810
if num_bytes > 0: # type OID
@@ -814,7 +820,22 @@ cdef class Buffer:
814820
self.skip_ub2() # flags
815821
if num_bytes > 0:
816822
packed_data = self.read_bytes()
817-
return packed_data[12:].decode()
823+
buf = DbObjectPickleBuffer.__new__(DbObjectPickleBuffer)
824+
buf._populate_from_bytes(packed_data)
825+
buf.read_header(&image_flags, &image_version)
826+
buf.skip_raw_bytes(1) # XML version
827+
buf.read_uint32(&xml_flag)
828+
if xml_flag & TNS_XML_TYPE_FLAG_SKIP_NEXT_4:
829+
buf.skip_raw_bytes(4)
830+
bytes_left = buf.bytes_left()
831+
ptr = buf.read_raw_bytes(bytes_left)
832+
if xml_flag & TNS_XML_TYPE_STRING:
833+
return ptr[:bytes_left].decode()
834+
elif xml_flag & TNS_XML_TYPE_LOB:
835+
lob_impl = ThinLobImpl._create(conn_impl, DB_TYPE_CLOB,
836+
ptr[:bytes_left])
837+
return PY_TYPE_LOB._from_impl(lob_impl)
838+
errors._raise_err(errors.ERR_UNEXPECTED_XML_TYPE, flag=xml_flag)
818839

819840
cdef int skip_raw_bytes(self, ssize_t num_bytes) except -1:
820841
"""

src/oracledb/impl/thin/constants.pxi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ DEF TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ = 33
451451
DEF TNS_OBJ_TDS_TYPE_BINARY_FLOAT = 37
452452
DEF TNS_OBJ_TDS_TYPE_BINARY_DOUBLE = 45
453453

454+
# xml type constants
455+
DEF TNS_XML_TYPE_LOB = 0x0001
456+
DEF TNS_XML_TYPE_STRING = 0x0004
457+
DEF TNS_XML_TYPE_FLAG_SKIP_NEXT_4 = 0x100000
458+
454459
# execute options
455460
DEF TNS_EXEC_OPTION_PARSE = 0x01
456461
DEF TNS_EXEC_OPTION_BIND = 0x08

src/oracledb/impl/thin/cursor.pyx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,20 @@ cdef class ThinCursorImpl(BaseCursorImpl):
7777
the variable is created to determine if a conversion is required and
7878
therefore a define must be performed.
7979
"""
80-
cdef ThinVarImpl var_impl
80+
cdef:
81+
ThinDbObjectTypeImpl typ_impl
82+
ThinVarImpl var_impl
8183
BaseCursorImpl._create_fetch_var(self, conn, cursor, type_handler, pos,
8284
fetch_info)
8385
var_impl = self.fetch_var_impls[pos]
8486
if var_impl.dbtype._ora_type_num != fetch_info._dbtype._ora_type_num:
8587
conversion_helper(var_impl, fetch_info,
8688
&self._statement._requires_define)
89+
elif var_impl.objtype is not None:
90+
typ_impl = var_impl.objtype
91+
if typ_impl.is_xml_type:
92+
var_impl.outconverter = \
93+
lambda v: v if isinstance(v, str) else v.read()
8794

8895
cdef int _fetch_rows(self, object cursor) except -1:
8996
"""

src/oracledb/impl/thin/messages.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ cdef class MessageWithData(Message):
603603
elif ora_type_num == TNS_DATA_TYPE_INT_NAMED:
604604
typ_impl = var_impl.objtype
605605
if typ_impl.is_xml_type:
606-
column_value = buf.read_xmltype()
606+
column_value = buf.read_xmltype(self.conn_impl)
607607
else:
608608
obj_impl = buf.read_dbobject(typ_impl)
609609
if obj_impl is not None:

0 commit comments

Comments
 (0)