We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
1 parent 4286227 commit 1ffaa7fCopy full SHA for 1ffaa7f
doc/src/api_manual/cursor.rst
@@ -371,7 +371,7 @@ Cursor Methods
371
372
373
.. method:: Cursor.var(typ, [size, arraysize, inconverter, outconverter, \
374
- typename, encoding_errors, bypass_decode])
+ typename, encoding_errors, bypass_decode, convert_nulls])
375
376
Creates a variable with the specified characteristics. This method was
377
designed for use with PL/SQL in/out variables where the length or type
@@ -442,10 +442,20 @@ Cursor Methods
442
meaning that python-oracledb does not do any decoding. See :ref:`Fetching raw
443
data <fetching-raw-data>` for more information.
444
445
+ The ``convert_nulls`` parameter, if specified, should be passed a boolean
446
+ value. Passing the value ``True`` causes the ``outconverter`` to be called
447
+ when a null value is fetched from the database; otherwise, the
448
+ ``outconverter`` is only called when non-null values are fetched from the
449
+ database.
450
+
451
For consistency and compliance with the PEP 8 naming style, the
452
parameter `encodingErrors` was renamed to `encoding_errors`. The old
453
name will continue to work as a keyword parameter for a period of time.
454
455
+ .. versionchanged:: 1.4
456
457
+ The ``convert_nulls`` parameter was added.
458
459
.. note::
460
461
The DB API definition does not define this method.
doc/src/api_manual/variable.rst
@@ -50,6 +50,13 @@ Variable Attributes
50
name will continue to work for a period of time.
51
52
53
+.. attribute:: Variable.convert_nulls
54
55
+ This read-only attribute returns whether the :data:`~Variable.outconverter`
56
+ method is called when null values are fetched from the database.
57
58
+ .. versionadded:: 1.4
59
60
.. attribute:: Variable.inconverter
61
62
This read-write attribute specifies the method used to convert data from
doc/src/release_notes.rst
@@ -74,6 +74,11 @@ Thick Mode Changes
74
Common Changes
75
++++++++++++++
76
77
+#) Added support for the :attr:`~Variable.outconverter` being called when a
78
+ null value is fetched from the database and the new parameter
79
+ ``convert_nulls`` to the method :meth:`Cursor.var()` is passed the value
80
+ ``True``
81
+ (`issue 107 <https://github.com/oracle/python-oracledb/issues/107>`__).
82
#) Replaced fixed 7-tuple for the cursor metadata found in
83
:data:`Cursor.description` with a class which provides additional
84
information such as the database object type and whether the column
src/oracledb/base_impl.pxd
@@ -333,6 +333,7 @@ cdef class BaseVarImpl:
333
readonly bint bypass_decode
334
readonly bint is_array
335
readonly bint nulls_allowed
336
+ readonly bint convert_nulls
337
public uint32_t num_elements_in_array
338
readonly DbType dbtype
339
readonly BaseDbObjectTypeImpl objtype
src/oracledb/cursor.py
@@ -743,6 +743,7 @@ def var(self,
743
typename: str=None,
744
encoding_errors: str=None,
745
bypass_decode: bool=False,
746
+ convert_nulls: bool=False,
747
*,
748
encodingErrors: str=None) -> "Var":
749
"""
@@ -794,6 +795,9 @@ def var(self,
794
795
DB_TYPE_VARCHAR, DB_TYPE_CHAR, DB_TYPE_NVARCHAR, DB_TYPE_NCHAR and
796
DB_TYPE_LONG to be returned as bytes instead of str, meaning that
797
oracledb doesn't do any decoding.
798
799
+ The convert_nulls parameter specifies whether the outconverter should
800
+ be called when null values are fetched from the database.
801
802
self._verify_open()
803
if typename is not None:
@@ -808,4 +812,5 @@ def var(self,
808
812
encoding_errors = encodingErrors
809
813
return self._impl.create_var(self.connection, typ, size, arraysize,
810
814
inconverter, outconverter,
811
- encoding_errors, bypass_decode)
815
+ encoding_errors, bypass_decode,
816
+ convert_nulls=convert_nulls)
src/oracledb/impl/base/cursor.pyx
@@ -403,7 +403,8 @@ cdef class BaseCursorImpl:
403
def create_var(self, object conn, object typ, uint32_t size=0,
404
uint32_t num_elements=1, object inconverter=None,
405
object outconverter=None, str encoding_errors=None,
406
- bint bypass_decode=False, bint is_array=False):
+ bint bypass_decode=False, bint is_array=False,
407
+ bint convert_nulls=False):
408
cdef BaseVarImpl var_impl
409
var_impl = self._create_var_impl(conn)
410
var_impl._set_type_info_from_type(typ)
@@ -413,6 +414,7 @@ cdef class BaseCursorImpl:
413
414
var_impl.outconverter = outconverter
415
var_impl.bypass_decode = bypass_decode
416
var_impl.is_array = is_array
417
+ var_impl.convert_nulls = convert_nulls
418
var_impl._finalize_init()
419
return PY_TYPE_VAR._from_impl(var_impl)
420
src/oracledb/impl/thick/var.pyx
@@ -1,5 +1,5 @@
1
#------------------------------------------------------------------------------
2
-# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
+# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
3
#
4
# This software is dual-licensed to you under the Universal Permissive License
5
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -276,3 +276,5 @@ cdef class ThickVarImpl(BaseVarImpl):
276
if self.outconverter is not None:
277
value = self.outconverter(value)
278
return value
279
+ elif self.convert_nulls:
280
+ return self.outconverter(None)
src/oracledb/impl/thin/messages.pyx
@@ -494,7 +494,7 @@ cdef class MessageWithData(Message):
494
num_elements = self.row_index
495
for i in range(num_elements):
496
value = var_impl._values[i]
497
- if value is None:
+ if value is None and not var_impl.convert_nulls:
498
continue
499
if isinstance(value, list):
500
for j, element_value in enumerate(value):
src/oracledb/var.py
@@ -90,6 +90,14 @@ def bufferSize(self) -> int:
90
91
return self.buffer_size
92
93
+ @property
94
+ def convert_nulls(self) -> bool:
95
+ """
96
+ This read-only attribute returns whether null values are converted
97
+ using the supplied ``outconverter``.
98
99
+ return self._impl.convert_nulls
100
101
def getvalue(self, pos: int=0) -> Any:
102
103
Return the value at the given position in the variable. For variables
tests/test_3700_var.py
@@ -433,5 +433,26 @@ def test_3729_deprecations(self):
433
self.assertEqual(var.actualElements, 200)
434
self.assertEqual(var.numElements, 200)
435
436
+ def test_3730_convert_nulls(self):
437
+ "3730 - test calling of outconverter with null values"
438
+ def type_handler(cursor, metadata):
439
+ return cursor.var(metadata.type_code,
440
+ outconverter=lambda v: f"|{v}|" if v else '',
441
+ convert_nulls=True, arraysize=cursor.arraysize)
+ self.cursor.outputtypehandler = type_handler
+ self.cursor.execute("""
+ select 'First - A', 'First - B' from dual
+ union all
+ select 'Second - A', null from dual
+ select null, 'Third - B' from dual""")
+ rows = self.cursor.fetchall()
+ expected_rows = [
+ ('|First - A|', '|First - B|'),
+ ('|Second - A|', ''),
+ ('', '|Third - B|')
+ ]
+ self.assertEqual(rows, expected_rows)
if __name__ == "__main__":
test_env.run_test_cases()
0 commit comments