oracle / python-oracledb

Python driver for Oracle Database conforming to the Python DB API 2.0 specification. This is the renamed, new major release of cx_Oracle
https://oracle.github.io/python-oracledb
Other
308 stars 61 forks source link

Cannot set NULL on DbObject attribute any more in 2.0 #273

Closed mkmoisen closed 5 months ago

mkmoisen commented 6 months ago

In 2.0 it is no longer possible to set an attribute on DbObject to NULL for a VARCHAR2.

This was possible in all previous versions of oracledb as well as cx_Oracle.

This bug was caused by the following change:

https://github.com/oracle/python-oracledb/commit/82112325eb65e99646ddecee0a264efad67198eb

The change assumes that the value is not null.

There needs to be a condition that checks if it is null and then avoids performing this max size check, for example:

        if value is not None and max_size > 0:
            if isinstance(value, str):
                actual_size[0] = len((<str> value).encode())
            else:
                actual_size[0] = len(<bytes> value)
            if actual_size[0] > max_size:
                violated[0] = True
  1. What versions are you using?

2.0

Give your database version.

19C

Also run Python and show the output of:

>>> import sys
>>> import platform
>>> 
>>> print("platform.platform:", platform.platform())
platform.platform: Linux-4.18.0-372.76.1.el8_6.x86_64-x86_64-with-glibc2.28
>>> print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
sys.maxsize > 2**32: True
>>> print("platform.python_version:", platform.python_version())
platform.python_version: 3.12.1

And:

>>> import oracledb
>>> print("oracledb.__version__:", oracledb.__version__)
oracledb.__version__: 2.0.0
  1. Is it an error or a hang or a crash?

error

  1. What error(s) or behavior you are seeing?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/app-root/lib/python3.12/site-packages/oracledb/dbobject.py", line 60, in __setattr__
    self._impl.set_attr_value(attr_impl, value)
  File "src/oracledb/impl/base/dbobject.pyx", line 123, in oracledb.base_impl.BaseDbObjectImpl.set_attr_value
  File "src/oracledb/impl/base/dbobject.pyx", line 44, in oracledb.base_impl.BaseDbObjectImpl._check_max_size
TypeError: object of type 'NoneType' has no len()

Cut and paste text showing the command you ran. No screenshots.

Here is the PLSQL to create the record:


create or replace package foo_pkg
is
    type r_bar IS RECORD (baz varchar2(10));
end foo_pkg;
/

create or replace package body foo_pkg
is

end foo_pkg;
/

Here is the python that creates an instance of the record type and set BAZ to null:

r_foo_type = conn.gettype('MY_SCHEMA.FOO_PKG.R_BAR')

r_foo = r_foo_type.newobject()

# This is OK, as expected
r_foo.BAZ = 'hi'

# Setting to NULL results in an error:
r_foo.BAZ = None

Here is the error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/app-root/lib/python3.12/site-packages/oracledb/dbobject.py", line 60, in __setattr__
    self._impl.set_attr_value(attr_impl, value)
  File "src/oracledb/impl/base/dbobject.pyx", line 123, in oracledb.base_impl.BaseDbObjectImpl.set_attr_value
  File "src/oracledb/impl/base/dbobject.pyx", line 44, in oracledb.base_impl.BaseDbObjectImpl._check_max_size
TypeError: object of type 'NoneType' has no len()
  1. Does your application call init_oracle_client()?

Yes, thick mode.

This tells us whether you are using the python-oracledb Thin or Thick mode.

cjbj commented 6 months ago

Thanks for the report and analysis.

anthony-tuininga commented 6 months ago

I have pushed a patch that should correct this bug. If you are able to build from source you can verify that it works for you.

anthony-tuininga commented 5 months ago

The patch has been included in version 2.0.1 which was just released.

mkmoisen commented 5 months ago

@anthony-tuininga Sorry I missed your message. I just tried 2.0.1 and my regression tests are passing now. Thanks!