事件は突然に
ある日アプリケーション・コンテナの検証していたところ、pdb_to_apppdb.sql の実行中に、見慣れたエラーが見慣れない場所で発生しました。
SQL> @?/rdbms/admin/pdb_to_apppdb.sql
SQL> SET FEEDBACK 1
SQL> SET NUMWIDTH 10
-----------------------------------<中略>-----------------------------------
SQL> alter system flush shared_pool;
alter system flush shared_pool
*
行1でエラーが発生しました。:
ORA-01031: 権限が不足しています
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0との接続が切断されました。
実行ユーザーは SYSDBA でした。
「SYSDBAなのに権限不足とは?」
「バグでは?」
「内部スクリプトが怪しいのでは?」
そんなふうにOracleを疑い続けた結果、犯人はOracleではありませんでした。
過去の自分でした。
環境
今回の検証環境は以下です。
- Oracle Database 19c
- Version 19.3.0.0
- アプリケーション・ルート:
APPROOT - アプリケーションPDB:
APPPDB1 -
APPPDB1は通常のPDBをクローンして作成
コンテナの構成は以下です。
CDB$ROOT
├─ PDB1 --クローン元
└─ APPROOT
├─ APPROOT$SEED
└─ APPPDB1 -- PDB1をクローンして作成したアプリケーション・コンテナ
発生した事象
今回、PDBをクローン元としてアプリケーションPDBを作成する検証をしていました。
SQL> ALTER SESSION SET CONTAINER=APPROOT;
セッションが変更されました。
SQL> CREATE PLUGGABLE DATABASE APPPDB1 FROM PDB1;
プラガブル・データベースが作成されました。
通常のPDBとして作成した APPPDB1 をアプリケーションPDBへ変換するため、以下を実行しました。
ALTER SESSION SET CONTAINER=APPPDB1;
@?/rdbms/admin/pdb_to_apppdb.sql
スクリプトは順調に進んでいるように見えました。
ビューが作成され、内部表が更新され、アプリケーション・ルートとの同期も行われています。
と思った直後、冒頭のエラーが発生しました。
急にSQL Plus追い出されました。
SYSDBAなのに権限不足?
実行時の接続は以下です。
sqlplus / as sysdba
当然、最初に疑ったのは接続ミスです。
SQL> show user
ユーザーは"SYS"です。
SYSです。
さらに、
SQL> SELECT SYS_CONTEXT('USERENV','ISDBA') FROM DUAL;
SYS_CONTEXT('USERENV','ISDBA')
--------------------------------------------------------------------------------
TRUE
しっかりSYSDBAです。
にもかかわらず、
ALTER SYSTEM FLUSH SHARED_POOL;
でORA-01031が発生しています。
この時点で頭の中はこうなりました。
……え?
SYSDBAで権限不足?
SYSDBAが権限不足なら、いったい誰なら権限が足りるのか。
変換状況を確認
スクリプトが途中終了したため、変換自体が失敗したと思いました。
しかし、アプリケーション・ルートで確認すると、少し不思議な状態になっていました。
SQL> ALTER SESSION SET CONTAINER=APPROOT;
セッションが変更されました。
SQL> SELECT pdb_name,
application_pdb,
status
FROM dba_pdbs
WHERE pdb_name='APPPDB1';
PDB_NAME APPLICATION_PDB STATUS
--------- ---------------- ----------
APPPDB1 YES NORMAL
APPLICATION_PDB は YES です。
さらに、APPPDB1でアプリケーション・ルート名を確認します。
SQL> ALTER SESSION SET CONTAINER=APPPDB1;
SQL> SELECT SYS_CONTEXT('USERENV','APPLICATION_NAME') FROM DUAL;
SYS_CONTEXT('USERENV','APPLICATION_NAME')
--------------------------------------------------------------------------------
APPROOT
APPROOTとの関連付けもできています。
「成功しているのでは?」
と思い、PDBを通常モードで開き直しました。
ALTER SESSION SET CONTAINER=CDB$ROOT;
ALTER PLUGGABLE DATABASE APPPDB1 CLOSE IMMEDIATE;
ALTER PLUGGABLE DATABASE APPPDB1 OPEN;
すると、次の警告が表示されました。
警告: PDBが変更されましたが、エラーがあります。
確認すると、APPPDB1は制限モードで開いています。
SELECT name,
open_mode,
restricted
FROM v$pdbs
WHERE name='APPPDB1';
NAME OPEN_MODE RESTRICTED
-------- ----------- ----------
APPPDB1 READ WRITE YES
Application PDBにはなっていますが、何かが終わっていません。
PDB_PLUG_IN_VIOLATIONSを確認
PDB関連で何かがおかしいときは、まず PDB_PLUG_IN_VIOLATIONS を確認します。
SELECT cause,
type,
message,
status
FROM pdb_plug_in_violations
WHERE name='APPPDB1'
ORDER BY time;
結果:
CAUSE
------------------------------------------
Non-Application PDB to Application PDB
TYPE
---------
ERROR
MESSAGE
------------------------------------------------------------
Non-Application PDB plugged in as an Application PDB,
requires pdb_to_apppdb.sql be run.
STATUS
---------
PENDING
Oracleからは、
pdb_to_apppdb.sqlを実行してください。
と言われています。
実行しました。
実行した結果、途中で追い出されたのです。
バグを疑う
困ったので、チャッピー君へ質問してみました。
回答は、
PENDING が消えずに再度 ORA-01031 で落ちるなら、19.3の不具合寄りなので、次はスクリプトをコピーして alter system flush shared_pool; の行だけ一時的にコメントアウトして通す方法を検討します。
ここからバグを疑い始めました。
環境は19.3.0.0です。
19cの初期リリースなので、アプリケーション・コンテナ周りの不具合があっても不思議ではありません。
さらに、エラー直前のソースコードを見ると、以下のようになっています。
ALTER SESSION SET "_APPLICATION_SCRIPT"=TRUE;
ALTER SYSTEM FLUSH SHARED_POOL;
pdb_to_apppdb.sql から呼び出される loc_to_common2.sql の中で、内部パラメータを有効にした直後にエラーが発生しています。
そのため、次のような仮説を立てました。
-
_APPLICATION_SCRIPT=TRUEのときだけ権限チェックがおかしくなる - Oracle 19.3固有の不具合?
- PDB内から
FLUSH SHARED_POOLを実行できない - スクリプト自体に不具合がある
かなりOracleを疑っています。
Oracleさん、すみませんでした。
一時は、以下をコメントアウトして回避することも考えました。
しかし、スクリプトを変更する前に、
そもそも、なぜSYSDBAでORA-01031になるのか?
を確認することにしました。
コメントアウトして動いたとしても、それは解決ではなく、エラー箇所を通らないようにしただけです。
ここで、クローン元PDBに何を設定していたかを思い出しました。
犯人は自分だった
以前クローン元のPDBには、検証目的でPDBへロックダウンプロファイルを設定していました。
ロックダウン・プロファイルは、PDB内で実行できる文や機能を制限する仕組みです。
例えば、以下のように ALTER SYSTEM を禁止できます。
CREATE LOCKDOWN PROFILE PDB_LOCKDOWN_PROFILE;
ALTER LOCKDOWN PROFILE PDB_LOCKDOWN_PROFILE DISABLE STATEMENT = ('ALTER SYSTEM');
PDBに適用する場合は、次のように設定します。
ALTER SYSTEM SET PDB_LOCKDOWN='PDB_LOCKDOWN_PROFILE';
今回、APPPDB1はこのPDBを元にクローンして作成していました。
そしてクローン先のAPPPDB1でも、ロックダウン・プロファイルが有効な状態になっていました。
確認します。
ALTER SESSION SET CONTAINER=APPPDB1;
SHOW PARAMETER PDB_LOCKDOWN
NAME TYPE VALUE
------------- ------- --------------------
pdb_lockdown string PDB_LOCKDOWN_PROFILE
いました。
ずっと原因はそこにいました。
なぜSYSDBAでもORA-01031になったのか
pdb_to_apppdb.sql の内部では、次の文が実行されます。
ALTER SYSTEM FLUSH SHARED_POOL;
しかし、APPPDB1に適用されているロックダウン・プロファイルでは、ALTER SYSTEM を禁止していました。
そのため、SYSDBAで接続していても、APPPDB1内で実行された文は拒否されます。
結果として、
ORA-01031: 権限が不足しています
が発生していました。
つまり、バグでも、SYSDBA権限の消失でもありません。
ロックダウン・プロファイルが正常に仕事をしていただけです。
正常に仕事をした結果、作業が失敗しました。
原因の流れ
今回の事象を整理すると、以下の流れになります。
クローン元PDBにロックダウン・プロファイルを設定
↓
APPPDB1にも設定が引き継がれる
↓
pdb_to_apppdb.sqlを実行
↓
スクリプト内部でALTER SYSTEM FLUSH SHARED_POOLを実行
↓
ロックダウン・プロファイルがALTER SYSTEMを拒否
↓
ORA-01031
↓
スクリプトが途中終了
すべてつながりました。
対処方法
今回は検証環境のため、変換作業中だけロックダウン・プロファイルを解除しました。
ALTER SYSTEM SET PDB_LOCKDOWN='' SCOPE=BOTH;
再度スクリプトを実行して正常終了すれば、原因がLockdown Profileであることを確認できます。
pdb_to_apppdb.sqlを再実行
APPPDB1をUPGRADEモードで開きます。
ALTER SESSION SET CONTAINER=CDB$ROOT;
ALTER PLUGGABLE DATABASE APPPDB1 CLOSE IMMEDIATE;
ALTER PLUGGABLE DATABASE APPPDB1 OPEN UPGRADE;
APPPDB1へ切り替え、スクリプトを再実行します。
ALTER SESSION SET CONTAINER=APPPDB1;
@?/rdbms/admin/pdb_to_apppdb.sql
今回は、ALTER SYSTEM FLUSH SHARED_POOL でORA-01031は発生せず、最後まで完了しました。
まとめ:今回の反省点
今回の最大の反省点は、
『バグを疑う前に、自分が設定した項目を思い出そう』 です。
特にORA-01031を見ると、次のような点に意識が向きがちでした。
- ユーザーに権限がない
- SYSDBAで接続していない
- 必要なロールが有効になっていない
- コンテナを間違えている
しかし、マルチテナント環境ではロックダウン・プロファイルによって、PDB単位でSQL文や機能が制限されている可能性があります。
SYSDBAだから何でもできるとは限りません。
エラーメッセージだけを見ると権限付与に目が行きますが、マルチテナント環境ではPDB単位の制限も忘れずに確認したいところです。