2015-01-29 2 views
0

Я пытаюсь вызвать хранимую процедуру (на oracle db 11g) из клиента на основе OCI. Процедура содержит один параметр OUT для типа объекта.OCI: Как связать выходной параметр типа объекта

Проблема: я всегда получаю «ORA-21525: номер атрибута или (элемент коллекции по индексу)% s нарушил его ограничения».

Я был бы очень признателен, если бы кто-нибудь мог дать мне подсказку, что может быть причиной.

Примечание: Интересно, что все работает нормально в следующих случаях:

  • заменяет тип возвращаемого от типа объекта к вложенной таблице таких объектов.
  • Я заменяю тип возвращаемого типа на некоторый примитивный тип, например. NUMBER
  • Я делаю это направление параметра IN и привязываю его таким же образом.

Кроме того, я обнаружил следующие вещи:

  • Та же ошибка присутствует, независимо от того, если я возвращать результат в качестве параметра OUT из процедуры, или если I пользователя RETURN из функции.
  • Если я попытаюсь вызвать хранимую процедуру с помощью PLSQL-скрипта, все будет выглядеть так, как ожидалось, без ошибок.
  • Я также попытался создать параллельную структуру C++ из двух полей OCINumber и использовать ее объект вместо вызова OCIObjectNew(), но получить ту же ошибку.
  • Я также попытался установить 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; 

С ++ ОКИ код (без инициализации соединения для краткости). Примечание. Я использую 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

После периода боев я нашел решение, которое сработало для меня.

Существовали две точки, которые имеют жизненно важное значение для успешного связывания:

  1. Объект, который предназначен быть связанными должны быть созданы с помощью вызова OCIObjectNew(), как это делается в коде, снабженного вопрос. Обратите внимание: я также рассмотрел возможность создания экземпляра структуры, представляющей соответствующий тип C++ напрямую. Но это не сработало, даже если второе условие было выполнено.
  2. После создания объекта все его поля должны быть инициализированы. То есть поля типа OCINumber должны быть установлены на определенное значение (например, с OCINumberFromInt()), а указатели на OCIString должны быть установлены в NULL.

Другой Гоча выходного объекта связывания (также наблюдается при связывании выходных вложенных таблиц) является то, что второй указатель на объект передается OCIBindObject является второй указатель причина: кажется, что OCI рассматривает его как массив указателей для вывода объектов. Это означает, что переменная, содержащая первый указатель (к которому указывает второй указатель), должна быть действительной до тех пор, пока не вызывается OCIExecuteStatement. Это означает, что если вы хотите извлечь процесс привязки к отдельной функции, вам необходимо передать второй указатель на объект, который затем передается в OCIBindObject(). Иначе это не сработает.

Надеюсь, что эти подсказки помогут кому-то избежать часовых разочарований и болезненной борьбы с джунглями документации OCI (и Oracle DB).

Смежные вопросы