Странный результат Oracle XMLType.getClobVal ()

Я использую Oracle 11g (на Red Hat). У меня есть простая обычная таблица со столбцом XMLType:

CREATE TABLE PROJECTS
(
  PROJECT_ID NUMBER(*, 0) NOT NULL,
  PROJECT SYS.XMLTYPE,
);

Используя Oracle SQL Developer (в Windows), я делаю:

select T1.PROJECT P1 from PROJECTS T1 where PROJECT_ID = '161';

Оно работает. Я получаю одну ячейку. Я могу дважды щелкнуть и загрузить весь XML-файл.

Затем я попытался получить результат в виде CLOB:

select T1.PROJECT.getClobVal() P1 from PROJECTS T1 where PROJECT_ID = '161';

Оно работает. Я получаю одну ячейку. Я могу дважды щелкнуть, увидеть весь текст и скопировать его. Но есть проблема. Когда я копирую его в буфер обмена, я получаю только первые 4000 символов. Кажется, что в позиции 4000 есть символ 0x00, а остальная часть CLOB не копируется.

Чтобы подтвердить это, я написал проверку в java:

// ... create projectsStatement
Reader reader = projectsStatement.getResultSet().getCharacterStream( "P1" );
BufferedReader bf = new BufferedReader( reader );
char buffer[] = new char[ 1024 ];
int count = 0;
int globalPos = 0;
while ( ( count = bf.read( buffer, 0, buffer.length ) ) > 0 )
    for ( int i = 0; i < count; i++, globalPos++ )
        if ( buffer[ i ] == 0 )
            throw new Exception( "ZERO at " + Integer.toString(globalPos) );

Reader возвращает полный XML, но мое исключение выбрасывается, потому что в позиции 4000 есть нулевой символ. Я мог бы удалить этот единственный байт, но это было бы довольно странным обходным путем.

Я там не использую VARCHAR2, но, может быть, эта проблема как-то связана с ограничением VARCHAR2 (4000 байт)? Есть другие идеи? Это ошибка Oracle или мне что-то не хватает?

-------------------- Редактировать --------------------

Значение было вставлено с помощью следующей хранимой процедуры:

create or replace
procedure addProject( projectId number, projectXml clob ) is
  sqlstr varchar2(2000);
begin

  sqlstr := 'insert into projects ( PROJECT_ID, PROJECT ) VALUES ( :projectId, :projectData )';
  execute immediate sqlstr using projectId, XMLTYPE(projectXml);

end;

Код Java для его вызова:

try ( CallableStatement cs = connection.prepareCall("{call addProject(?,?)}") )
{
    cs.setInt( "projectId", projectId );
    cs.setCharacterStream( "projectXml", new StringReader(xmlStr) , xmlStr.length() );
    cs.execute();
}

-------------------- Редактировать. ПРОСТОЙ ТЕСТ --------------------

Я буду использовать все, что узнал из ваших ответов. Создайте простейшую таблицу:

create table T1 ( P XMLTYPE );

Подготовьте два CLOB с XML. Первый с нулевым символом, второй без.

declare
  P1 clob;
  P2 clob;
  P3 clob;
begin

  P1 := '<a>';
  P2 := '<a>';
  FOR i IN 1..1000 LOOP
    P1 := P1 || '0123456789' || chr(0);
    P2 := P2 || '0123456789';
  END LOOP;
  P1 := P1 || '</a>';
  P2 := P2 || '</a>';

Проверьте, находится ли null в первом CLOB, а не во втором:

DBMS_OUTPUT.put_line( DBMS_LOB.INSTR( P1, chr(0) ) );
DBMS_OUTPUT.put_line( DBMS_LOB.INSTR( P2, chr(0) ) );

Получим ожидаемо:

14
0

Попробуйте вставить первый CLOB в XMLTYPE. Он не будет работать. Невозможно вставить такое значение:

insert into T1 ( P ) values ( XMLTYPE( P1 ) );

Попробуйте вставить второй CLOB в XMLTYPE. Это будет работать:

insert into T1 ( P ) values ( XMLTYPE( P2 ) );

Попробуйте прочитать вставленный XML в третий CLOB. Это будет работать:

select T.P.getClobVal() into P3 from T1 T where rownum = 1;

Проверьте, есть ли нуль. НЕТ НУЛЯ:

DBMS_OUTPUT.put_line( DBMS_LOB.INSTR( P3, chr(0) ) );

Кажется, что внутри базы данных нет нуля, и пока мы находимся в контексте PL / SQL, нет нуля. Но когда я пытаюсь использовать следующий SQL в SQL Developer (в Windows) или в Java (в Red Hat EE и Tomcat7), я получаю нулевой символ в позиции 4000 во всех возвращаемых CLOB:

select T.P.getClobVal() from T1 T;

BR, JM


person Mikosz    schedule 23.11.2012    source источник
comment
Можете ли вы записать его в файл с помощью utl_file и посмотреть, как выглядит содержимое? Вы тоже можете попробовать select XMLType.getClobVal(PROJECT) from PROJECTS;? (Хотя ничего функционально отличного)   -  person AnBisw    schedule 24.11.2012
comment
Как была заселена колонна? Вы уверены, что проблема связана с поиском - звучит маловероятно, если разные клиенты видят одно и то же. Вы также можете выбрать подстроку значения и посмотреть, есть ли там нулевой символ.   -  person Alex Poole    schedule 24.11.2012
comment
Я запустил utlfile.sql и prvtfile.plb, но все еще не могу использовать utl_file (ORA-06521: PL / SQL: функция сопоставления ошибок), извините.   -  person Mikosz    schedule 26.11.2012
comment
Я использовал XMLType.getClobVal (PROJECT), и там есть нулевой символ. Затем я использовал T1.PROJECT.getBlobVal (nls_charset_id ('AL32UTF8')), и в загруженном BLOB-объекте НЕТ нулевого символа (с использованием SQL Developer или Java).   -  person Mikosz    schedule 26.11.2012
comment
Я добавил процедуру вставки в свой пост выше.   -  person Mikosz    schedule 26.11.2012
comment
@Alex Я отредактировал свой пост. Кажется, что действительно null отсутствует, потому что невозможно заполнить столбец XMLTYPE таким образом. Похоже, проблема в поиске.   -  person Mikosz    schedule 27.11.2012


Ответы (4)


это не ошибка Oracle (он отлично хранит и извлекает \ 0. Это ошибка клиента / Windows (разные клиенты ведут себя по-разному в отношении "NUL", как и окна)

chr (0) на самом деле не является допустимым символом в не-blob (мне любопытно, как вы когда-либо заставляете XMLType принимать его в первую очередь, поскольку обычно он не анализирует).

\ 0 используется в C для обозначения конца строки (терминатор NUL), и некоторые графические интерфейсы пользователя прекратят обработку строки в этой точке. Например:

![SQL> select 'IM VISIBLE'||chr(0)||'BUT IM INVISIBLE'
  2  from dual
  3  /

'IMVISIBLE'||CHR(0)||'BUTIM
---------------------------
IM VISIBLE BUT IM INVISIBLE

SQL>

но жаба с этим не справляется: TOAD

Как видите, разработчик sql работает лучше:

Разработчик SQL

но если вы скопируете его, буфер обмена скопирует его только до нулевого символа. эта ошибка копирования и вставки не является ошибкой разработчиков SQL, это проблема с буфером обмена Windows, который не позволяет NUL правильно вставлять.

вы должны просто replace(T1.PROJECT.getClobVal(), chr(0), null) обойти это при использовании sql-разработчика / буфера обмена Windows.

person DazzaL    schedule 23.11.2012
comment
Спасибо за ответ. Конечно, буфер обмена - это проблема Windows, но это не главная проблема, это просто способ ее проверить. Я хочу знать, почему там null :). И я думаю, что это не проблема клиента. Когда я загружаю одну ячейку из этой таблицы (используя SQL Developer или Blob в Java), нулевой символ отсутствует. Он появляется только тогда, когда я использую PROJECT.getClobVal () (как в SQL Developer, так и в Java). Мне кажется, что getClobVal () вставляет нулевой символ в позицию 4000 своего результата. - person Mikosz; 26.11.2012
comment
нет, он этого не делает (или не должен), и если это так, для каждого отдельного LOB я бы рекомендовал вам отправить SR в Oracle. Я широко использую XMLType для файлов размером более 1 МБ, и он никогда не сбрасывал там chr (0) (10.2.0.4 + 11.2.0.2). - person DazzaL; 26.11.2012
comment
Спасибо. Я также использую 11.2.02 (Express Edition). Отредактировал свой пост простейшим тестом. Кажется, что проблема возникает с каждым результатом getClobVal (), когда он передается за пределы PL / SQL. - person Mikosz; 27.11.2012
comment
Я тестировал корпоративный клиент 11.2.0.2.0 + 11.2.0.2.0 корпоративную базу данных и не могу реплицировать, учитывая ваш тестовый пример, проблему 4000 chr (0). Используемая версия разработчика sql - 3.2.20.09. пробовали ли вы использовать другую машину или переустанавливать клиентское ПО, чтобы проверить, решает ли это проблема? Я тоже ничего не вижу на сайте поддержки oracle, но, поскольку ваш тест на стороне db не показал chr (0), я бы начал со стороны клиента (т.е. ваши драйверы java и установка клиента oracle) - person DazzaL; 27.11.2012

Я также столкнулся с той же проблемой, в точности описанной Микошем (я видел лишний символ «NUL» около 4000-го символа при выводе моего значения XMLType в виде Clob). Играя в SQLDeveloper, я заметил интересный обходной путь. Я пытался увидеть вывод своего XMLType, но устал от прокрутки до 4000-го символа, поэтому я начал заключать вывод Clob в substr (...). К моему большому удивлению, проблема исчезла. Я включил это в свое приложение Java и подтвердил, что проблема больше не существует, и мой Clob можно получить без дополнительного символа. Я знаю, что это не идеальный обходной путь, и я все еще не уверен, почему он работает (хотелось бы, чтобы кто-нибудь мог мне это объяснить), но вот сокращенный пример того, что у меня сейчас работает:

// Gets the xml contents
String sql = "select substr(x.xml_content.getClobVal(), 0) as xml_content from my_table x";
ps = con.prepareStatement(sql);
if(rs.next()) {
  Reader reader = new BufferedReader(rs.getCharacterStream("xml_content"));
  ...
}
person Mark    schedule 21.02.2013
comment
Также стоит упомянуть, что мы используем столбец XMLType в нескольких местах. В тех местах, где мы зарегистрировали схему (структурированный xml), я не вижу этой проблемы. Для столбца XMLType, не использующего зарегистрированную схему, мы видим эту проблему. - person Mark; 21.02.2013
comment
Большое спасибо за ваше предложение. Это тоже решило мою проблему. Это было единственное место, где я мог найти подходящее решение. - person Bart Blommaerts; 22.08.2013

Ошибка: 14781609 XDB: XMLType.getclobval () возвращает временный LOB, когда XML хранится в CLOB. исправление в патче 11.2.0.4

и другое решение, если читать как blob, тогда нет ошибки, например

T1.PROJECT.getBlobVal(nls_charset_id('UTF8'))
person Clear    schedule 18.02.2014

Достаточно легко проверить, является ли это вызовом .getClobVal() или нет - выполните INSTR тест в PL / SQL (не на Java) на полученном CLOB, чтобы увидеть, существует CHR(0) или нет.

Если это не так, я бы указал пальцем на установку вашего клиента Oracle.

person Ben    schedule 27.11.2012
comment
Спасибо. Вы правы, что в PL / SQL нет нуля. Появляется, когда результат переносится наружу. Это странно, потому что я использую два разных клиента (SQL Developer в Windows и Java в Red Hat). - person Mikosz; 27.11.2012
comment
Вы можете страдать от ошибки JDBC; поскольку оба ваших клиента основаны на Java. - person Ben; 27.11.2012