Mat*_*sen 2 python oracle plsql cx-oracle
从本指南中,我能够将简单数据类型(如cx_Oracle.NUMBER)的关联数组传递给 PL/SQL 过程。
CREATE OR REPLACE PACKAGE test
IS
TYPE t_ids IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
PROCEDURE foo(p_ids_i IN t_ids);
END;
/
Run Code Online (Sandbox Code Playgroud)
调用它:
ids = cursor.arrayvar(cx_Oracle.NUMBER, [1,2,3])
cursor.callproc('test.foo', [ids])
Run Code Online (Sandbox Code Playgroud)
但是,我想调用以下过程,foo而不是采用复杂类型。
CREATE OR REPLACE PACKAGE test
IS
TYPE r_foo IS RECORD (id NUMBER, name VARCHAR2(10));
TYPE t_complex IS TABLE OF r_foo INDEX BY PLS_INTEGER;
PROCEDURE foo(p_ids_i IN t_complex);
END;
/
Run Code Online (Sandbox Code Playgroud)
我尝试过各种事情,例如:
# Raises NotSupportedError: Variable_TypeByPythonType(): unhandled data type
foos = cursor.arrayvar((cx_Oracle.NUMBER, cx_Oracle.STRING), [(1, 'foo'), (2, 'bar')])
# Raises NotSupportedError: Variable_MakeArray(): type does not support arrays
foos = cur.arrayvar(cx_Oracle.OBJECT, [(1, 'foo'), (2, 'bar')])
Run Code Online (Sandbox Code Playgroud)
以下是失败的:
# Raises DatabaseError: ORA-04043: object TEST.R_FOO does not exist
record_type = conn.gettype('TEST.R_FOO')
Run Code Online (Sandbox Code Playgroud)
看起来您可以在包之外创建一个类型并引用它。
CREATE TYPE t_foo IS TABLE OF NUMBER; -- Not an Associative Array
Run Code Online (Sandbox Code Playgroud)
参考它:
t = conn.gettype('T_FOO')
Run Code Online (Sandbox Code Playgroud)
但是,不允许在包外创建关联数组的 RECORD。我可以用一个对象替换 RECORD,但我想不出有什么可以替换关联数组,这是 cx_Oracle 可以传入或传出的唯一集合类型。
完整代码:
PL/SQL:
-- 返回 12.1.0.2.0
从 v$instance 中选择版本;
创建或替换包测试
是
TYPE r_foo IS RECORD (id NUMBER, name VARCHAR2(10));
TYPE t_complex IS TABLE OF r_foo INDEX BY PLS_INTEGER;
程序 foo(p_ids_i 在 t_complex);
结尾;
/
创建或更换包装体测试
是
程序 foo(p_ids_i 在 t_complex)
是
开始
FOR i IN p_ids_i.FIRST .. p_ids_i.LAST 循环
DBMS_OUTPUT.PUT_LINE(p_ids_i(i).id || ' ' || p_ids_i(i).name);
结束循环;
结尾;
结尾;
/
- 以下按预期工作。
宣布
l_complex test.t_complex;
开始
l_complex(1).id := 1;
l_complex(1).name := '马修';
l_complex(2).id := 2;
l_complex(2).name := 'Moisen';
test.foo(l_complex);
结尾;
Python:
导入 cx_Oracle
打印 cx_Oracle.version # 5.3
打印 cx_Oracle.clientversion() # (12, 1, 0, 2, 0)
conn = cx_Oracle.connect('用户名/密码@sid')
cur = conn.cursor()
result = cur.execute("SELECT * FROM user_source WHERE name = 'TEST' and type = 'PACKAGE'")
# 这将成功打印包规范
对于结果行:
打印行
# 引发 DatabaseError: ORA-04043: 对象 TEST.R_FOO 不存在
conn.gettype('TEST.R_FOO')
# 引发 DatabaseError: ORA-04043: 对象 TEST.T_COMPLEX 不存在
conn.gettype('TEST.T_COMPLEX')
# 这引发了适当的异常,说我调用了该过程
# 不正确,证明我可以访问它。
cur.callproc('TEST.FOO', [])
在将 $ORACLE_HOME 等设置为我的 12c 客户端的情况下重新安装 cx_Oracle 后,我能够更进一步,但仍然遇到append操作错误。
导入 cx_Oracle
conn = cx_Oracle.connect('用户名/密码@sid')
# 这不再引发错误
recordTypeObj = conn.gettype('TEST.R_FOO')
tableTypeObj = conn.gettype('TEST.T_COMPLEX')
rec = recordTypeObj.newobject()
tab = tableTypeObj.newobject()
# 这工作正常
记录 ID = 1
rec.NAME = "foo"
# 这失败了
# cx_Oracle.NotSupportedError: Object_ConvertFromPython(): 未处理的数据类型 250
tab.append(rec)
这在 cx_Oracle 5.3 及更高版本中受支持。您必须使用支持此类事情的“对象”语法。
import cx_Oracle
conn = cx_Oracle.Connection("cx_Oracle/dev@localhost/orcl")
recordTypeObj = conn.gettype("TEST.R_FOO")
tableTypeObj = conn.gettype("TEST.T_COMPLEX")
tab = tableTypeObj.newobject()
rec = recordTypeObj.newobject()
rec.ID = 1
rec.NAME = "foo"
tab.append(rec)
rec = recordTypeObj.newobject()
rec.ID = 2
rec.NAME = "bar"
tab.append(rec)
cursor = conn.cursor()
cursor.callproc("test.foo", [tab])
Run Code Online (Sandbox Code Playgroud)