【PL/SQL】UTL_MAIL・UTL_SMTPでメール送信する方法|ACL設定・STARTTLS・エラー対処まで

【PL/SQL】メール送信を自動化する方法(UTL_MAIL / UTL_SMTP活用) PL/SQL

PL/SQLからメールを送信できるようにしておくと、夜間バッチの完了通知、エラー検知、監査レポート、問い合わせ受付の自動応答などをデータベース側で完結できます。Oracleには、簡単な通知に向く UTL_MAIL と、SMTP会話を細かく制御できる UTL_SMTP があります。

ただし、メール送信は「コードを書けば動く」だけの機能ではありません。ネットワークACL、SMTPサーバ、認証、STARTTLS、Wallet、文字コード、添付ファイルサイズ、誤送信防止、失敗時のリトライ設計まで含めて確認する必要があります。ジョブ通知として使う場合は DBMS_SCHEDULER完全ガイド、失敗履歴を残す設計は ロギングとトレーサビリティを両立する監査設計 もあわせて確認してください。

この記事で扱うこと

  • UTL_MAILUTL_SMTP の使い分け
  • ネットワークACLと SMTP_OUT_SERVER の準備
  • テキストメール、HTMLメール、添付メールの基本形
  • STARTTLS、AUTH、Walletを使う場合の注意
  • ORA-24247ORA-29278ORA-29279 の切り分け
  • 本番運用での誤送信防止・再送・ログ設計
スポンサーリンク

UTL_MAILとUTL_SMTPの使い分け

まず、どちらを使うかを決めます。UTL_MAIL は高レベルAPIで、単純なメール送信を短いコードで書けます。一方、UTL_SMTP はSMTPコマンドを扱う低レベルAPIで、認証、STARTTLS、マルチパート、細かなヘッダー制御が必要な場合に向きます。

UTL_MAIL簡単な通知、社内SMTP、認証なしのリレー、短いテキストメールに向きます。
UTL_SMTPSTARTTLS、AUTH、HTML、添付、複雑なヘッダー制御が必要な場合に向きます。
本番通知送信結果をログに残し、ジョブや監視と組み合わせる設計が重要です。
外部SMTPクラウドSMTPや認証必須SMTPでは、WalletやTLS要件を先に確認します。

前提準備:ネットワークACLを設定する

Oracle DatabaseからSMTPサーバへ接続するには、データベースユーザーに外部ネットワーク接続権限が必要です。11g以降では DBMS_NETWORK_ACL_ADMIN を使い、接続先ホストとポートに対して connect 権限を付与します。

注意点として、resolve 権限はポート範囲付きのACEに混ぜない方が安全です。DNS名前解決の権限はホスト単位で付与し、SMTP接続の connect はポート範囲付きで付与する、と分けて書くと公式仕様に沿った形になります。

network-acl.sql
-- DBA権限で実行する例
BEGIN
  -- SMTP接続権限。587番ポートだけ許可する
  DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
    host       => 'smtp.example.com',
    lower_port => 587,
    upper_port => 587,
    ace        => xs$ace_type(
      privilege_list => xs$name_list('connect'),
      principal_name => 'APP_USER',
      principal_type => xs_acl.ptype_db
    )
  );

  -- 名前解決権限。resolveはポート範囲を指定しない
  DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
    host => 'smtp.example.com',
    ace  => xs$ace_type(
      privilege_list => xs$name_list('resolve'),
      principal_name => 'APP_USER',
      principal_type => xs_acl.ptype_db
    )
  );
END;
/

ORA-24247: network access denied by access control list が出る場合は、まずACLを疑います。ユーザー名、ホスト名、ポート番号、PDB/CDBのどこで実行しているか、DBA権限で設定したかを確認してください。

UTL_MAILを使う準備

UTL_MAIL は便利ですが、環境によってはデフォルトでインストールされていません。DBAが utlmail.sqlprvtmail.plb を実行し、初期化パラメータ SMTP_OUT_SERVER を設定し、利用スキーマへ EXECUTE 権限を付与します。

utl-mail-setup.sql
-- SYSまたはDBA作業の例。実行場所は環境に合わせて確認する
@$ORACLE_HOME/rdbms/admin/utlmail.sql
@$ORACLE_HOME/rdbms/admin/prvtmail.plb

ALTER SYSTEM SET smtp_out_server = 'smtp.example.com:25' SCOPE=BOTH;

GRANT EXECUTE ON UTL_MAIL TO app_user;

SMTP_OUT_SERVERUTL_MAIL が使うSMTPサーバです。認証やSTARTTLSが必須のSMTPサーバでは UTL_MAIL だけで完結しにくいため、UTL_SMTP を検討します。

UTL_MAILでテキストメールを送る

単純な社内通知なら、UTL_MAIL.SEND が最も短く書けます。差出人、宛先、件名、本文、MIMEタイプを指定して送信します。

utl-mail-text.sql
BEGIN
  UTL_MAIL.SEND(
    sender     => 'batch@example.com',
    recipients => 'ops@example.com',
    subject    => '日次バッチ完了',
    message    => '日次バッチが正常終了しました。',
    mime_type  => 'text/plain; charset=UTF-8'
  );
END;
/

日本語メールでは、SMTPサーバやメールクライアント側の扱いも含めて文字化け確認が必要です。件名や本文のエンコード要件が厳しい場合、UTL_SMTP でヘッダーを明示的に組み立てた方が制御しやすくなります。

UTL_MAILでHTMLメールを送る

HTMLメールを送る場合は、mime_typetext/html を指定します。ただし、業務通知では見た目よりも到達性と読めることが大事です。HTMLだけでなく、必要ならテキスト版も用意します。

utl-mail-html.sql
BEGIN
  UTL_MAIL.SEND(
    sender     => 'batch@example.com',
    recipients => 'ops@example.com',
    subject    => 'エラー検知',
    message    => '<html><body><h1>エラー検知</h1><p>詳細はログを確認してください。</p></body></html>',
    mime_type  => 'text/html; charset=UTF-8'
  );
END;
/

UTL_SMTPでSTARTTLSとAUTHを扱う

認証必須のSMTPサーバやSTARTTLSが必要な環境では、UTL_SMTP を使います。重要なのは、STARTTLSを実行したあとに再度 EHLO を送ることです。TLS開始後にSMTPサーバが提示する拡張機能が変わるため、認証前に再確認します。

utl-smtp-starttls.sql
DECLARE
  c UTL_SMTP.CONNECTION;
BEGIN
  c := UTL_SMTP.OPEN_CONNECTION(
         host => 'smtp.example.com',
         port => 587,
         wallet_path => 'file:/u01/app/oracle/wallets/smtp',
         wallet_password => 'wallet_password'
       );

  UTL_SMTP.EHLO(c, 'db.example.com');
  UTL_SMTP.STARTTLS(c);

  -- STARTTLS後は再度EHLOする
  UTL_SMTP.EHLO(c, 'db.example.com');

  UTL_SMTP.AUTH(
    c        => c,
    username => 'smtp_user',
    password => 'smtp_password',
    schemes  => UTL_SMTP.ALL_SCHEMES
  );

  UTL_SMTP.MAIL(c, 'batch@example.com');
  UTL_SMTP.RCPT(c, 'ops@example.com');
  UTL_SMTP.OPEN_DATA(c);
  UTL_SMTP.WRITE_DATA(c, 'From: batch@example.com' || UTL_TCP.CRLF);
  UTL_SMTP.WRITE_DATA(c, 'To: ops@example.com' || UTL_TCP.CRLF);
  UTL_SMTP.WRITE_DATA(c, 'Subject: STARTTLS test' || UTL_TCP.CRLF);
  UTL_SMTP.WRITE_DATA(c, 'Content-Type: text/plain; charset=UTF-8' || UTL_TCP.CRLF);
  UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF);
  UTL_SMTP.WRITE_DATA(c, 'メール送信テストです。' || UTL_TCP.CRLF);
  UTL_SMTP.CLOSE_DATA(c);
  UTL_SMTP.QUIT(c);
EXCEPTION
  WHEN OTHERS THEN
    BEGIN
      UTL_SMTP.QUIT(c);
    EXCEPTION
      WHEN OTHERS THEN NULL;
    END;
    RAISE;
END;
/

Walletのパス、証明書、TLSバージョン、SMTPサーバの認証方式は環境差が大きい部分です。クラウドSMTPではアプリパスワード、IP制限、送信元ドメイン認証、送信レート制限も確認します。

添付ファイルを送るときの考え方

添付ファイルは、サイズ、文字コード、MIME境界、Base64エンコード、メールサーバ側の上限を考慮します。小さなテキストやCSVなら UTL_MAIL で足りる場合がありますが、大きな添付や複数パートを正確に制御したい場合は UTL_SMTP で組み立てます。

utl-mail-attachment.sql
BEGIN
  UTL_MAIL.SEND_ATTACH_VARCHAR2(
    sender      => 'batch@example.com',
    recipients  => 'ops@example.com',
    subject     => '集計CSV',
    message     => '集計結果を添付します。',
    mime_type   => 'text/plain; charset=UTF-8',
    attachment  => 'id,name,amount' || CHR(10) ||
                   '1,Sample,1000',
    att_inline  => FALSE,
    att_mime_type => 'text/csv; charset=UTF-8',
    att_filename  => 'summary.csv'
  );
END;
/

CLOB/BLOBを扱う添付処理では、VARCHAR2の上限や変換バッファ不足に注意します。LOBの読み書きは DBMS_LOB完全ガイド、CLOB/BLOB変換時の ORA-22835ORA-22835の原因と解決方法 が参考になります。

ジョブ失敗通知として使う

メール送信は、単体で使うよりジョブ管理・ログ管理と組み合わせると実務価値が出ます。ジョブが失敗したら即メール、成功時は日次サマリだけ送る、一定回数失敗したら通知を抑制する、といった運用ルールを決めます。

job-failure-mail.sql
CREATE OR REPLACE PROCEDURE notify_job_error(
  p_job_name IN VARCHAR2,
  p_message  IN VARCHAR2
) AS
BEGIN
  INSERT INTO job_notice_log(job_name, message, created_at)
  VALUES (p_job_name, p_message, SYSDATE);

  UTL_MAIL.SEND(
    sender     => 'batch@example.com',
    recipients => 'ops@example.com',
    subject    => '[ERROR] ' || p_job_name,
    message    => p_message,
    mime_type  => 'text/plain; charset=UTF-8'
  );
EXCEPTION
  WHEN OTHERS THEN
    -- メール送信失敗で本処理の原因を消さないよう、別ログに残す
    INSERT INTO mail_error_log(error_message, created_at)
    VALUES (SQLERRM, SYSDATE);
END;
/

メール送信そのものが失敗することもあります。エラー通知のためのメール送信が失敗したときに本来の障害情報を失わないよう、ログテーブルへ必ず残します。実行履歴の集中管理は ジョブ実行履歴の集中管理と異常検知、デバッグ出力の基本は DBMS_OUTPUTの使い方 も役立ちます。

よくあるエラーと切り分け

ORA-24247ネットワークACL不足。ユーザー、ホスト、ポート、PDBを確認します。
ORA-29278SMTPの一時エラー。サーバ混雑、レート制限、一時的な接続失敗を疑います。
ORA-29279SMTPの永続エラー。宛先、認証、送信元制限、リレー拒否を確認します。
認証失敗ユーザー名、パスワード、認証方式、STARTTLS後のEHLO再実行を確認します。
文字化け件名、本文、MIMEタイプ、メールクライアントの表示を確認します。
添付失敗ファイルサイズ、Base64、MIME境界、VARCHAR2/LOB変換を確認します。
smtp-debug-check.sql
-- まずDB内の設定と権限を確認する
SHOW PARAMETER smtp_out_server

SELECT host, lower_port, upper_port, ace_order
FROM dba_host_aces
WHERE host = 'smtp.example.com';

-- 送信処理の例外はSQLCODE/SQLERRMだけでなく、SMTP応答もログ化する
CREATE TABLE mail_send_log (
  log_id        NUMBER GENERATED ALWAYS AS IDENTITY,
  send_to       VARCHAR2(4000),
  subject       VARCHAR2(500),
  status        VARCHAR2(30),
  error_code    NUMBER,
  error_message VARCHAR2(4000),
  created_at    DATE DEFAULT SYSDATE
);

誤送信防止の設計

本番メール送信で最も怖いのは、技術的なエラーより誤送信です。検証環境から本番宛に送る、個人情報を添付する、宛先リストを間違える、障害時に同じ通知を大量送信する、といった事故を防ぐ仕組みを入れます。

環境別宛先開発・検証では固定のテスト宛先に強制上書きします。
送信許可リスト本番でもドメインや宛先種別を許可リスト化します。
件名プレフィックス[DEV][STG][PROD] を付けます。
再送制御同じエラーの通知は一定時間まとめ、通知嵐を防ぎます。
本文マスク個人情報、トークン、SQL全文、スタックトレースの露出を避けます。
recipient-guard.sql
CREATE OR REPLACE FUNCTION safe_recipient(
  p_recipient IN VARCHAR2
) RETURN VARCHAR2
AS
BEGIN
  IF SYS_CONTEXT('USERENV', 'DB_NAME') <> 'PRODDB' THEN
    RETURN 'dev-notice@example.com';
  END IF;

  IF NOT REGEXP_LIKE(p_recipient, '^[A-Za-z0-9._%+-]+@example\.com$') THEN
    RAISE_APPLICATION_ERROR(-20030, 'recipient is not allowed');
  END IF;

  RETURN p_recipient;
END;
/

本番導入前チェックリスト

ACLconnectresolve を正しく付与しているか。
SMTPホスト、ポート、認証方式、STARTTLS要否、レート制限を確認したか。
WalletTLS証明書、パス、権限、期限切れを確認したか。
宛先環境別宛先上書き、許可ドメイン、誤送信防止があるか。
ログ成功・失敗・SMTP応答・再送回数を追跡できるか。
例外処理メール送信失敗で本処理のエラーを消していないか。
添付サイズ、文字コード、個人情報、暗号化要否を確認したか。
運用SMTP障害時の代替連絡手段と復旧手順があるか。

まとめ

PL/SQLでメール送信を自動化するなら、単純な社内通知は UTL_MAIL、認証・STARTTLS・添付・細かなヘッダー制御が必要な場合は UTL_SMTP を選ぶのが基本です。どちらを使う場合も、ネットワークACL、SMTPサーバ設定、文字コード、ログ、誤送信防止をセットで設計します。

特に本番では、ORA-24247 のACL不足、ORA-29278 / ORA-29279 のSMTP応答、STARTTLS後のEHLO再実行、Wallet証明書、宛先制御が重要です。メールは障害を知らせる最後の通路になりやすいので、送信できない場合のログと代替確認手段まで用意しておきましょう。