XML型のCONTEXTモードとDOCUMENTモード

XML型のロード検証をやっていてちょっと躓いたのでメモ。

XML型のカラムを作ってXML文書をINSERTしてみる。

  • <A>a</A> →当然OK.INSERTされる。
  • <A>a</B> →Wel-formedでないのでエラー
  • aaaa →単なる文字列は何故かINSERTされてしまう。

xmldb=# INSERT INTO orders VALUES ('<A>a</A>');
INSERT 0 1
xmldb=# INSERT INTO orders VALUES ('<A>a</B>');
ERROR: invalid XML content
LINE 1: INSERT INTO orders VALUES ('<A>a</B>');
^
DETAIL: Entity: line 1: parser error : Opening and ending tag mismatch: A line 1 and B
<A>a</B>
^
Entity: line 1: parser error : chunk is not well balanced
<A>a</B>
^
xmldb=# INSERT INTO orders VALUES ('aaaa');
INSERT 0 1
xmldb=#

え?なんで単なる文字列がエラーにならないんだろう・・・。ちなみにこの状態でxpath関数を実行すると

xmldb=# select * from orders;
data

                  • -

<A>a</A>
aaaa
(2 rows)

xmldb=# select xpath('/', data) FROM orders;
ERROR: could not parse XML document
DETAIL: Entity: line 1: parser error : Start tag expected, '<' not found
aaaa
^
xmldb=#

ぐぬぬ・・・。xpath関数内のパースではエラーになってしまう。
どうやらPostgreSQLのマニュアルを見るとXML型にはCONTENTとDOCUMENTの二種類の指定が可能で、デフォルトはCONTENTらしい。で、この場合にはXML型として、単一のテキストノードも許容するようだ。
でも、xpath関数の第2引数はDOCUMENTであることを前提にしているので、単一のテキストノードなんかを渡されるとエラーになると。

で、PostgreSQLの実行時設定の中に xmloption てのがあるっぽい。これにはCONTENTかDOCUMENTが指定可能。xmloptionにDOCUMENTを指定すると aaaa のようなリテラルはINSERT時にもエラーにしてくれるようだ。

xmldb=# SET xmloption TO DOCUMENT;
SET
xmldb=# INSERT INTO orders VALUES ('aaaa');
ERROR: invalid XML document
LINE 1: INSERT INTO orders VALUES ('aaaa');
^
DETAIL: Entity: line 1: parser error : Start tag expected, '<' not found
aaaa
^
xmldb=#

CONTENT指定があるのは構わないけど、なんでデフォルトはDOCUMENTじゃないんだろう(´・ω・`)