Nested records - value not returned in specific cases · Issue #456 · oracle/python-oracledb (original) (raw)

What versions are you using?

database: 21XE / 19c EE 19.0.0.0.0
platform.platform: macOS-15.3-arm64-arm-64bit-Mach-O
sys.maxsize > 2**32: True
platform.python_version: 3.13.0
oracledb.version: 2.5.1

Is it an error or a hang or a crash?

ERROR - incorrect behaviour of output variables (plsql records)

What error(s) or behavior you are seeing?

Depending on the definition of nested record (number of fields and their position) I get different outcomes.
Below is a runnable scripts that shows 2 failing cases which I believe should pass (for example filling a value in nested field INNER2 should return the record from the DB and contain the fillled value, however, None is returned l_i_am_causing_troubles_here field is the last field in the nested record).

To see the different behaviours, comment/uncomment the different definitions of inner_t record.

Does your application call init_oracle_client()?

NO - using thin mode

Include a runnable Python script that shows the problem.

import oracledb

DB_DSN='XEPDB1' DB_USER='test' DB_PASSWORD='test'

DDLS = [ """ CREATE OR REPLACE PACKAGE test_pkg AS

TYPE inner_t IS RECORD (

-- works

-- lnum NUMBER


-- fails for test case: 1, 2

-- l_i_am_causing_troubles_here NUMBER, -- lnum NUMBER


-- fails for test case: NULL, 2

lnum NUMBER, l_i_am_causing_troubles_here NUMBER );

TYPE outer_t IS RECORD ( inner1 inner_t, inner2 inner_t );

FUNCTION parse(num1 NUMBER, num2 NUMBER) RETURN outer_t;

END test_pkg; """,

""" CREATE OR REPLACE PACKAGE BODY test_pkg AS

FUNCTION parse(num1 NUMBER, num2 NUMBER) RETURN outer_t IS l_outer outer_t; BEGIN l_outer.inner1.lnum := num1; l_outer.inner2.lnum := num2; RETURN l_outer; END parse;

END test_pkg; """ ]

with oracledb.connect(user=DB_USER, password=DB_PASSWORD, dsn=DB_DSN) as db_conn: with db_conn.cursor() as cursor: for ddl in DDLS: cursor.execute(ddl)

    outer_type = db_conn.gettype('TEST_PKG.OUTER_T')
    outer_var = cursor.var(outer_type)

    def parse(num1, num2):
        cursor.execute(
            """
            BEGIN
              :result := test_pkg.parse(:num1, :num2);
            END;
            """,
            result=outer_var,
            num1=num1,
            num2=num2
        )
        return outer_var.getvalue()

    outer = parse(None, None)
    assert outer is not None
    assert outer.INNER1 is None
    assert outer.INNER2 is None

    outer = parse(1, 2)
    assert outer is not None
    assert outer.INNER1 is not None
    assert outer.INNER2 is not None
    assert outer.INNER1.LNUM == 1
    assert outer.INNER2.LNUM == 2

    outer = parse(1, None)
    assert outer is not None
    assert outer.INNER1 is not None
    assert outer.INNER2 is None
    assert outer.INNER1.LNUM == 1

    outer = parse(None, 2)
    assert outer is not None
    assert outer.INNER1 is None
    assert outer.INNER2 is not None
    assert outer.INNER2.LNUM == 2

    print("All tests passed")