我正在尝试从基于OCI的客户端调用存储过程(在oracle db 11g上)。该过程包含对象类型的单个OUT参数。
问题:我总是得到“ORA-21525:属性号或(索引处的集合元素)%s违反了它的约束”错误。
如果有人能给我一个暗示可能是什么原因,我将非常感激。
注意:有趣的是,在下列情况下一切正常:
另外,我发现了以下内容:
pOutParam = NULL
并绑定它,但后来我遇到了“访问冲突,从地址00000读取”。这是代码。 PLSQL对象类型:
CREATE OR REPLACE TYPE dl_fake_type AS OBJECT
(
attr_one NUMBER(12,0),
attr_two NUMBER(12,0)
);
/
程序(为简洁起见,我跳过程序声明)
PROCEDURE dl_fake_fun(out_result OUT dl_fake_type)
IS
l_result dl_fake_type;
BEGIN
SELECT dl_fake_type(23, 35) INTO l_result FROM DUAL;
out_result := l_result;
END dl_fake_fun;
C ++ OCI代码(为简洁起见,没有连接初始化)。注意:我使用MSVS2013和一些C ++ 11特性,比如std :: string :: front()方法。
// ...
typedef basic_string<OraText, char_traits<OraText>, allocator<OraText> > OraTextString;
typedef basic_ostringstream<OraText, char_traits<OraText>, allocator<OraText> > OraOStringStream;
// Check if ociStatus == OCI_SUCCESS. If not, then print error and assert.
void checkOciStatus(const sword ociStatus, OCIError * errorHandle = NULL);
// ...
const OraTextString DL_FAKE_TYPE = (OraText const *)"DL_FAKE_TYPE";
const OraTextString outParamName = (OraText const *)":out_param";
OraOStringStream query(ios::ate);
query << "BEGIN my_dummy_pkg.dl_fake_fun(" << outParamName << "); END;";
const OraTextString & queryString = query.str();
OCIStmt * statement;
const ub4 executionMode = OCI_DEFAULT;
checkOciStatus(OCIHandleAlloc(envhp, (void **)&statement, OCI_HTYPE_STMT, /* xtramemsz */ 0, /* usrmempp */ NULL), errhp);
checkOciStatus(OCIStmtPrepare(statement, errhp, queryString.c_str(), queryString.length(), OCI_NTV_SYNTAX, executionMode), errhp);
const OraTextString schemaName = (OraText const *)"MY_SCHEMA_NAME";
OCIType * typeDescriptor = NULL;
checkOciStatus(
OCITypeByName(
envhp,
errhp,
svchp,
schemaName.c_str(),
schemaName.size(),
DL_FAKE_TYPE.c_str(),
DL_FAKE_TYPE.length(),
/* version name */ NULL,
/* version name length */ 0,
OCI_DURATION_SESSION,
OCI_TYPEGET_HEADER,
&typeDescriptor
),
errhp
);
OCIBind* bindHandle = NULL;
checkOciStatus(OCIBindByName(statement, &bindHandle, errhp, outParamName.c_str(), outParamName.length(), NULL, 0, SQLT_NTY, NULL, NULL, NULL, 0, NULL, executionMode), errhp);
void * pOutParam = NULL;
checkOciStatus(OCIObjectNew(envhp, errhp, svchp, OCI_TYPECODE_REF, typeDescriptor, NULL, OCI_DURATION_DEFAULT, /* true = value, false = ref */ FALSE, &pOutParam), errhp);
checkOciStatus(OCIBindObject(bindHandle, errhp, typeDescriptor, &pOutParam, NULL, NULL, 0), errhp);
checkOciStatus(OCIStmtExecute(svchp, statement, errhp, 1, 0, NULL, NULL, executionMode), errhp);
cout << "executed." << endl;
// ...
提前致谢。
答案 0 :(得分:0)
经过一段时间的挣扎,我找到了适合我的解决方案。
有两点对成功绑定至关重要:
OCIObjectNew()
来创建,因为它是在问题提供的代码中完成的。注意:我还考虑过直接创建表示相应C ++类型的结构实例的选项。但即使满足第二个条件,这也不起作用。OCINumber
的字段必须设置为某个特定值(例如OCINumberFromInt()
),指向OCIString
的字段必须设置为NULL
。输出对象绑定的另一个gocha(在绑定输出嵌套表时也会观察到)是传递给OCIBindObject
的对象的第二个指针是 second 指针,原因是:看来,OCI将其视为输出对象的指针数组。这意味着,包含第一个指针(指向第二个指针)的变量必须有效才能调用OCIExecuteStatement
。这个meens,如果你想提取绑定进程来分离函数,那么你必须传递 second 指向对象的指针,然后将其传递给OCIBindObject()
。否则它将无法工作。
我希望,这些提示可以帮助某人避免在OCI(和Oracle DB)文档的丛林中经历数小时的挫折和痛苦的挣扎。