2021年4月18日日曜日

行のロックのPL/SQLコードの制限

 こちらの記事で自動DMLプロセスの行のロックPL/SQLコードを記述しても、実行されないようだ、と説明しました。その点について、さらに調べて確認しました。

結果としては、対話グリッドについては期待通り動作しました。フォームは、現在のところ、PL/SQLコードは実行されません。

対話グリッドでは動作するので、確認してみました。以下、作業ログです。

検証に使用したアプリケーションのエクスポートを以下に置いています。

https://github.com/ujnak/apexapps/blob/master/exports/lockrowplsqlcode.sql

サンプル・データセットのEMP/DEPTに含まれる表EMPを使っています。表EMPから対話モード・レポートとフォームのページを作成し、それに加えて、対話グリッドのページを作成しています。

対話グリッドの行の自動処理(DML)のプロセスの行のロックPL/SQL Codeに切り替え、以下のコードを記述します。

declare
r emp%rowtype;
begin
apex_debug.info('my_custom_row_lock');
select * into r from emp
where empno = :EMPNO for update nowait;
end;

for update nowaitなので、行がロック済みであればORA-54が発生し、ロックの待機は行いません。

実際に表EMPにロックをかけた上で、対話グリッドから保存を実行するとORA-54が発生することが確認できます。

指定した秒数だけロックを待機するよう、コードを書き換えます。10秒だけ待ちます。

declare
r emp%rowtype;
begin
apex_debug.info('my_custom_row_lock');
select * into r from emp
where empno = :EMPNO for update wait 10;
end;

対話グリッドから変更を保存してみます。指定した秒数だけ待機したのち、エラーが発生します。発生したエラーはORA-54ではなく、ORA-30006 resource busy; acquire with WAIT timeout expiredです。

最後にfor updateのみ指定し、ずっと待機する動作を確認してみます。

declare
r emp%rowtype;
begin
apex_debug.info('my_custom_row_lock');
select * into r from emp
where empno = :EMPNO for update;
end;

対話グリッドから変更を保存してみます。Saveをクリックすると待機が始まり、ずっと待機します。

待機しているセッションが実行しているSQLを確認します。

SQL> select sql_text from v$sql

  2  where sql_id in

  3  (

  4   select sql_id from v$session

  5   where sid in

  6   (

  7       select waiting_session from dba_waiters

  8   )

  9  );


SQL_TEXT

--------------------------------------------------------------------------------

SELECT * FROM EMP WHERE EMPNO = :B1 FOR UPDATE


SQL> 

今度はUPDATE文ではなく、行のロックに記述したPL/SQLコード内で実行されているSELECT文で待機していることがわかります。

これで現行の対話グリッドでは、FSP_DML_LOCK_ROWやAPEX_DML_LOCK_WAIT_TIMEによる効果を、行のロックとしてPL/SQL Codeを選んで、PL/SQLコードを記述することによって達成できることがわかります。

以上です。

Oracle APEXによるアプリケーション作成の参考になれば幸いです。