我试图将一个非常大的值(> 536MB)设置为cx_oracle CLOB变量,但它似乎在512MB标记附近失败。
conn = cx_Oracle.connect('foo/bar@baz')
cur = conn.cursor()
clob = cur.var(cx_Oracle.CLOB)
# The following is successful
clob.setvalue(0, 'A' * 1024 * 1024 * 511)
# The following fails
clob.setvalue(0, 'A' * 1024 * 1024 * 512)
第二个命令(或任何大于此值的值)失败并显示:
cx_Oracle.DatabaseError: ORA-03146: Invalid buffer length for TTC field
在对第二个命令进行三次调用之后,会话本身似乎已断开连接(而第一个命令不会发生这种情况)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
cx_Oracle.OperationalError: ORA-03135: connection lost contact
Process ID: 17567
Session ID: 137 Serial number: 9226
我在两个结果相同的情况下对此进行了测试:我本地环境中的Python连接到我的本地Oracle;和远程unix环境中的python连接到不同的远程Oracle环境。它们都在完全相同的1024 * 1024 * 512
行失败,这使我相信问题可能出在cx_Oracle上。
数据库版本:Oracle 12CR1
Oracle客户端版本:12.1.0.2.0
cx_Oracle version:在5.1.3和6.0.3上测试,结果相同
Python版本:2.7.13
一切都是64位。
我的用例是我需要调用PLSQL过程将CLOB插入数据库。我们的项目遵循特定的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, data)
cur.callproc('xxfoo_bar_pkg.insert_one',
keywordParameters={
'p_data_i': clob,
})
答案 0 :(得分:0)
感谢Anthony Tuininga对github repo的回应。
这显然是一个错误,但以下是一个有效的解决方法,它涉及更改PLSQL以使用OUT
param作为CLOB,并使用Oracle的empty_clob
来实例化该值。这会将LOB locator返回给python,这有点像指向LOB值的指针。您可以写入lob定位器,然后是commit,它会将值写入DB。我不清楚网络对此的影响,但性能似乎与
的DDL:
CREATE TABLE xxtest_cx_oracle (
data CLOB
);
CREATE OR REPLACE PACKAGE xxtest_cx_oracle_pkg
IS
-- Normal way; limited to ~ 511MB
PROCEDURE insert_one(
p_data_i IN CLOB
);
-- Workaround; supports > 511MB
PROCEDURE insert_one_workaround(
p_data_o OUT CLOB
);
END;
CREATE OR REPLACE PACKAGE BODY xxtest_cx_oracle_pkg
IS
-- Normal way; limited to ~ 511MB
PROCEDURE insert_one(
p_data_i IN CLOB
)
IS
BEGIN
INSERT INTO xxtest_cx_oracle (
data
) VALUES (
p_data_i
);
END;
-- Workaround; supports > 511MB
PROCEDURE insert_one_workaround(
p_data_o OUT CLOB
)
IS
BEGIN
INSERT INTO xxtest_cx_oracle (
data
) VALUES (
empty_clob()
) RETURNING data INTO p_data_o;
END;
END;
的Python:
import cx_Oracle
conn = cx_Oracle.connect('FOO/bar@BAZ')
cur = conn.cursor()
# Old way, using normal IN param; only supports ~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()
# New way using empty_clob and OUT param; supports > 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
会失败:
>>> cursor.close()
>>> conn.close()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
cx_Oracle.DatabaseError: DPI-1054: connection cannot be closed when open statements or LOBs exist
但您可以del lob
或让conn
超出范围来解决此问题。