dspace

引き続き、エラーの調査。(対象バージョンはDspace 1.4.2)
「Java1.5で削除予定」となっている箇所は消しただけではエラーになる。調べてみると、Java SDK 5.0以降に有効になった記述法を使用するつもりのようだ。しかし、最近は言語構造がC#からの概念導入とかで進化しているらしいので、さっぱり分からない。最新版の文法で勉強し直した方が良さそうだ。
ちなみに、「変更予定」の通りに変更してみる*1と、「このバージョンでは対応していない」というエラーが出た。dspaceの内側ででているのか、コンパイラが出しているのかは分からなかったが、とりあえず諦めた。


で、これが問題のエラーメッセージ。インストール後、アドミニストレーターのアカウントを作成するためのバッチ処理で問題が出る。
dspaceの本家サイトにもこれと全く同じエラーメッセージについて質問した人がいて、載っていた答は「とりあえずPostgreSQL8.2だと大丈夫だ」というものだった(笑)

Exception occurred:org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = character varying
org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = character varying
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1592)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1327)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:192)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:451)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:350)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:254)
        at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:92)
        at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:92)
        at org.dspace.storage.rdbms.DatabaseManager.queryTable(DatabaseManager.java:159)
        at org.dspace.storage.rdbms.DatabaseManager.querySingleTable(DatabaseManager.java:447)
        at org.dspace.storage.rdbms.DatabaseManager.querySingleTable(DatabaseManager.java:465)
        at org.dspace.storage.rdbms.DatabaseManager.findByUnique(DatabaseManager.java:647)
        at org.dspace.storage.rdbms.DatabaseManager.find(DatabaseManager.java:612)
        at org.dspace.eperson.Group.find(Group.java:635)
        at org.dspace.administer.CreateAdministrator.main(CreateAdministrator.java:142)

142行目の呼び出し自体は、「あるテーブルでプライマリキーの値が1のデータを読み出す」というものなので、どこかでこの「1」がcharactorになってしまい、その後integerとして評価されているのだろう。


追跡できた範囲ではfindByUniqueに代入する時点で「1」はStringにキャストされている。同時にSQLのパラメータクエリを用意しており、この先SQLコマンドとして処理するためのようなのでまぁそんなものかと思って読み進んだ。


しかし、findByUniqueから呼ばれたquerySingleTable関数では問題の引数がStringである場合とintである場合で、オーバーロードが用意されている。さらにそこから呼び出されるqueryTable関数からで使われているloadParameters*2でも、パラメータとして受け取ったオブジェクトがStringの時とIntegerの時で操作を分けている(型ごとに違う動作をする)。このことから考えると、最終的にSQLに受け渡すのだとしても、ぎりぎりまで正しいデータ型を残しておかなければならないようだ。
とすると、findByUniqueに移動する際にStringにキャストしているのが間違いなのだろう。


おそらくは、PostgreSQL8.3のJDBCドライバがパラメータの型を厳密にチェックするようになったため、それまで分からなかったミスが顕在化したのだろう。
おそらく、findByUniqueにintかIntegerを引数として受け付けるバージョンをオーバーロードで用意して、findを書き直してやれば良いのではないか…


と分かったところで素直にPostgreSQLを8.2に落とすことにする(笑)


でもまぁ、ここまでやって確認しないのも何なので、一応、これでちゃんと動くようになることは確認した。


org.dspace.storage.rdbms DatabaseManeagerを二箇所変更。

    public static TableRow find(Context context, String table, int id)
            throws SQLException
    {
        String ctable = canonicalize(table);

//        return findByUnique(context, ctable, getPrimaryKeyColumn(ctable),
//                Integer.toString(id));
        return findByUnique(context, ctable, getPrimaryKeyColumn(ctable),
                id);
    }

コメント部分を変更。キャストをやめただけ。

    public static TableRow findByUnique(Context context, String table,
            String column, String value) throws SQLException
    {
        String ctable = canonicalize(table);

        if ( ! DB_SAFE_NAME.matcher(ctable).matches())
        	throw new SQLException("Unable to execute select query because table name ("+ctable+") contains non alphanumeric characters.");

        if ( ! DB_SAFE_NAME.matcher(column).matches())
        	throw new SQLException("Unable to execute select query because column name ("+column+") contains non alphanumeric characters.");
        
        String sql = "select * from " + ctable + " where "+ column +" = ? ";

        return querySingleTable(context, ctable, sql, value);
    }
    public static TableRow findByUnique(Context context, String table,
            String column, int value) throws SQLException
    {
        String ctable = canonicalize(table);

        if ( ! DB_SAFE_NAME.matcher(ctable).matches())
        	throw new SQLException("Unable to execute select query because table name ("+ctable+") contains non alphanumeric characters.");

        if ( ! DB_SAFE_NAME.matcher(column).matches())
        	throw new SQLException("Unable to execute select query because column name ("+column+") contains non alphanumeric characters.");
        
        String sql = "select * from " + ctable + " where "+ column +" = ? ";

        return querySingleTable(context, ctable, sql, value);
    }

上のStringバージョンを元にintバージョンのオーバーロードを追加。関数の宣言部分をintに書き換えただけ。

ちゃんとこうなる。

Administrator account created

Javaは詳しくないので、多分上に書いたオーバーロードよりももっとスマートな解決法があるのだろうが、これ以上はちょっと無理。
従ってSourceForge.netに投稿するとかいう恐ろしいことも無理(笑)

(しまった。ちゃんと動くようになるとインストールをやり直すのがめんどくさい(笑))

*1:引数の数が不定になる表記方法を使用している。

*2:いい加減遠い(笑)。eclipseの補助がなかったら途中でなげだしているな。