9

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) );

Reader は完全な 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 を準備します。1 つ目はヌル文字あり、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 に null 文字が表示されます。

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

BR、JM

4

4 に答える 4

7

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

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>

まだヒキガエルはこれで惨めに失敗します: ヒキガエル

ご覧のとおり、SQL開発者の方がうまくいきます。

SQL Developer

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

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

于 2012-11-23T23:52:19.600 に答える
3

また、Mikosz で説明されているのとまったく同じ問題が発生していました (XMLType 値を Clob として出力するときに、4000 番目の文字の周りに余分な「NUL」文字が表示されます)。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"));
  ...
}
于 2013-02-21T17:31:07.307 に答える
2

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

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

于 2012-11-27T00:59:18.177 に答える