一聚教程网:一个值得你收藏的教程网站

热门教程

如何在Oracle PL/SQL中发送邮件?

时间:2026-08-06 07:47:48 编辑:袖梨 来源:一聚教程网

Oracle 12c+中DBMS_MAIL已彻底删除,必须改用UTL_MAIL或UTL_SMTP;启用UTL_MAIL需三步:手动安装utlmail.sql与prvtmail.plb脚本、显式设置带端口的smtp_out_server参数、确保DB服务器直连SMTP端口且配置ACL权限。

Oracle 12c 及更高版本中 DBMS_MAIL 已被彻底删除,调用必报 ORA-00904: "dbms_mail": invalid identifier ——这不是权限或安装问题,是包本身不存在了。必须改用 UTL_MAILUTL_SMTP

UTL_MAIL 启用失败的三个硬性前提

看起来一行 UTL_MAIL.SEND 就能发信,但 80% 的失败卡在这三步没走完:

  1. DBA 必须手动执行安装脚本:@?/rdbms/admin/utlmail.sql@?/rdbms/admin/prvtmail.plb(路径中 ? 展开为 $ORACLE_HOME
  2. smtp_out_server 参数必须设且带端口:ALTER SYSTEM SET smtp_out_server = 'smtp.example.com:587' SCOPE=BOTH; ——不能写 http://,也不能漏端口
  3. 数据库服务器自身需能直连邮件服务器:防火墙、SELinux、网络策略必须放行出站 587465 端口(不是应用服务器能连就行)

验证是否生效:SELECT value FROM v$parameter WHERE name = 'smtp_out_server'; 返回非空字符串才算通过。

UTL_MAIL.SEND 中文乱码与长度陷阱

主题或正文变问号?不是字符集没配对,就是超长触发误导性错误:

  1. 必须显式指定 mime_type => 'text/plain; charset=utf-8'(HTML 邮件同理),否则默认用数据库字符集(如 WE8ISO8859P1),中文直接报废
  2. subjectmessage 参数上限为 32767 字节 —— 超长会抛 ORA-29260: network error: TNS:connection refused,实际和网络无关,纯长度校验失败
  3. 多个收件人必须拼成单字符串:'[email protected],[email protected]',不能传数组、集合或换行符

最小可用示例:

BEGINUTL_MAIL.SEND(sender => '[email protected]',recipients => '[email protected]',subject=> '测试',message=> '这是一封测试邮件',mime_type=> 'text/plain; charset=utf-8');END;

UTL_SMTP 连接卡死在 STARTTLS 的关键动作

当邮件服务器强制要求 STARTTLS(如 Gmail、Outlook、多数企业 SMTP),UTL_SMTP.OPEN_CONNECTION 后直接发 MAIL 会挂起或报 ORA-29279

  1. 必须先发 EHLO(不是 HELO),再发 STARTTLS 命令
  2. 发完 STARTTLS 后,需重新协商 TLS 层 —— Oracle 不自动处理,得手动调用 UTL_SMTP.STARTTLS(12.2+)或用 UTL_TCP 手动包装 SSL 握手(旧版本)
  3. 认证用户名/密码必须 Base64 编码:UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(v_user)),漏掉任意一环都会 535 错误
  4. 邮件头与正文之间必须有且仅有一个空行(UTL_TCP.CRLF || UTL_TCP.CRLF),少或多都会被拒

中文支持依赖字符集转换:UTL_RAW.cast_to_raw(CONVERT(v_msg, 'ZHS16GBK', 'AL32UTF8')),不能只靠 mime_type 声明。

ACL 权限不是可选项,而是前置开关

即使 UTL_MAILUTL_SMTP 配置全对,没 ACL 就是白搭 —— 报错 ORA-24247: network access denied by access control list

  1. ACL 文件名、主机名、端口范围必须严格匹配:DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(acl => 'email.xml', host => '*', lower_port => 25, upper_port => 587)
  2. 用户必须被明确授予 connect 权限:DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE('email.xml', 'MY_USER', TRUE, 'connect')
  3. ACL 生效后,仍需显式授权包执行权:GRANT EXECUTE ON UTL_MAIL TO MY_USERUTL_SMTP 同理)

真正容易被忽略的是:ACL 主机匹配支持通配符但不支持正则;host => '*' 允许所有域名/IP,但若只配了 smtp.example.com,连 smtp.example.com:587 都会被拒绝 —— 端口必须单独放开。

热门栏目