Нежелательные новые строки при буферизации результата sqlplus в XML-файл

#xml #linux #oracle #sqlplus #spool

#xml #linux #Oracle #sqlplus #буферизация

Вопрос:

Я пытаюсь извлечь некоторые данные из своей базы данных в 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>
  

Кажется, что самые длинные строки вырезаны, но я установил linesize равным 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. Пожалуйста, по крайней мере, покажите нам ваши команды query и sqlplus SET. Но linesize его не волнует длина строк внутри вашего XML, его волнует максимальное количество символов, которые могут быть напечатаны одновременно. Таким образом, если clob / xml / что бы вы ни выбрали, длиннее 32767 символов, в этот момент будет добавлен перевод строки. Возможно, это ваша проблема?

2. Да, извините, я отредактировал свой вопрос, добавив команды SET и некоторый код!

Ответ №1:

Как предположил @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 кб — надеюсь, только один раз; если осталось больше символов may без разрыва строки, то он выполнит второе чтение, но все равно добавит эту неправильную дополнительную новую строку и разорвет эту строку. С помощью dbms_output этого мало что можно сделать, однако вам нужно будет переключиться на utl_file запись на сервер вместо буферизации на клиента.