システム・トリガーを試す（１）（Oracle Database 19c)

Oracle Database 上で何等かのイベントが発生した場合にプログラムを実行する仕組みをトリガーと呼びます。トリガーは CREATE TRIGGER 文で登録します。一般的にはテーブルに対して作成され、レコードの INSERT / DELETE / UPDATE の単位で何等かのプログラム（通常 PL/SQL プロシージャ）を実行したい場合に使用します。トリガーの実行タイミング（BEFORE / AFTER）や、レコード単位（FOR EACH ROW）／文単位に実行する等の設定を行うことができます。

# システム・トリガー
この記事で紹介するのは、テーブルに格納されたレコードの更新や DML 単位ではなく Oracle Database インスタンス上で発生したイベントに対するトリガーです。CREATE TRIGGER 文の[マニュアル](https://docs.oracle.com/cd/F19136_01/lnpls/CREATE-TRIGGER-statement.html#GUID-AF9E33F1-64D1-4382-A6A4-EC33C36F237B)では system_trigger と記載されています。例えば以下のようなイベントでトリガーを実行可能です。

## DDL Event
 何等かのDDLが実行された場合に発行されるトリガーです。

|イベント|説明|
|:---|:---|
|ALTER|ALTER DATABASE文を除くALTER文の実行|
|ANALYZE|統計情報の制御|
|AUDIT|AUDIT文の発行|
|ASSOCIATE STATISTICS|統計タイプがオブジェクトに関連付けられる|
|COMMENT|コメントが追加される|
|CREATE|CREATE文でオブジェクト作成|
|DROP|オブジェクト削除|
|GRANT|権限の付与|
|NOAUDIT|NOAUDIT文の実行|
|RENAME|オブジェクト名の変更|
|REVOKE|権限のはく奪|
|TRUNCATE|TRUNCATE文の実行|
|DDL|上記のいずれかのDDLの実行|

## DATABASE Event
　データベース上のイベントが発生した場合に発行されるトリガーです。

|イベント|説明|
|:---|:---|
|AFTER STARTUP|データベースがオープンされた|
|BEFORE SHUTDOWN|インスタンスが停止されようとしている|
|AFTER DB_ROLE_CHANGE|ロールが変更された|
|AFTER SERVERERROR| エラーが発生した|
|AFTER LOGON|クライアントが接続した|
|BEFORE LOGOFF|クライアントが切断しようとしている|
|AFTER SUSPEND|トランザクションが停止した|
|BEFORE UNPLUG|PDBが切断されようとしている|
|BEFORE or AFTER SET CONTAINER| ALTER SESSION SET CONTAINERの実行前後|

# インスタンスのトリガー
この中でインスタンス起動／停止時に実行されるトリガーを試します。

# AFTER STARTUP TRIGGER
　データベース・インスタンスが起動した場合に実行されるトリガーです。AFTER STARTUP ON DATABASE句を使ってトリガーstartup_trigger1を作成します。下記の例ではトリガーが起動されると、現在時刻と「: AFTER STARTUP TRIGGER」文をアラーとログに書き込みます。

```SQL: AFTER STARTUP トリガーの作成
SQL> CREATE OR REPLACE TRIGGER startup_trigger1 AFTER STARTUP ON DATABASE
BEGIN
    DBMS_SYSTEM.KSDWRT(2, TO_CHAR(SYSTIMESTAMP) || ': AFTER STARTUP TRIGGER');
END;
/
トリガーが作成されました
```

インスタンスを起動してアラートログを確認します。

```
[5725] Successfully onlined Undo Tablespace 2.
Undo initialization online undo segments: err:0 start: 1885769 end: 1885828 diff: 59 ms (0.1 seconds)
Undo initialization finished serial:0 start:1885752 end:1885831 diff:79 ms (0.1 seconds)
Verifying minimum file header compatibility for tablespace encryption..
Verifying file header compatibility for tablespace encryption completed for pdb 0
Database Characterset is AL32UTF8
No Resource Manager plan active
joxcsys_required_dirobj_exists: directory object exists with required path /u01/app/oracle/product/19.0.0/dbhome_1/javavm/admin/, pid 5725 cid 0
replication_dependency_tracking turned off (no async multimaster replication found)
Starting background process AQPC
2021-04-04T16:23:51.852272+09:00
AQPC started with pid=41, OS id=5733
21-04-04 16:23:52.035240000 +09:00: AFTER STARTUP TRIGGER
Starting background process CJQ0
2021-04-04T16:23:52.051488+09:00
CJQ0 started with pid=42, OS id=5754
Completed: ALTER DATABASE OPEN
2021-04-04T16:23:52.138424+09:00
TMON (PID:5713): STARTING ARCH PROCESSES
Starting background process ARC1
2021-04-04T16:23:52.147342+09:00
ARC1 started with pid=51, OS id=5758
Starting background process ARC2
2021-04-04T16:23:52.156730+09:00
```
上記でわかるように、「Completed: ALTER DATABASE OPEN」のログが出力される直前にトリガーが実行されていることがわかります。この時点ではアーカイバ・プロセスは起動していません。またインスタンスがオープンされないと実行されないため、Data Guardスタンバイ・インスタンスでは通常は実行されません。

# BEFORE SHUTDOWN TRIGGER
　SHUTDOWN コマンドを実行され、データベース・インスタンスが停止した場合に実行されるトリガーです。BEFORE SHUTDOWN ON DATABASE 句を使って shutdown_trigger1 トリガーを作成します。下記の例ではトリガーが起動されると、現在時刻と「: BEFORE SHUTDOWN TRIGGER」文をアラーとログに書き込みます。

```SQL: BEFORE SHUTDOWN トリガーの作成
SQL> CREATE OR REPLACE TRIGGER shutdown_trigger1 BEFORE SHUTDOWN ON DATABASE
BEGIN
    DBMS_SYSTEM.KSDWRT(2, TO_CHAR(SYSTIMESTAMP) || ': BEFORE SHUTDOWN TRIGGER');
END;
/
トリガーが作成されました
```

インスタンスを停止してアラートログを確認します。

```
2021-04-04T16:22:26.735204+09:00
Shutting down ORACLE instance (immediate) (OS id: 4354)
2021-04-04T16:22:26.743418+09:00
Shutdown is initiated by sqlplus@rel78-2 (TNS V1-V3).
Stopping background process SMCO
2021-04-04T16:22:28.900938+09:00
Shutting down instance: further logons disabled
2021-04-04T16:22:29.052145+09:00
Stopping background process CJQ0
Stopping background process MMNL
2021-04-04T16:22:30.059440+09:00
Stopping background process MMON
2021-04-04T16:22:32.060912+09:00
21-04-04 16:22:32.060808000 +09:00: BEFORE SHUTDOWN TRIGGER
License high water mark = 4
Stopping Emon pool
2021-04-04T16:22:32.062000+09:00
Stopping all emon processes
Stopping EMNC
2021-04-04T16:22:32.071580+09:00
Stopping all emon processes
Dispatchers and shared servers shutdown
```
SHUTDOWN コマンドを実行し、いくつかのバックグラウンド・プロセス停止後にトリガーが動作していることがわかります。Data Guard スタンバイ・インスタンスのシャットダウンではログが出力されなかったため、スタンバイ・インスタンスではトリガーは起動しないのかもしれません。

BEFORE SHUTDOWN トリガーはインスタンスの異常終了時には実行されないようです。LGWR プロセスを強制終了した際のアラートにはトリガーの実行を示すログは出力されませんでした。

```
PMON (ospid: 34140): terminating the instance due to ORA error 470
2021-04-05T09:01:22.208708+09:00
Cause - 'Instance is being terminated due to fatal process death (pid: 20, ospid: 34177, LGWR)'
2021-04-05T09:01:22.218169+09:00
System state dump requested by (instance=1, osid=34140 (PMON)), summary=[abnormal instance termination].
System State dumped to trace file /u01/app/oracle/diag/rdbms/o19a/O19A/trace/O19A_diag_34160.trc
2021-04-05T09:01:22.532299+09:00
Dumping diagnostic data in directory=[cdmp_20210405090122], requested by (instance=1, osid=34140 (PMON)), summary=[abnormal instance termination].
2021-04-05T09:01:23.618609+09:00
Instance terminated by PMON, pid = 34140
```