Нежелательные новые строки при буферизации результата sqlplus в xml-файл - PullRequest
1 голос
/ 21 марта 2019

Я пытаюсь извлечь некоторые данные из моей базы данных в файл XML. Для этого используйте bash-скрипт, который вызывает команду sqlplus и помещает результат в новый файл.

Проблема возникает после извлечения результата. Мой xml-файл больше не действителен, так как добавлено несколько нежелательных новых строк ...

Вот пример того, что я хочу:

<xml>
 <element>John</element>
 <element>some data</element>
 <element>a longer data line</element>
</xml>

И вот что я получил:

<xml>
 <element>John</element>
 <element>some data</eleme
 nt>
 <element>a longer data 
 line</element>
</xml>

Кажется, что самые длинные строки обрезаются, но я установил размер строки в 32767 в Sqlplus, и эти строки не такие длинные ...

Вот как выглядит моя команда sqlplus:

sqlplus -s {connection} << EOF
set serveroutput on size unlimited
set feedback off
set termout off
set linesize 32767

spool file.xml;

DECLARE
l_xmltype XMLTYPE;
l_ctx dbms_xmlgen.ctxhandle;
v_clob CLOB;
v_clob_length INTEGER;
pos INTEGER;
buffer VARCHAR2(32767);
amount BINARY_INTEGER := 32767;

BEGIN

l_ctx := dbms_xmlgen.newcontext('SELECT a.rowid, a.* FROM mytable a');
l_xmltype := dbms_xmlgen.getXmlType(l_ctx);
dbms_xmlgen.closeContext(l_ctx);

v_clob := l_xmltype.getClobVal;
v_clob_length := length(v_clob);

WHILE pos < clob_length LOOP
 dbms_lob.read(v_clob, amount, pos, buffer);
 dbms_output.put_line(buffer);
 pos := pos + amount;
END LOOP;

END;
/
EOF
Spool off;

Есть ли у вас какие-либо подсказки, чтобы помочь мне решить эту проблему?

Спасибо!

1 Ответ

0 голосов
/ 21 марта 2019

Как предположил @kfinity, это связано с обработкой CLOB, а также с тем, как работает dbms_output. Вы читаете CLOB кусками по 32 КБ и записываете каждый из них с помощью put_line(), который добавляет символ новой строки после каждого блока 32 КБ. Они не совпадают с какими-либо существующими разрывами строк в вашем XML-документе, поэтому вы получаете исходные разрывы, а затем дополнительные разрывы, которые выглядят несколько случайными и в середине текста, но на самом деле находятся в предсказуемых местах.

Очевидное решение состоит в том, чтобы переключиться с put_line() на put(), но это нарушит максимальный размер буфера и выдаст что-то вроде "ORU-10028: переполнение строки, ограничение 32767 байт на строку".

Вместо чтения в фиксированных кусках по 32 Кб, вы можете читать по одной строке за раз; CLOB на самом деле не понимает строки как таковые, но вы можете искать разрывы строк, например:

WHILE pos < v_clob_length LOOP
  -- read to next newline if there is one, rest of CLOB if not
  if dbms_lob.instr(v_clob, chr(10), pos) > 0 then
    amount := dbms_lob.instr(v_clob, chr(10), pos) - pos;
    dbms_lob.read(v_clob, amount, pos, buffer);
    pos := pos + amount + 1; -- skip newline character
  else
    amount := 32767;
    dbms_lob.read(v_clob, amount, pos, buffer);
    pos := pos + amount;
  end if;

  dbms_output.put_line(buffer);
END LOOP;

if ищет символ новой строки после текущей позиции. Если он находит один, то сумма вычисляется как количество символов от текущей позиции до этой новой строки (точнее, минус один - так как вы не хотите самой новой строки), он читает столько символов, а затем корректирует позицию на прочитанное количество плюс один (чтобы пропустить новую строку - что вам не нужно / не нужно, так как put_line() добавляет еще одно).

Если он не находит его, он читает до 32 Кб - возможно, только один раз; если осталось больше символов, которые могут остаться без разрыва строки, тогда будет выполнено второе чтение, но все равно добавится эта мошенническая дополнительная новая строка и будет разбита эта строка. Однако с помощью dbms_output вы ничего не можете сделать с этим, вам придется переключиться на utl_file запись на сервер, а не на буферизацию на клиенте.

...