Oracle 12c+已移除DBMS_MAIL,须改用UTL_MAIL;需手动安装utlmail.sql和prvtmail.plb脚本,配置smtp_out_server参数(含端口),显式指定mime_type字符集,设置ACL权限并确保网络连通。
oracle 12c+ 中 dbms_mail 已彻底移除,调用直接报 ora-00904: "dbms_mail": invalid identifier;必须改用 utl_mail(推荐)或 utl_smtp(需手动处理协议细节)。
UTL_MAIL 包是否已安装?检查并安装的实操步骤
默认情况下,UTL_MAIL 不在 Oracle 数据库中预装。即使你有 DBA 权限,也得手动运行两个脚本:
- 以
SYS身份连接:sqlplus / as sysdba - 执行安装脚本(路径可能因版本/安装方式微调,但标准位置是
$ORACLE_HOME/rdbms/admin/):@?/rdbms/admin/utlmail.sql@?/rdbms/admin/prvtmail.plb - 安装后验证:运行
SELECT object_name FROM dba_objects WHERE object_name = 'UTL_MAIL' AND owner = 'SYS' AND status = 'VALID';,返回一行即成功
注意:prvtmail.plb 是包体(package body),漏掉它会导致 UTL_MAIL.SEND 调用时报 PLS-00905: object SYS.UTL_MAIL is invalid —— 这不是权限问题,是包体没加载。
smtp_out_server 参数必须显式设置且带端口
UTL_MAIL 不会自动找邮件服务器,它完全依赖数据库参数 smtp_out_server。不设或设错,调用 SEND 时大概率报 ORA-29260: network error: TNS:connection refused(这个错误名极具误导性,实际是连不上 SMTP)。
- 必须包含端口号,例如:
ALTER SYSTEM SET smtp_out_server = 'smtp.gmail.com:587' SCOPE=BOTH; - 不能加
http://或https://前缀,也不能写成域名不带端口(如'smtp.gmail.com') - 检查是否生效:
SELECT value FROM v$parameter WHERE name = 'smtp_out_server';,返回非空字符串才算配置成功 - Oracle 11gR2+ 支持
SCOPE=BOTH动态生效;10g R1 等旧版本需SCOPE=SPFILE并重启实例
UTL_MAIL.SEND 调用时中文乱码和长度限制
看似简单的 SEND 调用,中文主题或正文变问号、超长内容静默失败,几乎都源于这两个点:
-
mime_type参数必须显式指定字符集,例如:mime_type => 'text/plain; charset=utf-8'或'text/html; charset=utf-8';不传或传默认值'text/plain; charset=us-ascii',数据库会按自身字符集(如WE8ISO8859P1)编码,中文直接损坏 -
subject和message参数上限为 32767 字节(不是字符数),超长会触发ORA-29260错误(再次强调:这不是网络故障,是参数越界) - 多个收件人必须用英文逗号拼成单个字符串:
recipients => 'a@x.com,b@y.com';传数组、集合或分号分隔都会报ORA-06502: PL/SQL: numeric or value error
最小可用示例(可直接执行):
BEGIN
UTL_MAIL.SEND(
sender => 'noreply@company.com',
recipients => 'user@company.com',
subject => '测试邮件',
message => '这是一封含中文的测试邮件',
mime_type => 'text/plain; charset=utf-8'
);
END;
ACL 权限和防火墙:常被忽略的“最后一公里”
即使包装好了、参数写对了、SMTP 地址也通,仍可能失败——因为 Oracle 12c+ 强制启用网络访问控制列表(ACL)。没有 ACL 授权,UTL_MAIL 会在底层调用 UTL_TCP 时被拦截,报 ORA-01031: insufficient privileges 或 ORA-24247: network access denied by access control list (ACL)。
- 需由 DBA 执行 ACL 创建与授权(不能跳过):
BEGIN DBMS_NETWORK_ACL_ADMIN.create_acl(acl => 'mail_acl.xml', description => 'Mail server access', principal => 'YOUR_SCHEMA', is_grant => TRUE, privilege => 'connect'); END;BEGIN DBMS_NETWORK_ACL_ADMIN.assign_acl(acl => 'mail_acl.xml', host => 'smtp.company.com'); END; - 数据库服务器本身要能直连目标 SMTP 服务器的对应端口(如 587/465/25),和你的开发机或应用服务器能否发信完全无关
- 若用公共邮箱(如 Gmail、163),务必确认其 SMTP 是否允许未加密连接、是否要求 OAuth2 或 App Password ——
UTL_MAIL不支持 OAuth2,只能走传统密码认证(且很多服务商已禁用)
真正卡住人的,往往不是语法,而是 ACL 没配、端口被墙、或 SMTP 服务端拒绝了来自数据库 IP 的连接请求 —— 这些必须逐层验证,不能只盯着 PL/SQL 代码看。











