Oracleプロシージャ実行時に「{}」が有るとエラー

閲覧: 459 回
最初の未読メッセージにスキップ

hajimeni

未読、
2010/03/30 21:43:552010/03/30
To: DBFluteユーザの集い
西山(hajimeni)です。

環境:
DBFlute:0.9.5.5
Oracle10gR2(10.2.0.3)

ちょっとマニアックすぎる現象ですので、こちらに書きます。

Oracleのバグで、ストアドプロシージャを呼ぶ際に、
CallableStatement stmt = conn.prepareCall("{call p1(?)}");
というように、「{}」を付けた呼び方をすると、ダメな場合があるようです。

回避手段は、
CallableStatement stmt = conn.prepareCall("call p1(?)");
というように、「{}」を付けないで呼べば大丈夫になります。

「CHAR 型で IN/OUT の引数を持つプロシージャで、特定の呼び方をした場合のみ」
に発生するようで、極々極々レアなパターンだとは思います。(詳細はOracle Krown)

Oracle社も把握してるけど、回避手段があるから優先度を下げているそうです。

この対応をしないとダメな現象が発生してしまいましたので、{}を付けないで実行出来るように修正しょうと思うのですが、
TnProcedureCommand クラスの createSql を修正するだけで大丈夫でしょうか?(使用しているバージョンを上げるのは無理
そうですので、runtimeをなおそうと思っています。)

また、Oracleだけの問題かもしれませんが、回避手段を取れるようなオプションか何かを検討した方がいいのではないでしょうか。
(といっても、プロシージャ単位で制御しないといけないのかどうかも不明ですが。。。戻り値ある場合も不明ですし。。。)

kubo

未読、
2010/03/30 22:20:112010/03/30
To: dbf...@googlegroups.com
jfluteです。

hajimeniさん、こんにちは

確かにマニアックの現象ですね。。。
アップグレードできないのであれば、
とりあえず runtime を修正するようにして下さい。
(ImplementedInvokerAssistant経由やExBhv経由から
オーバーライドの連鎖で runtime 触れずには行けないかなぁ...)

> 「CHAR 型で IN/OUT の引数を持つプロシージャで、特定の呼び方をした場合のみ」
> に発生するようで、極々極々レアなパターンだとは思います。(詳細はOracle Krown)

dbflute-oracle-exampleで再現してみたいのですが、
どんなプロシージャかって、一言で言い表せます?
(現象が複雑なのかな!?)

規則的なら、特別な処理を入れて自動で回避するようにしたい
のですが、そうでないなら、ProcedurePmbのオプションとかで
指定して回避できるようにしようかなと。

2010/3/31 hajimeni <hajim...@gmail.com>:

> --
> このメールは Google グループのグループ「DBFluteユーザの集い」の登録者に送られています。
> このグループに投稿するには、dbf...@googlegroups.com にメールを送信してください。
> このグループから退会するには、dbflute+u...@googlegroups.com にメールを送信してください。
> 詳細については、http://groups.google.com/group/dbflute?hl=ja からこのグループにアクセスしてください。
>
>

kubo

未読、
2010/03/30 22:30:512010/03/30
To: dbf...@googlegroups.com
jfluteです。

局所的であれば、
exbhvで、
1. outsideSql()をオーバーライド
2. OutsideSqlBasicExecutorをオーバーライド
3. OutsideSqlCallCommandをオーバーライド
4. TnProcedureCommandをオーバーライド
でどうかな?
プロシージャの名前でif文も書けるし。
(古いバージョンでもどうかな...)

2010/3/31 kubo <dbf...@gmail.com>:

taktos

未読、
2010/03/30 22:33:022010/03/30
To: DBFluteユーザの集い
瀧口(taktos)です。

半年くらい前に、jfluteさんと話したような記憶が。。
dbflute-oracle-example の SP_ORACLE_CHAR_INOUT_PARAMETER がそれだと思います。
定義はこんな感じ
-- #df:begin#
create or replace procedure SP_ORACLE_CHAR_INOUT_PARAMETER(
v_inout_char in out char) as
begin
dbms_output.put_line(v_inout_char);
v_inout_char := 'bar';
end SP_ORACLE_CHAR_INOUT_PARAMETER;
/
-- #df:end#

当時はバグが再現できなかったように記憶してますが、hajimeni さんの問題と、なにか違うところありますか?


On 3月31日, 午前11:30, kubo <dbfl...@gmail.com> wrote:
> jfluteです。
>
> 局所的であれば、
> exbhvで、
> 1. outsideSql()をオーバーライド
> 2. OutsideSqlBasicExecutorをオーバーライド
> 3. OutsideSqlCallCommandをオーバーライド
> 4. TnProcedureCommandをオーバーライド
> でどうかな?
> プロシージャの名前でif文も書けるし。
> (古いバージョンでもどうかな...)
>

> 2010/3/31 kubo <dbfl...@gmail.com>:


>
>
>
> > jfluteです。
>
> > hajimeniさん、こんにちは
>
> > 確かにマニアックの現象ですね。。。
> > アップグレードできないのであれば、
> > とりあえず runtime を修正するようにして下さい。
> > (ImplementedInvokerAssistant経由やExBhv経由から
> > オーバーライドの連鎖で runtime 触れずには行けないかなぁ...)
>
> >> 「CHAR 型で IN/OUT の引数を持つプロシージャで、特定の呼び方をした場合のみ」
> >> に発生するようで、極々極々レアなパターンだとは思います。(詳細はOracle Krown)
> > dbflute-oracle-exampleで再現してみたいのですが、
> > どんなプロシージャかって、一言で言い表せます?
> > (現象が複雑なのかな!?)
>
> > 規則的なら、特別な処理を入れて自動で回避するようにしたい
> > のですが、そうでないなら、ProcedurePmbのオプションとかで
> > 指定して回避できるようにしようかなと。
>

> > 2010/3/31 hajimeni <hajimeni...@gmail.com>:

kubo

未読、
2010/03/30 22:41:502010/03/30
To: dbf...@googlegroups.com
jfluteです。

> 半年くらい前に、jfluteさんと話したような記憶が。。
あ、確かに...

> dbflute-oracle-example の SP_ORACLE_CHAR_INOUT_PARAMETER がそれだと思います。
これは実行できてますね。。。
(そういえば再現できなかったとかだったか...)


#
# すいません、ちょっと出かけるので
# 帰ってからメールみます。
#

2010/3/31 taktos <tak...@gmail.com>:

西山はじめ

未読、
2010/03/30 22:54:472010/03/30
To: dbf...@googlegroups.com
hajimeniです。

再現出来るプロシージャなんですが、ネットにも接続出来ず持ち出せない、クライアントさんの環境にしかないので、なんとも・・・(私自身も助っ人で解析しに行っただけですので・・・)

しかし以前にもそのような問題が合ったのですね・・・

再現出来るSQLが作れましたら報告します。

> 1. outsideSql()をオーバーライド
> 2. OutsideSqlBasicExecutorをオーバーライド
> 3. OutsideSqlCallCommandをオーバーライド
> 4. TnProcedureCommandをオーバーライド

これで試してみます。
コッチのほうが、runtimeに手を入れないので何かと応用出来そうです。


2010年3月31日11:41 kubo <dbf...@gmail.com>:

--
--
西山はじめ

hajimeni

未読、
2010/03/31 0:20:472010/03/31
To: DBFluteユーザの集い
hajimeniです。

再現できるSQLを作成しました。
プロシージャ内でSelect なり insert/update なりがあるとダメなようです。

create table hoge( c_data varchar(30) );
create or replace procedure char_parameter(p1 in out char) as
v1 varchar2(30);
begin
select c_data into v1 from hoge
where c_data = rtrim(p1);
end;
/

これで、
CallableStatement cs = connection.prepareCall("{call
CHAR_PARAMETER(?)}");
cs.registerOutParameter(1, java.sql.Types.VARCHAR);
cs.setString(1, "JDBC Test p1");
cs.executeUpdate();

だと通らず、
CallableStatement cs = connection.prepareCall("call
CHAR_PARAMETER(?)");
にすると通ります。
利用したOracleは 9.2.0.5
文字コードは SJIS
JDBC は ojdbc14.jar (10.2付属) と classes12.jar (10.2付属)
JDKは、1.5 と 1.6 。
で試しましたが、全部ダメでした。

On 3月31日, 午前11:54, 西山はじめ <hajimeni...@gmail.com> wrote:
> hajimeniです。
>
> 再現出来るプロシージャなんですが、ネットにも接続出来ず持ち出せない、クライアントさんの環境にしかないので、なんとも・・・(私自身も助っ人で解析しに行っただけですので・・・)
>
> しかし以前にもそのような問題が合ったのですね・・・
>
> 再現出来るSQLが作れましたら報告します。
>
> > 1. outsideSql()をオーバーライド
> > 2. OutsideSqlBasicExecutorをオーバーライド
> > 3. OutsideSqlCallCommandをオーバーライド
> > 4. TnProcedureCommandをオーバーライド
>
> これで試してみます。
> コッチのほうが、runtimeに手を入れないので何かと応用出来そうです。
>

> 2010年3月31日11:41 kubo <dbfl...@gmail.com>:

> --
> 西山はじめ

kubo

未読、
2010/03/31 11:16:572010/03/31
To: dbf...@googlegroups.com
jfluteです。

/= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[SQLState]
72000

[ErrorCode]
1460

[SQLException]
java.sql.SQLException
ORA-01460: リクエストされた変換はできません。
ORA-06512: "EXAMPLEDB.SP_ORACLE_CHAR_INOUT_PARAMETER", 行7
ORA-06512: 行1
= = = = = = = = = =/

やったー、再現した!ありがとうございます。
って、喜ばしい状況ではないですね。

とりあえずDBFluteでは、ProcedurePmbに isEscapeStatement()
というメソッドを追加して、デフォルトは true ですが、
PmbのExクラスのオーバーライドで抑制できるようにしました。
既に、dbflute-oracle-exampleにて反映して確認済みです。
(Exampleとしてわかりやすく無名インナークラス使ってますが、
実際にやるなら、Exクラスで固定でオーバーライドが良いでしょう)

hajimeniさんのところでは、オーバーライドの
連鎖でなんとか到達してみて下さい。


#
# ちなみに、プロシージャのカーソル(ResultSet)に
# 対応するCustomizeEntityを自動生成できるように
# しました(デフォルトでは無効で、オプション利用)。
# dbflute-oracle-example がそうなっています。
#


#
# ちなみに、Class.getSimpleName() で、
# 無名インナークラスだと空文字が戻るのびっくりしたよ
#


2010/3/31 hajimeni <hajim...@gmail.com>:

hajimeni

未読、
2010/03/31 22:34:022010/03/31
To: DBFluteユーザの集い
hajimeniです。

>やったー、再現した!ありがとうございます。
よかったです。

outsideSqlからTnProceduceCommandまでオーバーライドして動作確認は出来ました。

ちなみに、エラーですが JDBC と Oracle のバージョンの組み合わせによって
ORA-1460以外が出るかもしれません、とのことです。

発生条件をまとめると
・Oracle9i~11g
・CHAR型のIN/OUTパラメータを持つプロシージャ
・そのパラメータが、プロシージャ内でクエリーに利用されている。
・JDBCで{call procedure(p1)} のようにカッコつきで呼び出す。

になりました。
ちなみに余談ですが、大本の原因はSQL/PLUSから「begin procedure(p1); end;」と呼ぶとアウトになる問題です。
JDBCからでは、「{}」付だと「begin procedure(p1); end;」の形に自動展開されるため、この現象になるそうです。

> 無名インナークラスだと空文字が戻るのびっくりしたよ
名前がないので空文字でもしょうが無いということですか・・・

// 以前のプロジェクトでクラス名取っている箇所があった気が・・・大丈夫かな?

> 2010/3/31 hajimeni <hajimeni...@gmail.com>:

> ...
>
> もっと読む ≫

kubo

未読、
2010/04/02 6:28:372010/04/02
To: dbf...@googlegroups.com
jfluteです。

なるほど、情報ありがとう。
DB Native な構文に変換すると逆にDB固有の
バグに遭遇してしまうってことですね。

getSimpleName()は、実質的な害はないけれど、
(メイン利用はログ用だし、無名コンポーネントは登録できないし)
一応、気持ち悪いので、DBFluteの中では排除しました。
独自に Foo$1 って名前が取得できるようなユーティリティ作って、
それを利用するようにしました。

2010/4/1 hajimeni <hajim...@gmail.com>:

全員に返信
投稿者に返信
転送
新着メール 0 件