#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
запись на сервер вместо буферизации на клиента.