PostgreSQL 8.3.xだとリポジトリブラウザが見えない

2008/07/15 追記:
類似の不具合が判明したので併せて修正しています。

会社が変わったので、別PC上でOSその他もろもろ最新バージョンにあげてみたのですが、
Tracリポジトリブラウザが見えないと言う状況が発生しました。

インストールしているもろもろのバージョンはこんな感じです。

OS CentOS 5.1
PostgreSQL 8.3.1
Python 2.5.2
DB Driver? pyPgSQL 2.5.1
Trac 0.10.4-ja-1
subversion 1.4.6

以下は、その原因ならびに対応方法です。

せっかちな人用に先にパッチを書いておきます。

$TRAC_ROOT/lib/python2.5/site-packages/trac/versioncontrol/cache.py
を以下のように変更すれば、動作しました。
旧:

246:        cursor.execute("SELECT time,author,message FROM revision "
247:                       "WHERE rev=%s", ((rev),))
:
257         cursor.execute("SELECT path,node_type,change_type,base_path,base        _rev "
258                        "FROM node_change WHERE rev=%s "
259                        "ORDER BY path", (self.rev,))

新:

246:        cursor.execute("SELECT time,author,message FROM revision "
247:                       "WHERE rev=%s", ((str(rev)),))
:
257         cursor.execute("SELECT path,node_type,change_type,base_path,base        _rev "
258                        "FROM node_change WHERE rev=%s "
259                        "ORDER BY path", ((str(self.rev)),))

以下は、事象の発生から原因特定まで。



普通にTracをインストールして、リポジトリブラウザを表示すると、
以下のようなコールスタックがでてしまいます。

Traceback (most recent call last):
  File "/opt/trac/lib/python2.5/site-packages/trac/web/main.py", line 406, in dispatch_request
    dispatcher.dispatch(req)
  File "/opt/trac/lib/python2.5/site-packages/trac/web/main.py", line 237, in dispatch
    resp = chosen_handler.process_request(req)
  File "/opt/trac/lib/python2.5/site-packages/trac/versioncontrol/web_ui/browser.py", line 143, in process_request
    self._render_directory(req, repos, node, rev)
  File "/opt/trac/lib/python2.5/site-packages/trac/versioncontrol/web_ui/browser.py", line 168, in _render_directory
    changes = get_changes(self.env, repos, [i['rev'] for i in info])
  File "/opt/trac/lib/python2.5/site-packages/trac/versioncontrol/web_ui/util.py", line 37, in get_changes
    changeset = repos.get_changeset(rev)
  File "/opt/trac/lib/python2.5/site-packages/trac/versioncontrol/cache.py", line 45, in get_changeset
    self.authz)
  File "/opt/trac/lib/python2.5/site-packages/trac/versioncontrol/cache.py", line 247, in __init__
    "WHERE rev=%s", ((rev),))
  File "/opt/trac/lib/python2.5/site-packages/trac/db/util.py", line 50, in execute
    return self.cursor.execute(sql_escape_percent(sql), args)
  File "/opt/trac/lib/python2.5/site-packages/trac/db/util.py", line 50, in execute
    return self.cursor.execute(sql_escape_percent(sql), args)
  File "/opt/python/lib/python2.5/site-packages/pyPgSQL/PgSQL.py", line 3111, in execute
    raise OperationalError, msg
OperationalError: ERROR:  operator does not exist: text = integer
LINE 1: SELECT time,author,message FROM revision WHERE rev=56
                                                          ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.

原因は多分これでしょう。
OperationalError: ERROR: operator does not exist: text = integer
LINE 1: SELECT time,author,message FROM revision WHERE rev=56


文字列と数値を比較すんなってことですね。
まずは、データベースのテーブルの内容を確認してみます。

    Table "public.revision"
 Column  |  Type   | Modifiers
---------+---------+-----------
 rev     | text    | not null
 time    | integer |
 author  | text    |
 message | text    |
Indexes:
    "revision_pkey" PRIMARY KEY, btree (rev)
    "revision_time_idx" btree ("time")

確かに文字列でした。古いマシン上(PostgreSQL 8.2.4)でもテーブル構成は一緒です。

何が違うか比べてみました。

unmutch
Trac 0.10.4-ja-1 0.10.4-ja-1  
DB Driver? pyPgSQL 2.5.1 pyPgSQL 2.5.1  
Python 2.5.1 2.5.2 x
PostgreSQL 8.2.4 8.3.1 x
OS Fedora Core3 CentOS 5.1 x

一番簡単そうなDBから手をつけることにしました。
上記でエラーとなったSQL「SELECT time,author,message FROM revision WHERE rev=56」を
8.2.4と、8.3.1上で直接実行してみました。
見事に、8.3.1だけこけます。


理由を調べてみました。
ostgreSQL 8.3 に関する技術情報
に、以下のような記述があります。

今までは text 型入力を受けとる演算子や関数に文字でない値が渡されると、自動的に text 型にキャストしていました。 これからは text 型でないデータを渡したい場合には text 型への明示的なキャストが必要になります。 例えば以前は以下の式が動作していました。

これっすね。いろいろ理由はあるみたいですが、、、
余計なことすんなぁぁぁ!!!

じゃぁ、明示的にキャストなりクォートで囲むなりすればいいんだなと思って、
コールスタックを元に調べてみましたよ。

で、pyPgSQL君へきちんとStringTypeの変数を渡せば怒られない事を下記サンプルで
確認できたので、上記のような対応にいたるわけです。

import pyPgSQL.PgSQL
con = pyPgSQL.PgSQL.connect("localhost:5432:tableName:uid:password")
rev=56
cur = con.cursor()
cur.execute("SELECT time,author,message FROM revision "
"where rev=%s", ((str(rev)),))
cur.close()
con.close()


どっちが作法的によろしくないかと言えばTracなんだけど、OracleMySQLもこれは通るよなぁ。。。
それに、偏見かもしれないけど、LLって型付け弱いからせめて、テーブルのメタ情報から類推してほしいなぁ。。。
#でも、そうなると遅くなる可能性もかなり高いからなぁ。


やっぱ型はきちんと意識しようぜってことです。