13.03.2012

Передача XML>32k через ADO в Oracle

При работе с XML+ADO+Oracle всё прекрасно, пока размер потока с XML не превышает 32k
 Обмен данными в XML имеет ряд преимуществ:
  • малое время выполнения транзакции;
  • повышение надежности обработки информации в многопользовательских системах;
  • упрощение отладки исполняемого модуля.
Очевидный путь - передача в процедуру структуры XML в виде переменной типа CLOB. Переменная легко преобразуется в XML-объект и распарсивается с помощью пакета DBMS_XMLDOM:
declare
 xmltypedoc xmltype;
 doc DBMS_XMLDOM.domdocument;
 docElem DBMS_XMLDOM.DOMElement;
 c clob
begin
 ...
 xmltypedoc := xmltype(c);
 doc := dbms_xmldom.newDOMDocument(xmltypedoc);
 docElem := dbms_xmldom.getDocumentElement(doc);
 ...
end;
При попытке передать через ADO XML-структуру объемом больше 32767B, получим ошибку ORA-01460: затребовано нереализованное или неразумное преобразование.
Почему так происходит?
Посмотрим, что на подобный вопрос ответил о.Том:
So, how does this apply to CLOBs as parameters to a Packaged Procedure?
As an example, if I have a procedure that will parse & process an XML document, how do you perform the "CALL(SOME_PACKAGE.SOME_PROC('...'))" from the client if the XML document is larger than 4K?
I am using ADO to execute Procedure call from NT service against a 9i install on SUN UNIX. Is there a way to do this with the ADO or am I forced to use Embedded SQL (Pro*C/C++)? If the later, how would I perform the embedded execution?
Thanks!!
Followup April 11, 2002 - 7pm Central time zone:
The XML document can be 32k (the varchar2 limit in PLSQL is 32k, not 4000 bytes).
If you have >32k -- i would be tempted to use a global temporary table, stream the data into that and call a procedure to work against that clob/blob. you can do all of this from VB. No C needed.
32k - это ограничение длины типа данных varchar2. Так как ADO весьма криво работает (точнее, вообще не работает) c типом данных ftOraCLOB, сделаем так, как советует Т. Кайт: поместим данные в промежуточное BLOB поле, а затем сконвертируем BLOB в CLOB.
Код для вставки данных в промежуточную таблицу (Delphi):
procedure PutXMLToPkg;
var
 LStrStream : TMemoryStream;
begin
 try
  LStrStream := TMemoryStream.Create;
  xml.SaveToStream(LStrStream); //TXMLDocument;
  ADOQuery1.Close;
  ADOQuery1.Parameters.ParamByName('pXML').LoadFromStream(LStrStream, ftBlob);
  // запрос выполняет insert
  ADOQuery1.ExecSQL;
 except
  on E : Exception do
  ShowMessage(E.Message);
 end;
end;
Функция на сервере для конвертации BLOB->CLOB
FUNCTION FNC_Blob_Para_Clob(pBLOB BLOB)
RETURN CLOB
IS
 nCLOB CLOB;
 nSRC_OFFSET NUMBER;
 nDEST_OFFSET NUMBER;
 nLANG_CONTEXT NUMBER := DBMS_LOB.default_lang_ctx;
 nWARNING NUMBER;
 nAMOUNT NUMBER;
BEGIN
 IF DBMS_LOB.GETLENGTH(pBLOB) > 0 THEN
  DBMS_LOB.CREATETEMPORARY(nCLOB, TRUE);
  nSRC_OFFSET := 1;
  nDEST_OFFSET := 1;
  nAMOUNT := DBMS_LOB.GETLENGTH(pBLOB);
  DBMS_LOB.CONVERTTOCLOB( nCLOB
   , pBLOB
   , nAMOUNT
   , nSRC_OFFSET
   , nDEST_OFFSET
-- кириллическая кодировка из sys.prop$
   , NLS_CHARSET_ID ('CL8MSWIN1251')
   , nLANG_CONTEXT
   , nWARNING);
  RETURN nCLOB;
 ELSE
  nCLOB := TO_CLOB('');
  RETURN nCLOB;
 END IF;
 DBMS_LOB.FREETEMPORARY(nCLOB);
END;
(полный список значений NLS_CHARSET_ID здесь)
Применение функции:
declare
 b blob;
 c clob;
begin
 select fblob into b from TEMP_LOB where ID = 1;
 select FNC_Blob_Para_Clob(b) into c from dual;
end;

Комментариев нет :