Skip to content

INVISIBLE column with custom %ROWTYPE type: unexpected end of data #325

@Jaza

Description

@Jaza
  1. What versions are you using?

    Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
    Version 19.20.0.0.0
    
    platform.python_version: 3.11.0rc1
    
    oracledb.__version__: 2.1.1
    
  1. Is it an error or a hang or a crash?

    Error

  2. What error(s) or behavior you are seeing?

    DPY-5006: unexpected end of data: want 1 bytes but only 0 bytes are available
    
  1. Does your application call init_oracle_client()?

    No

  1. Include a runnable Python script that shows the problem.

    Setup SQL:

    CREATE TABLE foo_tmp (
        "id" NUMBER,
        "code" VARCHAR2(255 BYTE),
        "thingy" VARCHAR2(1 BYTE) INVISIBLE
    );
    
    INSERT INTO foo_tmp (id, code, thingy) values (1, 'foo', 'f');
    
    create or replace PACKAGE foo_test AS 
    
        TYPE foo_tmp_array IS TABLE OF foo_tmp%ROWTYPE
        INDEX BY BINARY_INTEGER;
    
        PROCEDURE prGetRecords (
            out_rec OUT foo_test.foo_tmp_array
        );
    
    END foo_test;
    
    create or replace PACKAGE BODY foo_test IS 
    
        PROCEDURE prGetRecords (
            out_rec OUT foo_test.foo_tmp_array
        ) 
        IS
            CURSOR c_foo_tmp IS
            SELECT *
            FROM foo_tmp;
        BEGIN
            OPEN  c_foo_tmp;
            FETCH c_foo_tmp BULK COLLECT INTO out_rec;
            CLOSE c_foo_tmp;
        END prGetRecords;
    
    END foo_test;
    

    Python script:

    import asyncio
    
    from oracledb import create_pool_async, makedsn
    
    
    async def list_data(pool):
        async with pool.acquire() as conn:
            data_coll_type = await conn.gettype("FOO_TEST.FOO_TMP_ARRAY")
            data_coll = data_coll_type.newobject()
    
            keyword_parameters = {
                "out_rec": data_coll,
            }
    
            proc_name = "foo_test.prGetRecords"
    
            await conn.callproc(
                name=proc_name,
                keyword_parameters=keyword_parameters,
            )
    
            rows = []
    
            for record in data_coll.aslist():
                row = {}
    
                for type_attr in data_coll.type.element_type.attributes:
                    attr_name = type_attr.name
                    attr_value = getattr(record, type_attr.name, None)
                    row[f"{attr_name}"] = attr_value
    
                rows.append(row)
    
            return rows
    
    
    async def main():
        dsn = makedsn(
            host="mydbhost",
            port=1521,
            sid="myservicename",
        )
    
        pool = create_pool_async(
            user="myuser,
            password="mypass",
            dsn=dsn,
        )
        rows = await list_data(pool)
        print(rows)
    
    
    asyncio.run(main())

    Expected output:

    [{'ID': 1.0, 'CODE': 'foo'}]
    

    Actual output:

    Traceback (most recent call last):
      File "/pathto/get_foo_records_test.py", line 54, in <module>
        asyncio.run(main())
      File "/usr/lib/python3.11/asyncio/runners.py", line 188, in run
        return runner.run(main)
               ^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.11/asyncio/runners.py", line 120, in run
        return self._loop.run_until_complete(task)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.11/asyncio/base_events.py", line 650, in run_until_complete
        return future.result()
               ^^^^^^^^^^^^^^^
      File "/pathto/get_foo_records_test.py", line 50, in main
        rows = await list_data(pool)
               ^^^^^^^^^^^^^^^^^^^^^
      File "/pathto/get_foo_records_test.py", line 30, in list_data
        attr_value = getattr(record, type_attr.name, None)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/pathto/lib/python3.11/site-packages/oracledb/dbobject.py", line 47, in __getattr__
        return self._impl.get_attr_value(attr_impl)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "src/oracledb/impl/thin/dbobject.pyx", line 452, in oracledb.thin_impl.ThinDbObjectImpl.get_attr_value
      File "src/oracledb/impl/thin/dbobject.pyx", line 162, in oracledb.thin_impl.ThinDbObjectImpl._ensure_unpacked
      File "src/oracledb/impl/thin/dbobject.pyx", line 278, in oracledb.thin_impl.ThinDbObjectImpl._unpack_data
      File "src/oracledb/impl/thin/dbobject.pyx", line 316, in oracledb.thin_impl.ThinDbObjectImpl._unpack_data_from_buf
      File "src/oracledb/impl/thin/dbobject.pyx", line 346, in oracledb.thin_impl.ThinDbObjectImpl._unpack_value
      File "src/oracledb/impl/base/buffer.pyx", line 720, in oracledb.base_impl.Buffer.read_str
      File "src/oracledb/impl/base/buffer.pyx", line 634, in oracledb.base_impl.Buffer.read_raw_bytes_and_length
      File "src/oracledb/impl/base/buffer.pyx", line 730, in oracledb.base_impl.Buffer.read_ub1
      File "src/oracledb/impl/base/buffer.pyx", line 152, in oracledb.base_impl.Buffer._get_raw
      File "/pathto/lib/python3.11/site-packages/oracledb/errors.py", line 181, in _raise_err
        raise error.exc_type(error) from cause
    oracledb.exceptions.InternalError: DPY-5006: unexpected end of data: want 1 bytes but only 0 bytes are available
    

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions