web-dev-qa-db-ja.com

奇妙なOracleXMLType.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';

できます。私は1つのセルを取得します。 XMLファイル全体をダブルクリックしてダウンロードできます。

次に、結果をCLOBとして取得しようとしました。

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

できます。私は1つのセルを取得します。ダブルクリックして全文を表示し、コピーすることができます。しかし問題がある。クリップボードにコピーすると、最初の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) );

リーダーは完全なXMLを返しますが、位置4000にヌル文字があるため例外がスローされます。この1バイトを削除することはできますが、これはかなり奇妙な回避策になります。

私はそこで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 );

XMLを使用して2つのCLOBを準備します。最初はヌル文字あり、2番目はなし。

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にあり、2番目の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 ) );

2番目のCLOBをXMLTYPEに挿入してみてください。それが動作します:

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

挿入されたXMLを3番目のCLOBに読み取ってみてください。それが動作します:

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

Nullがあるかどうかを確認します。 nullはありません:

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

データベース内にnullがなく、PL/SQLコンテキストにある限り、nullはありません。しかし、SQL Developer(Windowsの場合)またはJava(Red Hat EEおよびTomcat7の場合)で次のSQLを使用しようとすると、返されたすべてのCLOBの位置4000でヌル文字が返されます。

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

BR、JM

8
Mikosz

oracleのバグではありません(\ 0を正常に格納および取得します。クライアント/ウィンドウのバグ(「NUL」に関しては、ウィンドウとは異なるクライアントの動作が異なります)

chr(0)は、実際には非BLOBでは有効な文字ではありません(通常は解析されないように、XMLTypeが最初にそれを受け入れるようにする方法に興味があります)。

\ 0は文字列の終わりを示すためにCで使用され(NULターミネータ)、一部のGUIはその時点で文字列の処理を停止します。例えば:

_![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 Developer

ただし、コピーすると、クリップボードはヌル文字までしかコピーしません。このコピー&ペーストエラーはSQL開発者のせいではありませんが、WindowsクリップボードでNULが正しく貼り付けられないという問題があります。

sQL Developer/Windowsクリップボードを使用する場合は、これを回避するためにreplace(T1.PROJECT.getClobVal(), chr(0), null)を実行する必要があります。

6
DazzaL

また、Mikoszが説明したのとまったく同じ問題が発生していました(XMLType値をC​​lobとして出力すると、4000番目の文字の周りに余分な「NUL」文字が表示されます)。 SQLDeveloperで遊んでいると、興味深い回避策に気づきました。 XMLTypeの出力を確認しようとしましたが、4000番目の文字までスクロールするのにうんざりしていたため、Clob出力をsubstr(...)でラップし始めました。驚いたことに、この問題は実際には消えました。これをJavaアプリに組み込み、問題が発生しなくなり、余分な文字なしでClobを取得できることを確認しました。これは理想的な回避策ではないことを知っています。 mなぜそれが機能するのかまだわかりませんが(誰かが私にそれを説明できればいいのですが)、これが私が現在機能しているものの簡略化された例です:

// 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"));
  ...
}
3
Mark

バグ:14781609 XDB:XMLがCLOBに格納されている場合、XMLType.getclobval()は一時LOBを返します。パッチセット11.2.0.4で修正

ブロブとして読み取られた場合の別の解決策は、次のようなエラーはありません

T1.PROJECT.getBlobVal(nls_charset_id('UTF8'))
3
Clear

.getClobVal()呼び出しであるかどうかを確認するのは簡単です-結果のCLOBでPL/SQL(Javaではない)でINSTRテストを実行して、CHR(0)かどうかを確認します。存在するかどうか。

そうでない場合は、Oracleクライアントのインストールに指を向けます。

1
Ben