You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle数据库邮件未触发排查及ORA-29278错误求助

排查Oracle数据库端邮件未触发问题及日志追踪方法

一、通用排查步骤

  • 验证SMTP配置正确性:检查WS_PMNT_CONFIG表中MAIL_HOST和MAIL_PORT的配置值是否与目标环境SMTP服务器一致,确认无拼写错误或端口不匹配。
  • 测试网络连通性:从Oracle数据库服务器执行telnet <MAIL_HOST> <MAIL_PORT>命令,验证是否能正常连接SMTP服务,排查防火墙或网络策略是否阻止访问。
  • 确认数据库权限:确保执行存储过程的用户拥有UTL_SMTP、UTL_TCP、UTL_RAW等包的执行权限,以及访问WS_PMNT_CONFIG、APIPMT_MAIL_RECEPIENTS等相关表的权限。
  • 检查存储过程逻辑:
    • 确认APIPMT_MAIL_RECEPIENTS表中存在有效的发件人(FROM_TO='F')和收件人(FROM_TO='T')邮箱地址。
    • 验证游标REJ和SUCC是否能查询到数据,确认邮件内容及附件生成逻辑正常。
    • 检查PKG_SEND_MAIL.SEND_MAIL调用参数是否完整,特别是SMTP主机、端口参数是否正确传递。

二、数据库日志追踪方法

  • 自定义错误日志:当前代码仅通过DBMS_OUTPUT输出错误,建议新增自定义日志表(如MAIL_ERROR_LOG),将错误时间、错误信息、执行参数等写入表中,便于后续排查。
  • 数据库告警日志:查看Oracle数据库告警日志(alert.log),排查是否存在与UTL_SMTP相关的权限错误、网络异常或资源限制信息。
  • SMTP服务器日志:联系邮件管理员查看SMTP服务器日志,确认数据库服务器的连接请求是否被拒绝或触发限流策略,这对定位SMTP层错误至关重要。

三、针对ORA-29278: SMTP transient error: 421 Service not available的具体排查

该错误表示SMTP服务器返回临时不可用响应,常见原因及解决方法:

  • SMTP服务器不可达:数据库服务器无法连接配置的SMTP主机/端口,需重新确认网络连通性、防火墙规则及SMTP服务器运行状态。
  • SMTP服务器限流或拒绝未授权连接:部分SMTP服务器会对陌生IP或频繁请求限流,需确认数据库服务器IP是否在SMTP白名单中;若SMTP要求身份验证,当前代码未实现UTL_SMTP.AUTH逻辑,需补充认证步骤。
  • 环境配置不一致:目标环境SMTP服务器地址、端口或认证方式与测试环境不同,需重新核对WS_PMNT_CONFIG中的配置值。

相关代码参考

存储过程PMNT_API_MAILER

create or replace PROCEDURE PMNT_API_MAILER(PMNT_DATE IN DATE)

AS

BEGIN

DECLARE

strMailHost   VARCHAR2(50);

strMailPort   VARCHAR2(4);

strHeader    VARCHAR2(4000);

strMessage   VARCHAR2(4000);

nPos      NUMBER;

strFrom     VARCHAR2(100);

strSendTo    VARCHAR2(1000);

strCCTo     VARCHAR2(1000);

strSubject   VARCHAR2(500);

strSendTo_BAF  VARCHAR2(1000);

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



P_TEXT     clob;--VARCHAR2(4000);

P_HTML     clob;

V_SUM_LOAN_PRIN_ADV  NUMBER := 0;

V_Count     Number;

V_BusinessDate VARCHAR(20);

v_error varchar2(1000);

P_DATE     DATE := PMNT_DATE;

v_cnt_cu    number := 0;

attachments PKG_SEND_MAIL.ARRAY_ATTACHMENTS := PKG_SEND_MAIL.ARRAY_ATTACHMENTS();

V_REJECT_DATA CLOB;

V_SUCCESS_DATA CLOB;



CURSOR REJ IS

  SELECT a.agreementid,a.downloadid,b.chequeid,b.BANK_REJ_REASON

  FROM lea_h2hpayment_hdr a, lea_h2hpayment_dtl b

  where a.downloadid = b.downloadid

  and a.download_date = P_DATE

  and a.pmnt_method = 'API'

  and a.status = 'A'

  and b.BANK_REJ_REASON is not null;



CURSOR SUCC IS

  SELECT a.agreementid,a.downloadid,b.chequeid,b.BANK_TXN_REF_NO

  FROM lea_h2hpayment_hdr a, lea_h2hpayment_dtl b

  where a.downloadid = b.downloadid

  and a.download_date = P_DATE

  and a.pmnt_method = 'API'

  and a.status = 'A'

  and b.BANK_TXN_REF_NO is not null;





 BEGIN

 v_error :=null;

  
BEGIN



 select CONF_VALUE

 into strMailHost

 from WS_PMNT_CONFIG

 WHERE CONF_KEY = 'MAIL_HOST'

 and status = 'A';



  select CONF_VALUE

 into strMailPort

 from WS_PMNT_CONFIG

 WHERE CONF_KEY = 'MAIL_PORT'

 and status = 'A';



  EXCEPTION WHEN OTHERS THEN

  v_error := 'Error while fetching data from WS_PMNT_CONFIG' || sqlerrm;

END;



BEGIN

 SELECT EMAIL

 INTO strFrom

 FROM APIPMT_MAIL_RECEPIENTS

 WHERE FROM_TO ='F'

 AND ROWNUM = 1;



  EXCEPTION WHEN OTHERS THEN

  v_error := 'Error while fetching sender mail from APIPMT_MAIL_RECEPIENTS' || sqlerrm;

  return;

END;



strHeader  := 'Date: ' || TO_CHAR(SYSDATE,'dd Mon yy hh24:mi:ss') ;

strSubject := 'Payment records processed via API on ' || to_char(P_DATE,'dd-MON-yyyy');



P_HTML := '&lt;HTML&gt;&lt;BODY bgcolor=&quot;white&quot;&gt;

      &lt;TABLE BORDER=0&gt;

      &lt;font color=&quot;black&quot; face=&quot;Tahoma&quot; SIZE=&quot;2&quot; point-size=100 weight=600&gt;

      &lt;TR&gt;&lt;TD colspan=&quot;2&quot;&gt;Dear Team,

      &lt;BR&gt;

      &lt;BR&gt;

      &lt;/TD&gt;&lt;/TR&gt;

      &lt;TR&gt;&lt;TD colspan=&quot;2&quot;&gt;Please find below information related to Payment Disbursement :&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;

       &lt;TR&gt;&lt;/TR&gt;

       &lt;BR&gt;

      &lt;TR&gt;&lt;TD colspan=&quot;2&quot;&gt;1. Success file&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;

      &lt;BR&gt;

      &lt;TR&gt;&lt;TD colspan=&quot;2&quot;&gt;2. Reject file&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;

      &lt;/font&gt;&lt;/TABLE&gt;';



P_TEXT := '';



P_TEXT := '&lt;BR&gt;';



P_HTML := P_HTML || P_TEXT;



P_TEXT := '';



P_TEXT := '&lt;TABLE BORDER=0&gt;

      &lt;font color=&quot;black&quot; face=&quot;Tahoma&quot; SIZE=&quot;2&quot; point-size=100 weight=600&gt;

      &lt;TR&gt;&lt;TD colspan=&quot;2&quot;&gt;Thanks &amp; Regards&lt;/TD&gt;&lt;/TR&gt;

      &lt;TR&gt;&lt;TD colspan=&quot;2&quot;&gt;IT Team&lt;/TD&gt;&lt;/TR&gt;&lt;/font&gt;&lt;/TABLE&gt;';



P_HTML := P_HTML || P_TEXT ;



P_HTML := P_HTML || '&lt;/BODY&gt;&lt;/HTML&gt;';





SELECT LISTAgg(EMAIL,',') WITHIN GROUP (ORDER BY EMAIL)

into strSendTo

FROM APIPMT_MAIL_RECEPIENTS

where status = 'A'

and from_to = 'T';



V_SUCCESS_DATA := 'LAN ID,CHQ ID,BATCH ID,UTR'||chr(13)||chr(10);

FOR S IN SUCC LOOP

  V_SUCCESS_DATA := V_SUCCESS_DATA ||

           S.AGREEMENTID||','||S.CHEQUEID||','||S.DOWNLOADID||','||S.BANK_TXN_REF_NO||chr(13)||chr(10);

END LOOP;

V_REJECT_DATA := 'LAN ID,CHQ ID,BATCH ID,Reject Reason'||chr(13)||chr(10);

FOR R IN REJ LOOP

  V_REJECT_DATA := V_REJECT_DATA ||

           R.AGREEMENTID||','||R.CHEQUEID||','||R.DOWNLOADID||','||R.BANK_REJ_REASON||chr(13)||chr(10);

END LOOP;



attachments.extend(2);



attachments(1).attach_name := 'Reject_'||PMNT_DATE||'.csv';

attachments(1).data_type := 'text/plain';

attachments(1).attach_content := V_REJECT_DATA;



attachments(2).attach_name := 'Success_'||PMNT_DATE||'.csv';

attachments(2).data_type := 'text/plain';

attachments(2).attach_content := V_SUCCESS_DATA;



PKG_SEND_MAIL.SEND_MAIL ( strFrom,strSendTo,strSubject,P_HTML,strCCTo,attachments ,'text/html', strMailHost, strMailPort);



trap('End SEND_MAIL');



  EXCEPTION



  WHEN UTL_SMTP.INVALID_OPERATION THEN

  v_error:='Invalid Operation in SMTP transaction.'||SQLERRM;



WHEN UTL_SMTP.TRANSIENT_ERROR THEN

  v_error:='Temporary problems in sending email - try again later.'||SQLERRM;



WHEN UTL_SMTP.PERMANENT_ERROR THEN

  v_error:='Errors in code for SMTP transaction.'||SQLERRM;



WHEN OTHERS THEN

  v_error:=' Error Occured while sending Mail.'||SQLERRM;

   

   DBMS_OUTPUT.put_line(v_error);

 END;



 END;

包体PKG_SEND_MAIL

create or replace PACKAGE BODY PKG_SEND_MAIL AS



PROCEDURE SEND_MAIL(

v_from_name  VARCHAR2,

v_to_name   VARCHAR2,

v_subject   VARCHAR2,

v_message_body VARCHAR2,

v_cc_name   VARCHAR2 DEFAULT '',

attachments  array_attachments DEFAULT NULL,

v_message_type VARCHAR2 DEFAULT 'text/plain',

v_smtp_ser  varchar2 ,

v_smtp_port   varchar2

) AS

 v_smtp_server    VARCHAR2(20) := v_smtp_ser;

 n_smtp_server_port NUMBER    := v_smtp_port;

 conn        utl_smtp.connection;

 v_boundry      VARCHAR2(20) := 'SECBOUND';

 n_offset      NUMBER    := 0;

 n_amount      NUMBER    := 1900;

 v_final_to_name   CLOB     := '';

 v_final_cc_name   CLOB     := '';

 v_mail_address   VARCHAR2(100);

 v_error_msg     varchar2(4000 byte);



  BEGIN

  conn := utl_smtp.open_connection(v_smtp_server,n_smtp_server_port);

   utl_smtp.helo(conn, v_smtp_server);

   utl_smtp.mail(conn, v_from_name);



  -- Add all recipient

  v_final_to_name := v_to_name;

  v_final_to_name := replace(v_final_to_name, ' ');

   v_final_to_name := replace(v_final_to_name, ',', ';');

  LOOP

   n_offset := n_offset + 1;

  v_mail_address := regexp_substr(v_final_to_name, '[^;]+', 1, n_offset);

   EXIT WHEN v_mail_address IS NULL;

  utl_smtp.rcpt(conn, v_mail_address);

  END LOOP;



  -- Add all recipient

  v_final_cc_name := v_cc_name;

   v_final_cc_name := replace(v_final_cc_name, ' ');

   v_final_cc_name := replace(v_final_cc_name, ',', ';');

   n_offset := 0;

    LOOP

    n_offset := n_offset + 1;

      v_mail_address := regexp_substr(v_final_cc_name, '[^;]+', 1, n_offset);

      EXIT WHEN v_mail_address IS NULL;

      utl_smtp.rcpt(conn, v_mail_address);

      END LOOP;



        -- Open data

       utl_smtp.open_data(conn);



        -- Message info

         utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('To: ' || v_final_to_name || UTL_TCP.crlf));

          utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Cc: ' || v_final_cc_name || UTL_TCP.crlf));

            utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || UTL_TCP.crlf));

            utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('From: ' || v_from_name || UTL_TCP.crlf));

             utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Subject: ' || v_subject || UTL_TCP.crlf));

             utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('MIME-Version: 1.0' || UTL_TCP.crlf));

             utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Content-Type: multipart/mixed; boundary=&quot;' || v_boundry || '&quot;' || UTL_TCP.crlf || UTL_TCP.crlf));



              -- Message body

              utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('--' || v_boundry || UTL_TCP.crlf));

               utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Content-Type: ' || v_message_type || UTL_TCP.crlf || UTL_TCP.crlf));

               utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw(v_message_body || UTL_TCP.crlf));



                -- Attachment Part

               IF attachments IS NOT NULL

                THEN

                 FOR i IN attachments.FIRST .. attachments.LAST

                 LOOP

                -- Attach info

                  utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('--' || v_boundry || UTL_TCP.crlf));

  utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Content-Type: ' || attachments(i).data_type

            || ' name=&quot;'|| attachments(i).attach_name || '&quot;' || UTL_TCP.crlf));

  utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('Content-Disposition: attachment; filename=&quot;'

            || attachments(i).attach_name || '&quot;' || UTL_TCP.crlf || UTL_TCP.crlf));



-- Attach body

  n_offset := 1;

  WHILE n_offset &lt; dbms_lob.getlength(attachments(i).attach_content)

  LOOP

    utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw(dbms_lob.substr(attachments(i).attach_content, n_amount, n_offset)));

    n_offset := n_offset + n_amount;

  END LOOP;

  utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('' || UTL_TCP.crlf));

        END LOOP;

         END IF;

        -- Last boundry

        utl_smtp.write_raw_data(conn, utl_raw.cast_to_raw('--' || v_boundry || '--' || UTL_TCP.crlf));



          -- Close data

           utl_smtp.close_data(conn);

            utl_smtp.quit(conn);



           EXCEPTION

            WHEN OTHERS THEN

             v_error_msg := 'Error in PKG_SEND_MAIL. '||sqlerrm;

              DBMS_OUTPUT.PUT_LINE(v_error_msg);



                END;



                END;

内容的提问来源于stack exchange,提问作者Kishore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 08:20:26