当前位置 : 首页 » 互动问答 » 正文

cx_Oracle使用cx_Oracle.DatabaseError设置CLOB var的较大值失败:ORA-03146:TTC字段的缓冲区长度无效

分类 : 互动问答 | 发布时间 : 2018-04-27 17:44:03 | 评论 : 1 | 浏览 : 56 | 喜欢 : 0

我试图设置一个非常大的值(> 536MB)到一个cx_oracle CLOB变量中,但它似乎在512MB标记周围失败。

 conn = cx_Oracle.connect('foo / bar @ baz')
cur = conn.cursor()
clob = cur.var(cx_Oracle.CLOB)

#以下是成功的
clob.setvalue(0,'A'* 1024 * 1024 * 511)

#以下失败
clob.setvalue(0,'A'* 1024 * 1024 * 512)

第二个命令(或大于此值的任何值)失败:

 cx_Oracle.DatabaseError:ORA-03146:TTC字段的缓冲区长度无效

对第二个命令进行三次调用后,会话本身似乎已断开连接(而第一个命令不会发生这种情况)

 Traceback(最近一次调用最后一次):
  在<module>中的文件“<stdin>”,第1行
cx_Oracle.OperationalError:ORA-03135:连接失去联系
进程ID:17567
会话ID:137序列号:9226

我已经在两个具有相同结果的环境中测试了这一点:在本地环境中连接到本地Oracle的Python;以及连接到不同远程Oracle环境的远程unix环境中的python。两者都在完全相同的 1024 * 1024 * 512 行中失败,这导致我相信问题可能与cx_Oracle有关。

数据库版本:Oracle 12CR1

Oracle Client版本:12.1。 0.2.0

cx_Oracle版本:在5.1.3和6.0.3上测试,结果相同

Python版本:2.7.13

一切都是64位。


我的用例是我需要调用一个将CLOB插入数据库的PLSQL过程。我们的项目遵循特定的ERP规则集,并且我们不允许执行直接的DML。我有类似于以下代码:

 data = get_user_provided_data()

conn = cx_Oracle.connect('foo / bar @ baz')
cur = conn.cursor()
clob = cur.var(cx_Oracle.CLOB)
clob.setvalue(0,数据)

cur.callproc( 'xxfoo_bar_pkg.insert_one',
               keywordParameters = {
                   'p_data_i':clob,
               })

回答(1)

  • 1楼
  • 感谢Anthony Tuininga对 github repo .

    的回应这显然是一个错误,但以下是一个有效的解决方法,它涉及到将PLSQL更改为对CLOB使用OUT参数,并使用Oracle的empty_clob实例化该值。这将返回一个 LOB locator 回到python,这有点像一个指向LOB值的指针。您可以写入lob定位器,然后进行提交,将提交值写入数据库。我不清楚这个网络的影响,但性能似乎类似于

    DDLs:

     CREATE TABLE xxtest_cx_oracle(
        数据CLOB
    );
    
    
    创建或更换软件包xxtest_cx_oracle_pkg
    IS
         - 正常的方式;限于〜511MB
        PROCEDURE insert_one(
            p_data_i在CLOB中
        );
    
         - 解决方法;支持> 511MB
        PROCEDURE insert_one_workaround(
            p_data_o OUT CLOB
        );
    结束;
    
    创建或更换包装体xxtest_cx_oracle_pkg
    IS
         - 正常的方式;限于〜511MB
        PROCEDURE insert_one(
            p_data_i在CLOB中
        )
        IS
        开始
            INSERT INTO xxtest_cx_oracle(
                数据
            )VALUES(
                p_data_i
            );
        结束;
    
         - 解决方法;支持> 511MB
        PROCEDURE insert_one_workaround(
            p_data_o OUT CLOB
        )
        IS
        开始
            INSERT INTO xxtest_cx_oracle(
                数据
            )VALUES(
                EMPTY_CLOB()
            )返回数据INTO p_data_o;
        结束;
    结束;
    

    Python:

     import cx_Oracle
    conn = cx_Oracle.connect('FOO / bar @ BAZ')
    cur = conn.cursor()
    
    #旧的方式,使用正常的IN参数;只支持〜511MB
    data = cur.var(cx_Oracle.CLOB)
    data.setvalue(0,'A'* 1024 * 1024 * 511)
    cur.callproc('xxtest_cx_oracle_pkg.insert_one',(data,))
    conn.commit()
    
    #使用empty_clob和OUT参数的新方法;支持> 511MB
    data = cur.var(cx_Oracle.CLOB)
    cur.callproc('xxtest_cx_oracle_pkg.insert_one_workaround',(data))
    lob = data.getvalue()
    lob.write('A'* 1024 * 1024 * 512)
    conn.commit()
    

    我注意到的一件事是试图关闭conn会失败:<notranslate >>>> cursor.close() >>> conn.close() 回溯(最近一次通话最后):    在<module>中的文件“<stdin>”,第1行 cx_Oracle.DatabaseError:DPI-1054:存在打开的语句或LOB时,无法关闭连接

    但是你可以只是

    超出范围来解决这个问题。del lob or let conn go out of scope to resolve this.

相关阅读: