我试图将一个非常大的值(>536‘m)设置为一个cx_oracle CLOB变量,但它似乎在512’m标记附近失败。
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;python在远程unix环境中连接到不同的远程Oracle环境。它们都在完全相同的1024 * 1024 * 512
线上失败,这使我相信问题可能与cx_Oracle有关。
数据库版本: Oracle 12CR1
Oracle客户端版本: 12.1.0.2.0
cx_Oracle版本: 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,
})
发布于 2017-11-15 22:05:34
感谢安东尼·图宁加在github回购的回应。
这显然是一个错误,但下面是一个有效的解决方法,它涉及更改PLSQL以使用CLOB的OUT
param,以及使用Oracle的empty_clob
实例化该值。这将返回一个LOB定位器到python,这有点像一个指向LOB值的指针。您可以写到lob定位器,然后是commit,它会将值写入DB。我不清楚这对网络的影响,但性能似乎类似于
DDLs:
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
超出范围来解决这个问题。
https://stackoverflow.com/questions/47276246
复制相似问题