非 Unicode データベースでの XML

DB2 for Linux, UNIX, and Windows で XML データでのコード・ページ変換を処理する

この記事では、DB2® Viper 2 の非 Unicode データベースで XML のネイティブ・データ型を使用する方法を説明します。置換文字が使用されないようにする方法、そしてクエリー構造化のポインターと文字参照の使用方法についてのヒントを紹介するとともに、この製品に新しく付属しているユーティリティーのユーザー定義関数 (UDF) について説明します。

Randall P. Spalten (rspalten@us.ibm.com), Advisory Software Engineer, IBM

Photo: Randy SpaltenRandall Spalten は、カリフォルニア州サンノゼにある Silicon Valley Laboratory の顧問ソフトウェア・エンジニアです。1997年、IBM に入社して Information Integration に関わって以来、フェデレーション、DDL セマンティクス、二相コミット処理、ランタイムなど、DB2 のさまざまな分野に取り組んだ後、pureXML サポートに移行しました。彼は現在、pureXML Runtime チームで非 Unicode サポートとランタイム・パフォーマンスの強化に専念しています。



Preethi Vishwanath (pvishwa@us.ibm.com), Software Engineer, IBM

Photo: Preethi VishwanathPreethi Vishwanath は、DB2 XML Runtime グループのソフトウェア開発者です。2006 年、研修生として IBM に参加して以来、非 Unicode データベースに対する XML サポートの有効化に取り組んでいます。彼女の専門分野は、分散データベース設計、ランタイムと言語サポート、そして複製問題です。San Jose State University ではコンピューター・サイエンスの修士号を取得しました。



2007年 7月 19日

V9 での XML サポート

DB2 9 でデータベースに XML を組み込むには、そのデータベースに対応した UTF-8 コード・セットを使う以外に方法はありませんでした。この制約は、XML データに ASCII コード・ポイントしか含まれていないとしても変わりません。XML はバイナリー・フォーマットでデータベースに保存され、テキスト・ノードは UTF-8 コード・ポイントとして保存されます。DB2 9 では XML 文書に対する変換は行われないため、XML 文書は一連の UTF-8 バイトとしてデータベースに挿入せざるを得ませんでした。DB2 Viper 2 リリースでは、pureXML 機能で使用するためのすべてのデータベースを Unicode で作成しなければならないという制約が解除されています。この記事では、非 Unicode データベースでの XML のサポートについて説明し、さらに、データ保全性を失うことなく XML 文書を安全に挿入し、その文書に対してクエリーを実行する方法を説明します。

サンプル・シナリオ: Unicode コード・セットにマイグレーションせずに pureXML にマイグレーションする

データベース管理者 (DBA) として既存の非 Unicode の DB2 データベースを管理している場合を想定してください。このデータベースにはライブラリーの本を目録にしたテーブルが含まれていて、テーブルのフォームは現在、以下のようになっています。

リスト 1. リレーショナル・スキーマの例
	CREATE TABLE BOOKCAT
		(TITLE           VARCHAR(400) NOT NULL,
		AUTHOR_LAST      VARCHAR(50) NOT NULL,
		AUTHOR_FIRST     VARCHAR(50),
		PUBLISHER        VARCHAR(100),
		DATE_PUB         DATE NOT NULL,
		ISBN             BIGINT NOT NULL PRIMARY KEY,
		KEYWORD1         CHAR(20),
		KEYWORD2         CHAR(20),
		FIRST_PARA       CLOB(2K));

ただしこのデータは、非 Unicode のリレーショナル・テーブルでは制約があります。具体的には、現行データベースの文字セットにはない文字が本のタイトルや本文に含まれる場合、1 冊の本につき 3 つ以上のキーワードがある場合、キーワードのサイズが新しいエントリーに収まりきらない場合、そして本の出版日が不明な場合や著者が 2 人以上いる場合です。

このスキーマにはあまりにも制約があり過ぎるため、このデータを XML で保存する方法を調査することにしましたが、既存のデータベースを UTF-8 にマイグレーションすることは好ましくありません。この本のデータベースには、中国語、ロシア語などの他の言語で書かれた各国の新刊本も流入してくる場合があるからです。そのため、このデータベースは本に関するメタデータ (タイトル、著者、最初のパラグラフ) を英語 (※訳注: この記事は、英語を母国語とする前提で書かれていますので、日本語を母国語とする読者のみなさんは「英語」を「日本語」と置き換えてお読みください。) と原語の両方で保存できるようにしなければなりません。それぞれの本は、その言語に応じた方法でエンコードされた XML 文書としてモデル化できます。しかし、これらの文書を非 Unicode の pureXML データベースに挿入、保存し、これらの文書にクエリーを実行するにはどうすればいいのでしょうか。


非 Unicode データベースでの新たな XML サポート

DB2 Viper 2 では、新規あるいは既存のデータベースに含まれる新規または既存のテーブルに、カスタマーが XML 列を作成できるようになっています。Unicode コード・セットを使用しなければならないという条件はもうありません。XML 型の列を作成するための書式は以下のとおりです。

CREATE TABLE <table_name> (<column_name1> XML);

CREATE TABLE の構文は以前のままであることに注目してください。

例:

リスト 2. XML 型の列の作成
CREATE TABLE  xmlbookcat (
	ISBN BIGINT NOT NULL PRIMARY KEY,
	BOOK XML
);

XML 列を追加してテーブルを変更するには、もちろん列をテーブルに追加するのが正当な方法です (つまり、テーブルには DEFAULT 節のない NOT NULL 列を使うことはできず、テーブルの行には幅 80 バイトの新しい XML 列を収容できるだけの幅がなければならないということです)。DB2 では、ユーザーが CREATE TABLE または ALTER TABLE の列宣言に CCSID UNICODE 節あるいは CCSID ASCII 節を使用して、データベースの他の文字コード・セット ID (CCSID) とは異なる CCSID で保存するテーブルを作成することができます。しかし、非 Unicode データベースでは CCSID UNICODE 属性を持つテーブルに XML 列を作成あるいは追加することは許可されていません。この属性を持つテーブルはデータベース・コード・ページには保存されず、これらのテーブルで XML がサポートされることもありません。このような操作を試行すると、-873 (コード・ページ不一致) エラーとなるので、非 Unicode データベースに XML データを保持するための新しいテーブルを作成してください。

例:

CREATE TABLE untable(a VARCHAR(20)) CCSID UNICODE;
ALTER TABLE untable ADD xmlcol XML;

上記では、SQL0873N Objects encoded with different encoding schemes cannot be referenced in the same SQL statement (1 つの SQL 文の中で異なるエンコーディング方法でエンコードされたオブジェクトを参照することはできません。) というエラーが発生します。


XML データの保存

以下は、XML 文書の構造例です。

リスト 3. XML 文書の構造
	<book>
		<title> . . . </title>
		<native_title> . . . </native_title>
		<author>
			<firstname> . . . </firstname>
			<lastname> . . . </lastname>
			<native_name> . . . </native_name>
		</author>
		<publisher> . . . </publisher>
		<date> . . . </date>
		<keyword> . . . </keyword>
		<first_para> . . . </first_para>
		<native_first_para> . . . </native_first_para>
		<ISBN> . . . </ISBN>
	</book>

本によって使われている言語は異なる可能性があるため、異なる文字セットが含まれる XML 文書を同じ DB2 列に保存しなければなりません。以下の例では、要素の名前は ASCII にして読みやすくしていますが、要素内に含まれる値はあらゆる有効な Unicode コード・ポイントで構成することができます。このような XML 文書のサンプル・データを以下に記載します。

リスト 4. XML 文書のサンプル・データ
	<book>
		<title> A Tale of Two Cities </title>
		<author>
			<firstname> Charles </firstname>
			<lastname> Dickens </lastname>
		</author>
		<publisher> Signet Classics </publisher>
		<date> 7/1/1997 </date>
		<keyword> London </keyword>
		<keyword> Sydney Carton </keyword>
		<keyword> Paris </keyword>
		<first_para> It was the best of times, it was the worst of times </first_para>
		<ISBN> 0451526562 </ISBN>
	</book>

リスト 5. XML 文書のサンプル・データ

リスト 6. XML 文書のサンプル・データ
	<book>
		<title> War and Peace </title>
		<native_title> Война и мир </native_title>
		<author>
			<firstname> Leo </firstname>
			<lastname> Tolstoy </lastname>
		</author>
		<publisher> Modern Library </publisher>
		<date> 7/9/2002 </date>
		<keyword> Russia </keyword>
		<keyword> History </keyword>
		<keyword> Paris </keyword>
		<keyword> Napoleon </keyword>
		<keyword> Война </keyword>
		<first_para> Well, Prince, Genoa and Lucca are now no more than private estates 
                of the Bonaparte family </first_para>
		<native_first_para> Eh bien, mon prince. Genes et Lucques ne sont plus que 
               des apanages, des поместья, de la famille Buonaparte </native_first_para>
		<ISBN> 0375760644 </ISBN>
	</book>

上記のデータを非 Unicode データベースに安全に保存する方法を検討する前に、XML データが DB2 を介してどのようにクライアントからサーバーへ、そしてサーバーから pureXML 機能へと送られて構文解析とクエリーが行われるかを理解しておく必要があります。


XML の場合のデータ・フロー

DB2 の XML コンポーネントは Unicode コード・ページ内で動作します。そのため、XML の構文解析、シリアライズ、クエリーの実行、そして比較はすべて、Unicode コード・ページで行われます。データベースが UTF-8 であれば、データベース・コード・ページでデータを操作できます。一方、データベースが非 Unicode のコード・ページ (ISO-8859-1 など) である場合は、XML 文書、XQUERY ストリング、そしてフラグメントのすべてで、データベース・コード・ページから Unicode (具体的には UTF-8、コード・ページ 1208) へのコード・ページ変換が必要になります。

DB2 の SQL コンポーネントはデータベース・コード・ページ内で動作するため、SQL 比較、SQL クエリーの実行、SQL キャストはいずれもデータベース・コード・ページで行われます。クエリーが XML アクティビティーと SQL アクティビティーの両方で構成されている場合は、「SQL 世界」と「XML 世界」それぞれのデータを正しいコード・ページで操作しなければならないため、この 2 つの「世界」の間での変換が行われます。

データをデータベース・コード・ページから UTF-8 に変換するアクションは「XML 世界」への変換と呼び、データを UTF-8 からデータベース・コード・ページに変換するアクションは「SQL 世界」への変換と呼ぶことにします。サーバー上での SQL/XML または XQUERY 実行中に行われるコード・ページ変換は、この 2 種類しかありません。

非 Unicode データベースに対する XML サポートには、Unicode のクライアントを使用することをお勧めします。クライアントは入力 XML 文書と結果 XML 文書のすべての文字を表現できるはずだからです。クライアント・コード・ページとデータベース・コード・ページが同じであれば、クライアントとサーバー間での変換は行われないので、クライアントが入力 XML 文書と結果 XML 文書のすべての文字を表現することができます。

クライアントはそのコード・ページを、DB2CODEPAGE 環境変数を使って一時的に無効にすることができます。この変数を 1208 (UTF-8) に設定すると、クライアントがコード・ページ 1028 で動作しているかのように、DB2 とクライアントとの通信が行われます。しかし、このようにしてクライアント・コード・ページを無効にすると、誤った出力になったり、あるいは印刷不可能な文字になることがあります (943 データベースから、DB2CODEPAGE が無効にされた ASCII クライアントに漢字データが送信されるなど)。Windows の場合、クライアント環境のコード・ページを無効にするには、Windows の「コントロール・パネル」の「地域と言語のオプション」で目的の変更を行うという方法を採れます。


変換の対象

図 1. 変換プロセスの例
変換プロセスの例

DB2 クライアントから DB2 サーバーに要求が送信されるときには常に、すべての文字データ (クエリーや CHAR ホスト変数など) がクライアント・コード・ページからデータベース・コード・ページに変換されます。この変換は、要求に XML が関係するかどうかには関わらず行われます。

クエリーの XML 部分は、XML パーサーが構文解析して XML 値を出力 (シリアライズ) するために XML 世界に変換され、その結果は SQL 世界に変換されます。この両方の変換で、置換文字が取り込まれる可能性があります。置換文字とは、ターゲット・コード・ページがコード・ポイントをレンダリングできない場合に文字ストリームに挿入される置換コード・ポイントのことです。置換文字によってサーバーでのデータの完全性は失われてしまうため、この記事では置換文字を避ける方法に目を向けます。

SQL クエリーとシリアライズされた XML の結果は、SQL 世界に変換されてからクライアントに返されます。この時点でも、シリアライズされた XML にデータベース・コード・ページで表現できない文字やクライアント・コード・ページで表現できない文字があると、置換文字が取り込まれることになります。ここからは、この両方の置換文字の落とし穴にはまらないようにする方法を説明していきます。


XML 文書の挿入

データが正しいコード・ページに変換される仕組みがわかったところで、今度はこの変換が XML 文書の挿入にどのように影響するかについて考えてみましょう。XML 文書をテーブルに挿入するには、以下のような XML データが含まれる文字ストリングを使用できます。

リスト 7. XML データが含まれる文字ストリング
INSERT INTO xmlbookcat VALUES 
(0451526562, XMLPARSE (DOCUMENT '<?xml version="1.0" encoding="ISO-8859-1" ?>
	<book>
		<title> A Tale of Two Cities </title>
		<author>
			<firstname> Charles </firstname>
			<lastname> Dickens </lastname>
		</author>
		<publisher> Signet Classics </publisher>
		<date> 7/1/1997 </date>
		<keyword> London </keyword>
		<keyword> Sydney Carton </keyword>
		<keyword> Paris </keyword>
		<first_para> It was the best of times, it was the worst of times </first_para>
		<ISBN> 0451526562 </ISBN>
	</book>'));

INSERT 文全体がクライアント・コード・ページからデータベース・コード・ページに変換された後、XMLPARSE 文書ストリングが UTF-8 に変換されて XML の構文解析が行われます。これらの変換では置換文字が発生することがなく、逆の変換も可能なので、このシナリオでは安全に XML データを挿入できるというわけです。データベース・コード・ページでエンコードされ、データベース・コード・ページで表現可能な文字だけが含まれる XML 文書は常に安全に挿入できます。このことは、データベースが非 Unicode であっても当てはまります。一方、同じ環境で中国語の XML 文書を挿入としたらどうなるでしょう。

リスト 8. 中国語の XML 文書の挿入

上記での問題は、データベース・サーバーがこの SQL 文を受信してデータベース・コード・ページに変換すると、いずれの中国語文字も (ISO-8859-1 の ASCII 文字でのみ構成されている) データベース・コード・ページで表現できないという点です。この時点で、DB2 はこれらの表現できない文字を置き換えるために置換文字を使用します。この置換文字のコード・ポイントはターゲット・コード・ページによって異なりますが、コード・ページ 819 (ISO-8859-1) の場合は 16 進文字 0x1A となります。この文字は、クライアントでは通常、疑問符 (「?」) として表示されます。

置換文字の存在は、すなわちデータの完全性が失われているということを意味します。元の文書の中国語文字は失われているため、データ・ストリームをクライアントに返すだけではこれらの文字を取得できません。置換文字は、別のコード・ページでは異なる置換文字に変換されるのが常だからです。

上記の XML 文書では、XML パーサーが本の原語タイトルを「????」として受信します (それぞれの ? 文字は 0x1A コード・ポイントです)。これは正規 XML 文字ストリームなのでパーサーはエラーをスローしませんが、XML 列内のデータは壊れています。この文書を選択すると、置換文字がクライアントに返されることになります。

注意する点として、データベースが Shift-JIS エンコード方式のコード・ポイントをサポートするコード・ページ 943 で作成されていれば、上記の INSERT 文は安全で、文書はデータベースに挿入されます。ロシア語の例の場合、XML 文書を安全に挿入できるのは、ISO-8859-5 (キリル文字) エンコード方式をサポートするデータベースのみです。

データベース・コード・ページでは表現できないコード・ポイントを大量に含む XML 文書をユーザーが頻繁に使用する場合に、もっとも完璧なソリューションとなるのは、Unicode データベースを使うことです。この場合、サーバーでの構文解析またはシリアライズの際に置換文字が取り込まれることはありません。非 Unicode のクライアントにデータを返したときに置換文字が入り込む可能性はありますが、それでもデータベース内の XML 文書には影響しません。


ENABLE_XMLCHAR パラメーターの使用

Viper 2 リリースには、CHAR 型ホスト変数や文字ストリングから XML を構文解析しないようにするための新たな DBA 構成パラメーターが導入されています。ENABLE_XMLCHAR という名前のこのパラメーターは、データベースを対象にしています。デフォルトでは、ENABLE_XMLCHAR が TRUE に設定されてすべてのデータベースが作成されますが、このパラメーターを FALSE に設定することもできます。その場合、ユーザーが BIT DATA 型ではない CHAR、VARCHAR、または CLOB 型のホスト変数で暗黙的または明示的な XMLPARSE 操作を行うクエリーを実行するとエラーがスローされるようになります。このシナリオで使用できるのは、CHAR FOR BIT DATA、VARCHAR FOR BIT DATA、および BLOB です。このデータベース・パラメーターの値を変更するには、以下のコマンドを実行します。

db2 update db cfg for database name using ENABLE_XMLCHAR FALSE

値の変更を有効にするには、DBA がインスタンスを再利用しなければなりません。例えば、ユーザーが以下の操作を実行するとします。

INSERT INTO XTAB VALUES (XMLPARSE(DOCUMENT 'I just 
    joined the ΔΨΠ fraternity!' PRESERVE WHITESPACE));

ISO-8859-1 コード・セットのデータベースで XTAB に挿入される XML 文書には、ギリシャ文字の代わりとして 3 つの置換文字が含まれる結果となります。この場合、ENABLE_XMLCHAR が FALSEに設定されていれば、INSERT 文はエラーになります。

SQL20429N  The XML operation "XMLPARSE" is not allowed on strings 
      that are not FOR BIT DATA on this database.

上記のエラーは、ユーザーに対し、CHAR 型には XMLPARSE を使用できないことを伝えるものです。この文書を適切にデータベースに挿入するためには、ユーザーが外部 API を使用して「I just joined the ΔΨΠ fraternity!」というストリングを BLOB、CHAR FOR BIT DATA、または VARCHAR FOR BIT DATA ホスト変数にバインドしなければなりません。すると、クライアント・コード・ページとまったく同じコード・ポイントを使用して、そのままのテキストが XML 列に挿入されることになります。


安全でない XML 文書の挿入

データベース・コード・ページで表現できないコード・ポイントが含まれる文書を安全に挿入する方法は唯一、BLOB または BIT DATA ホスト変数を使うことです。これは CLP からは行えません。

DB2 は BLOB データでのコード・ページ変換を行わないため、XML を BLOB ホスト変数にバインドすれば置換文字を回避できるようになります。BLOB に含まれるデータのコード・ページを判断するには、いくつか特定のルールがあります。

BLOB XML ストリームにバイト・オーダー・マーク (BOM) が含まれる場合、これを使用して BLOB に含まれるデータの想定コード・ページが判断されます。これは、ホスト変数で「外部エンコーディング」が行われないため、「内部エンコーディング」と呼ばれています (つまりホスト変数に関連するコード・ページは存在しないということです)。

BLOB XML ストリームに BOM が含まれていない場合は、ストリームの解析によって、XML データのヘッダー内にある「<?xml version="1.0" encoding = "...encoding string">」というフォームの XML のエンコーディング属性が検索されます。このエンコーディング属性が存在する場合は CCSID にマッピングされるので、その CCSID が内部エンコード方式として使用されます。有効なエンコーディング属性のリストについては、「参考文献」セクションを参照してください。

BOM もなく、エンコーディング属性も指定されていない BLOB データは、UTF-8 コード・ポイントで構成されているとみなされます。XML 構文解析の前に、BLOB データには有効な UTF-8 コード・ポイントのみが含まれているかどうかのチェックは行われないことに注意してください。単独のコード・ポイント 0xDB が含まれる BLOB が送信されて XML として構文解析されると、この BLOB は正規 UTF-8 コード・ポイントでないためエラーが発生します。XML に含められる UTF-8 コード・ポイントの有効範囲にはさらに制約があります。詳細については、「参考文献」セクションを参照してください。


XML 文書を「クリーンアップ」するための UDF

最終手段は、問題の文字を 16 進の文字参照 (「&#xhhhh;」の形式。ここで、hhhh はそれぞれの文字の 16 進 Unicode UTF 16 コード・ポイント) に変換することです。10 進文字参照はあらゆる XML フラグメントで使用可能で、XML 構文解析の際に実際のコード・ポイントに置き換えられます。例えば文字ストリング「I just joined the &#x394;&#x3A8;&#x380; fraternity!」は、UTF-8 では「I just joined the ΔΨΠ fraternity!」に相当します。

ユーザーが XML 文書を変換する際の支援として、DB2 には XML 文書をデータベースに挿入する前にテストし、クリーンアップする 2 つの UDF が用意されています。

一方の UDF は TEST_XML という名前の関数で、XML 文書が含まれる BLOBを取り込み (優先されるメソッドは、UTF-8 コード・セットでエンコードされる XML テキスト・ファイルを参照する BLOB_FILE を使用することです)、ブール値を出力します。TEST_XML が呼び出されると、DB2 は UTF-8 からデータベース・コード・ページへの BLOB の変換を試行し、変換中に置換文字が検出されなかった場合は TRUE を返し、置換文字が検出された場合は FALSE を返します。これによって文書が挿入されたり、BLOB が変更されることはありません。これは単なるテストとして、この XML 文書を CHAR、VARCHAR、または CLOB として、データの完全性を失うことなく安全にデータベースに挿入できるかを確かめるだけのものです。

BLOB が UTF-8 以外のコード・ページでエンコードされている場合、ユーザーは BLOB 入力のコード・ページをオプションの 2 番目のパラメーターとして TEST_XML に指定できます。

もう一方の UDF は、置換エスケープ文字で置き換えて変換を行います。これは CLEAN_XML と呼ばれる UDF で、(UTF-8 コード・ページになる前提の) XML 文書が含まれる BLOB または BLOB ファイルを取り込み、XML 文書が含まれる CLOB をデータベース・コード・ページで出力します。この際、データベース・コード・ページに安全に変換できない各コード・ポイントは、エスケープ文字形式「&#xhhhh」(hhhh は文字の 16 進 UTF 16 コード・ポイント) に置き換えられます。この操作でも文書が挿入されたり BLOB が変更されたりすることはありませんが、INSERT/XMLPARSE と併せて使用すると、特定の BLOB XML 文書を安全にデータベースに挿入することができます。以下はその一例です。

INSERT INTO XTAB VALUES (XMLPARSE(CLEAN_XML(:BLOB_HV)));

BLOB が UTF-8 以外のコード・ページでエンコードされている場合、ユーザーは BLOB 入力のコード・ページをオプションの 2 番目のパラメーターとして CLEAN_XML に指定できます。

例:

以下の XML 文書は、コード・ページISO-8859-7 のデータベースに対する入力 BLOB として指定されているサンプルだとします (以下の Stérl&iacute;ng で使用されている記号「é」と「&iacute;」は ISO-8859-7 に属していないことに注意してください)。

Sample input:

<?xml version="1.0" encoding="utf-8" ?>
      <a> Sterling
      </a>

上記の入力で TEST_XML を実行すると、出力値は 1 となります。これは、上記の Stérl&iacute;ng で使用されている記号「é」と「&iacute;」は ISO-8859-7 に属していないため、置き換える必要があるためです。

CLEAN_XML を実行した場合も同様で、Stérl&iacute;ng は Stérlíng に変換されることになります。つまり、é は対応する 16 進値 &#xE9 に、&iacute; は対応する 16 進値 &#xED に置き換えられるというわけです。

Sample output:

<?xml version="1.0" encoding = "utf-8" ?>
        <a>
 St&#xE9;rl&#xED;ng 

        </a>

これでデータベース・コード・ページとは関係なく、この XML 文書を XML 列を持つテーブルに挿入できるようになります。ただし、この 2 つの UDF に関する警告として、これらの UDF は BLOB を XML 文書として構文解析しないということに注意してください。整形式のチェックが行われないため、XML 文書の要素タグ名や他のテキスト以外の部分が 16 進文字参照に変更される可能性があります。XML 文書に、要素タグ名や XML ヘッダー情報の内部ではデータベース・コード・ページでレンダリングできないコード・ポイントが含まれている場合、CLEAN_XML を実行した文書は整形式 XML 文書でなくなってしまうおそれがあります。


非 Unicode データベースでの XML データへのクエリー

XML のシリアライズとは、最終的にクライアントに送り返すために Unicode XML 値をホスト変数にバインドするプロセスのことです。FOR BIT DATA ではない CHAR、VARCHAR、または CLOB 型に値がシリアライズされる場合には、SQL 世界へのコード・ページ変換が行われます。BLOB のシリアライズではコード・ページ変換は行われませんが、シリアライズされたヘッダー内に「エンコーディング」属性が割り当てられます。この属性は、デフォルトで UTF-8 のエンコード方式に設定されます。

シリアライズの際に置換文字が取り込まれる可能性があるのは、コード・ポイントがデータベース・コード・ページでレンダリングできない場合です。置換文字が取り込まれると、ユーザーにはエラー -20412 が表示されます。このエラーは、XML 値のシリアライズでは置換文字を注入してはならないという XML ホワイト・ペーパーの規則に対応します。

このシナリオでは SELECT がどのように機能するかを見てみましょう。

Syntax :

	SELECT XMLSERIALIZE(<XML column name> AS <datatype>) FROM <table_name>;

単純な例として、INSERT: SELECT XMLSERIALIZE(book AS CLOB(1M)) FROM xmlbookcat; または XQUERY db2-fn:xmlcolumn('XMLBOOKCAT.BOOK')/book; の結果をフェッチします。

XQUERY はデフォルトで、その結果セットを CLOB にシリアライズすることに注意してください。テーブルから XML 列を選択する場合も、その結果は CLOB にシリアライズされます。

CLOB データ型にはデータベース・コード・ページに相当する固有のコード・ページがあるため、シリアライズの際には暗黙的に、応答が SQL 世界へコード・ページ変換されます。その結果がデータベースから Unicode クライアントに送り返され、応答は適宜変換されます。ASCII データの場合は、データを失うことなくクライアント・コード・ページで同じデータを得られることになります。

一方、中国語とロシア語の文字の場合には、XML シリアライザーが出力用にこれらの文字を置換文字に置き換えます。クライアントはシリアライズされている XML データに置換文字が含まれていることを検出すると、エラー -20412 をスローします。中国語またはロシア語の文書が BLOB として挿入されているとしても、このエラーを発生させずに文書を CLOB 型あるいは CHAR 型としてシリアライズすることはできません。

このデータを選択するのに唯一安全な方法は、データを BLOB ホスト変数、または BLOB FILE ホスト変数にバインドして XML ファイルに書き出すことです。データを XML 出力ホスト変数に直接バインドし、シリアライズせずにクライアントにデータを返すことが可能であれば、それがこのような置換文字を避ける最善の方法となります。


文字参照の使用

クライアントは文字参照を XML ストリームに手動で埋め込んで、XML データを BLOB にバインドせずに固有のコード・ポイントをデータベースに渡すことができます。以下は、ギリシャ語のデルタ文字 (「Δ」。Unicode コード・ポイントは U+0394、UTF-8 コード・ポイントは 0xCE94) を使った例です。

	INSERT INTO XMLBOOKCAT VALUES (1, xmlparse(document 
    '<?xml version="1.0"?><book>H&#x0394;llo</book>'));

上記の文は、任意のデータベースでデルタ文字を XML 文書に挿入します。

	SELECT XMLSERIALIZE(XMLCOL AS BLOB(250)) FROM xmlbookcat;

結果は以下の出力になります。

	x'3C626F6F6B3E48CE946C6C6F3C2F626F6F6B3E'

上記は「<book>HΔllo</book>」に対応する UTF-8 コード・ポイントです。デルタ文字が 16 進文字参照 (16 進数 394) によって渡され、ISO8859-1 データベースに正しく保存されていることに注目してください。デルタ文字をシリアライズすることで、その文字が UTF-8 で表現 (16 進数 CE94) されます。


置換文字

コード・セット 8859-1 への漢字の挿入

INSERT INTO xmlbookcat VALUES( 11, 
    XMLPARSE(document'<?xml version="1.0" encoding="utf-8" ?> 
       <book><pr番duct> </pr番duct></book>'));

product に含まれる漢字「番」は、データベース・コード・ページ 8859-1 の一部ではありません。解析は、この文字が置換文字に置き換えられてから行われるため、要素タグ名が原因の SQL エラー・コード「16110 XML Syntax error. Expected to find Attribute name (16110 XML 構文エラー。属性名がありません)」がスローされます。

コード・セット 8859-1 での漢字の XQUERY

XQUERY (番);

上記の文の結果は、エラー -16002N、「An XQuery expression has an unexpected token 0x1A following XQUERY (XQuery 式のXQUERY の後に予期しないトークン 0x1A があります)」となります。

漢字 XML 要素の選択

SELECT XMLELEMENT(NAME "pr番duct") FROM xmlbookcat;

上記の文は漢字「番」が空白スペースに置き換えられるため、エラー・コード「SQL 20275N The XML name "pr duct" is not valid (SQL 20275N。XML 名「pr duct」が無効です)」という結果になります。

コード・ページが混在した XQUERY の例

	XQUERY for $x in db2-fn:xmlcolumn('XMLBOOKCAT.BOOK')/book[keyword = 'Paris']	
		return $x/author;

上記の例は、book 文書をスキャンして、「Paris」というキーワードを持つ本の著者を返します。これによって「Leo Tolstoy」および「Charles Dickens」の要素が戻されることになります。ロシア語の本にはキリル文字の Unicode 文字が含まれていますが、この XQUERY は ISO-8859-1 データベースでも有効です。その理由は、結果述部と一致する要素だけがシリアライズされるためです。

	XQUERY for $x in db2-fn:xmlcolumn('XMLBOOKCAT.BOOK')/book[native_title = '三國演義']
		return $x/date;

上記の例は、データベースがタイトル述部で使用されているコード・セット Shift_JIS のコード・ポイントをサポートする場合は正当ですが、その他のデータベースでは正しく実行されません。データが結果セットとして返されるだけの場合でも、タイトル述部が安全にトランスコードされるようにクエリー自体がデータベース・コード・ページに変換されるためです。これらのコード・ポイントをサポートしないデータベースの場合、以下のようにコード・ポイントを記号ごとに対応する文字参照に置き換えることで、このクエリーを XML 文書に対して実行することが可能になります。

	XQUERY for $x in db2-fn:xmlcolumn('XMLBOOKCAT.BOOK')/book
          [native_title='&#x4E09;&#x570B;&#x6F14;&#x7FA9;']
		return $x/date;

このコードはスマートでないかもしれませんが、サーバー上の述部に正しく一致し、期待される値を返します。DB2 に渡すあらゆる文字列で、このような文字参照を使ってデータベース・コード・ページでは表現できないコード・ポイントを置き換えることができます。

	XQUERY for $x in db2-fn:xmlcolumn('XMLBOOKCAT.BOOK')/book
		return $x/native_first_para;

上記のクエリーを UTF-8 コード・セット以外のデータベースに対して実行すると、クライアント側で置換文字エラー -20412 が発生します。このクエリーは、ロシア語、中国語、英語のコード・ポイントを同じ結果セットで返すからです。このすべての値が含まれる非 Unicode のコード・ページは存在しません。一方、クライアントが結果セットを BLOB にバインドすれば、バイトをトランスコードすることなく安全にクライアントに返すことができます。

	XQUERY for $x in db2-fn:xmlcolumn('XMLBOOKCAT.BOOK')/book
		return $x/first_para;

上記のクエリーは、本の「原語」での最初のパラグラフに ASCII 以外の文字が含まれているとしても、各本の最初のパラグラフを英語で返します。このクエリーはどのデータベースでも安全に実行することができます。

ここで、コード・ページ変換プロセスから派生する問題をもう少し検討してみましょう。SQL パーサーと XML パーサーは DB2 コンパイラー内で連携しますが、それぞれが期待する入力は別のコード・ページにあります。では、同じリクエストに XQUERY と SQL が混在する場合はどうなるでしょう。

	XQUERY db2-fn:sqlquery("SELECT BOOK FROM XMLBOOKCAT")//book[date < '01/01/1999'];

上記は SQL リクエストが組み込まれた XQUERY です。まず、XQUERY全体(SQL リクエストも含む) が UTF-8 に変換され、クエリーの SQL 部分 ("SELECT BOOK FROM XMLBOOKCAT") が SQL 世界に変換されます。その結果、このクエリーは部分的にコード・ページが混在する混成クエリーとなります。

リスト 9 では、中国語の文字 ("羅") を ISO-8859-1 データベースで比較しようとしています。

リスト 9. 中国語文字「羅」の比較
SELECT XMLCAST(
XMLQUERY('$doc/book/author[native_name = "羅"]' 
PASSING xmlbookcat.book AS "doc") AS CLOB)
 	FROM xmlproductnuc;

結果は、リスト 10 に示すように 0x1A の置換による SQL コード 16002N のエラーになります。

リスト 10. SQL コード16002N
SQL16002N  An XQuery expression has an unexpected token "0x1A" following " = ". 
Expected tokens may include: "#x9|#xA|#xD|[#x20-#xD7FF]|[#xE000-#xFFFD]|
[#x10000-#x10FFFF]". Error QName=err:XPST0003.  SQLSTATE=10505

この値を XML データに対して正確に比較するには、文字参照 (&#x7F85 など) を使用しなければなりません。

XMLTABLE のような複雑な例の場合は、以下のようになります。

リスト 11. XMLTABLE の比較
SELECT x.* from XMLBOOKCAT xmlb, 
      XMLTABLE('$p/book' PASSING xmlb.book AS "p" COLUMNS 
"Title" 		VARCHAR(50)    PATH '//title',
"Publisher"		VARCHAR(32)    PATH '//publisher',
"Author_First"	VARCHAR(32)    PATH '//author/firstname',
"Author_Last"	        VARCHAR(32)    PATH '//author/lastname') AS X;

XML 文書は細分化され、それぞれにバインドされた列 (title、publisher、firstname、lastname) がデータベース・コード・ページに変換されますが、このすべての要素に ASCII 文字が含まれているので安全です。一方、以下のXMLTABLE の呼び出しは安全ではありません。

リスト 12. 安全でない XMLTABLE の呼び出し
SELECT x.* from XMLBOOKCAT xmlb, 
      XMLTABLE('$p/book' PASSING xmlb.book AS "p" COLUMNS 
"Title" 		VARCHAR(50)    PATH '//native_title',
"Publisher"		VARCHAR(32)    PATH '//publisher',
"Author"	      VARCHAR(32)    PATH '//author/native_name') AS X;

上記では、「native_name」要素と「native_title」要素に含まれる Unicode 文字をデータベース・コード・ページに変換してリレーショナル・テーブル内の VARCHAR 列に保存しようとしているため、非 Unicode データベースで置換文字が発生します。非 Unicode データベース内では、コード・ページが混在した XML 文書は推奨されません。


まとめ

XML を非 Unicode データベースに追加する必要があり、その XML 文書にデータベース・コード・ページで表現できない文字がたくさん含まれている場合は、データベースを Unicode にマイグレーションしてから、該当するフォームで XML データを挿入することをお勧めします。

一方、XML 文書にデータベース・コード・ページで表現可能な文字しか含まれていない場合には、ユーザーは何の制約も置換文字も強いられずに XML 文書をデータベースに挿入してクエリーを実行することができます。ただし、データベース・コード・ページにはない文字が含まれる新しい XML データを挿入しないよう注意してください。

データベース・コード・ページにない文字が少ししか含まれない文書であれば、UDF TEST_XMLを使ってチェックすることができます。このテストでは、XML 文書を安全にデータベースに挿入できる場合は TRUE が返されます。FLASE が返される場合には、非 Unicode データベースに挿入する前に文書を CLEAN_XML UDF に渡してクリーンアップする必要があるということです。

DBA では、ENABLE_XMLCHAR データベース構成パラメーターを使用して、文字タイプに応じてユーザーによる XML 文書の挿入を制限できます。

データベースから XML をフェッチするのにもっとも安全な方法は、XML ネイティブ型のホスト変数を使うことです。BLOB も同じく、コード・ページ変換を行わずに XML データをフェッチする安全な方法となります。

データベース・コード・ページにない文字が含まれる XML データに対してクエリーを実行する際には、10 進または 16 進文字参照を使用するようにしてください。

参考文献

学ぶために

製品や技術を入手するために

  • Viper 2 コードをダウンロードして、オープン・ベータに参加してください。
  • IBM 製品の評価版をダウンロードして、DB2、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を使ってみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。プロフィールで選択した情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, XML
ArticleID=273136
ArticleTitle=非 Unicode データベースでの XML
publish-date=07192007