Open
Description
-
What versions are you using?
Python 3.9.13 (Windows, Oracle Linux 8)
Database: Oracle 19c
oracledb: 3.1.0, 3.1.1 -
Is it an error or a hang or a crash?
DatabaseError with ORA-22922 -
What error(s) or behavior you are seeing?
I have observed the error "ORA-22922: nonexistent LOB value" after reading around 100 LOB objects. If lobAutoload is configured (refCursor.outputtypehandler = _connection_lobautoload_handler) I fetched the async cursor well.
I noticed that the issue is present only for the async version in oracledb module, sync version works well, my old code based on cx_Oracle + cx_Oracle_async also works well with this case.
-
Does your application call init_oracle_client()?
no -
Include a runnable Python script that shows the problem.
create or replace package pkg_test1 is
function encode(p_prefix nvarchar2, p_idx number) return blob;
function test return sys_refcursor;
end pkg_test1;
/
create or replace package body pkg_test1 is
gServerCodepage v$nls_parameters.value%type;
function encode(p_prefix nvarchar2, p_idx number) return blob
as
l_value nvarchar2(2000);
l_blob blob;
l_raw long raw;
begin
l_value := p_prefix || '-' || to_char(p_idx);
dbms_lob.createtemporary(l_blob, true, dbms_lob.call);
if gServerCodepage != 'AL32UTF8' then
l_raw := utl_raw.cast_to_raw(convert(l_value, 'AL32UTF8', gServerCodepage));
else
l_raw := utl_raw.cast_to_raw(l_value);
end if;
dbms_lob.writeappend(l_blob, utl_raw.length(l_raw), l_raw);
return l_blob;
end encode;
function test return sys_refcursor
as
l_cur sys_refcursor;
begin
open l_cur for
SELECT rownum id,
pkg_test1.encode('col1', rownum) blob1,
pkg_test1.encode('col2', rownum) blob2,
pkg_test1.encode('col3', rownum) blob3,
pkg_test1.encode('col4', rownum) blob4,
pkg_test1.encode('col5', rownum) blob5,
pkg_test1.encode('col6', rownum) blob6,
pkg_test1.encode('col7', rownum) blob7,
pkg_test1.encode('col8', rownum) blob8,
pkg_test1.encode('col9', rownum) blob9,
pkg_test1.encode('col10', rownum) blob10,
pkg_test1.encode('col11', rownum) blob11,
pkg_test1.encode('col12', rownum) blob12
FROM dual
--CONNECT BY LEVEL <= 1000;
CONNECT BY LEVEL <= 25;
return l_cur;
end test;
begin
select
(select p.value from v$nls_parameters p where p.PARAMETER = 'NLS_NCHAR_CHARACTERSET')
into gServerCodepage
from dual;
end pkg_test1;
/
import oracledb
import asyncio
def _connection_lobautoload_handler(cursor, metadata):
""" Handler to configure cursor types in connection """
if metadata.type_code is oracledb.DB_TYPE_CLOB:
return cursor.var(oracledb.DB_TYPE_LONG, arraysize=max(cursor.arraysize, 4096))
if metadata.type_code is oracledb.DB_TYPE_BLOB:
return cursor.var(oracledb.DB_TYPE_LONG_RAW, arraysize=max(cursor.arraysize, 4096))
if metadata.type_code is oracledb.DB_TYPE_NCLOB:
return cursor.var(oracledb.DB_TYPE_LONG_NVARCHAR, arraysize=max(cursor.arraysize, 4096))
def main():
pool = oracledb.create_pool(
user = "user",
password ="password",
dsn = "connection-string"
)
con = pool.acquire()
try:
with con.cursor() as cur, con.cursor() as refCursor:
cur.execute(
""" begin :cur := pkg_test1.test; end;""",
cur = refCursor)
rowCount = 0
while True:
row = refCursor.fetchone()
if row == None: break
for idx in range(0, len(refCursor.description)):
if refCursor.description[idx][0].startswith('BLOB'):
value = (row[idx].read()).decode('utf-8') if row[idx] != None and isinstance(row[idx], oracledb.LOB) else row[idx]
print(f'{rowCount}: {refCursor.description[idx][0]}={value}')
rowCount += 1
finally:
con.close()
async def mainAsync():
pool = oracledb.create_pool_async(
user = "user",
password ="password",
dsn = "connection-string"
)
con = await pool.acquire()
try:
async with con.cursor() as cur, con.cursor() as refCursor:
#refCursor.outputtypehandler = _connection_lobautoload_handler
await cur.execute(
""" begin :cur := pkg_test1.test; end;""",
cur = refCursor)
rowCount = 0
while True:
row = await refCursor.fetchone()
if row == None: break
for idx in range(0, len(refCursor.description)):
if refCursor.description[idx][0].startswith('BLOB'):
value = (await row[idx].read()).decode('utf-8') if row[idx] != None and isinstance(row[idx], oracledb.AsyncLOB) else row[idx]
print(f'{rowCount}: {refCursor.description[idx][0]}={value}')
rowCount += 1
finally:
await con.close()
if __name__ == '__main__':
print ('Test case#1: oracledb (sync mode).')
main()
print ('Test case#2: oracledb (async mode).')
asyncio.run(mainAsync())