Postgres・MDB2のキャスト演算子とプレースホルダ

月花です。

PDOによるデータベース操作の話をします。

今回はこんな話です

ことの発端

MDB2を使ったフレームワークで、Postgresから疑問符プレースホルダを使ったINSERTを行った際に、不思議なエラーが発生した。

INSERT INTO test (id,name,value) VALUES (1,NULL::TEXT,?);

こういうクエリをprepareして、

array('test')

を食わせてexecuteするような、一見問題のないINSERT文を実行した。

すると、

〜〜〜
MDB2 Error: unknown error
〜〜〜
[Native message: ERROR:  入力の最後で 構文エラー
LINE 1: INSERT INTO test (id,name,value) VALUES (1,NULL$1

とのこと。

不思議なエラー文だ。

糸口を探る

[Native message: ERROR:  入力の最後で 構文エラー
LINE 1: INSERT INTO test (id,name,value) VALUES (1,NULL$1

ということで、$1がどうにも気になる。
これはたぶん、プレースホルダだ。

原文では、NULLがベタ書きされている箇所は一個しか無いので、そうすると

NULL::TEXT

が気になってきた。

もしかして

INSERT INTO test (id,name,value) VALUES (1,CAST( NULL AS TEXT ),?);

コロンでのキャスト演算子ではなく、こうなら・・・・

いけた。

ということは、

  • コロン2つによるキャストが影響している
  • どうも不当にプレースホルダとして解釈されている
  • コロンを使わなければ治る

となれば原因は

結局は、

INSERT INTO test (id,name,value) VALUES (1,NULL::TEXT,?);

この、 NULL::TEXT の、 :TEXT が名前付きプレースホルダとして解釈され、 NULL$1 となり、さらに元々の疑問符プレースホルダとの競合もあり、構文エラーとなったようである。

ところが、これそんなはずはなく、正しい文法のはずだ。
ためしにいくつかのSQLクライアントから、疑問符プレースホルダを適当な値に変換したものを実行したが、どれも成功した。

ということは、フレームワーク・・・と思ったがこいつはMDB2との橋渡しをしているだけだ。

というわけでMDB2を探っていく。

環境の再現

適当な VirtualBoxMDB2をインストールして、下記のような環境にした。

# pear list
Installed packages, channel pear.php.net:
=========================================
Package           Version State
Archive_Tar       1.4.2   stable
Console_Getopt    1.3.1   stable
MDB2              2.4.1   stable
MDB2_Driver_pgsql 1.4.1   stable
PEAR              1.9.5   stable
Structures_Graph  1.0.4   stable
XML_RPC           1.5.4   stable
XML_Util          1.2.3   stable

これで、さくっと書いて実行する。

require_once 'MDB2.php';

$dsn = 'pgsql://***:***@***/***';
$options = array(
    'debug' => 2,
    'result_buffering' => false,
);

$mdb2 = MDB2::connect($dsn);
if (PEAR::isError($mdb2)) {
    die($mdb2->getMessage());
}else{
    $sql="INSERT INTO test (id,name,value) VALUES (1,NULL::TEXT,?);";
    $statement=$mdb2->prepare($sql);
    if (PEAR::isError($statement)){
        echo $statement->getDebugInfo();
    }else{
        $statement->execute(array('test'));
    }
}

$mdb2->disconnect();

とすると、やはり同じエラーが出た。
ということで、MDB2が悪いことが確定して一安心。

解決

そもそも、キャスト演算子がコロンでできている以上、検索のしにくさったら。
過去の例が全然見当たらない。

こういうときはとりあえずバージョンアップをしてみよう。

# pear install MDB2-2.5.0b5
# pear install MDB2_Driver_pgsql-1.5.0b4
#
# pear list
Installed packages, channel pear.php.net:
=========================================
Package           Version State
Archive_Tar       1.4.2   stable
Console_Getopt    1.3.1   stable
MDB2              2.5.0b5 beta
MDB2_Driver_pgsql 1.5.0b4 beta
PEAR              1.9.5   stable
Structures_Graph  1.0.4   stable
XML_RPC           1.5.4   stable
XML_Util          1.2.3   stable

とすると、あっさり成功したのでした。

なので、MDB2ChangeLogを読み漁る。
すると、MDB2_Driver_pgsql 1.5.0a1 において、

Changelog:

〜〜〜

- fixed bug #11652: failed prepared queries containing the "::type" style of casting

〜〜〜

なんと、2007-07-20 12:38 UTC に報告された不具合である。
10年前て・・・