Postgres函数用于验证电子邮件地址。

12

我的一个调用函数验证电子邮件地址的检查约束对我来说不起作用。

CREATE OR REPLACE FUNCTION f_IsValidEmail(text) returns BOOLEAN AS 
'select $1 ~ ''^[^@\s]+@[^@\s]+(\.[^@\s]+)+$'' as result
' LANGUAGE sql;



SELECT f_IsValidEmail('myemail@address.com');

这个函数返回了false,但应该是true。我已经尝试了一些其他的正则表达式,但都失败了。有人能指出这个函数哪里有问题吗?

截图


你的问题是什么?我在你的代码中没有看到任何检查约束。 - user330315
更具体地说,我只发布了不起作用的函数。 - Nick Binnet
那么究竟是什么“不起作用”?你是否收到了错误提示?检查约束条件是什么样子的? - user330315
忘掉检查约束吧。这个函数没有返回正确的结果。正如你所看到的,SELECT "Policies"."f_IsValidEmail"('myemail@address.com'); 返回了 false,但实际上应该是 true。 :) - Nick Binnet
当我尝试你的示例时,它返回true。 - user330315
无论我传递什么,它都返回“f”表示假。我正在Windows 7上使用pgAdmin III。 :-( - Nick Binnet
7个回答

22

这些答案中有很多都接近正确,以下是我的建议:

  • 你需要使用域名,而不是规则系统
  • 不需要用正则表达式验证这些电子邮件地址。(2017年3月更新:这个说法并非完全正确)

我在DBA.StackExchange.com上展示了两种方法如何正确地执行此操作,分别是检查MX记录和使用HTML5规范。以下是简短明了的内容。

CREATE EXTENSION citext;
CREATE DOMAIN email AS citext
  CHECK ( value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$' );

SELECT 'foobar@bar.com'::email;
SELECT CAST('foobar@bar.com' AS email);

更多信息,我强烈建议全文阅读答案。在答案中,我还展示了如何创建一个基于Email::ValidDOMAIN,并解释了我为什么不再使用那种方法。


请记住,在不受信任的语言上创建函数需要成为PostgreSQL超级用户。 - Neoecos
谢谢。它似乎允许"foo@bar"作为有效的电子邮件地址(即,不检查FQDN中是否至少有一个点)。虽然,我想在某些情况下,如果将“bar”替换为“localhost”,那也是可以的。 - JJC

14
在你付出很多努力之前,你需要确保你不会排除掉有效的电子邮件地址。有各种疯狂的规则来确定电子邮件地址中可以或不能包含哪些内容,如果你在这方面做错了,一个完全有效的电子邮件地址的用户可能会被你的系统拒绝。
确定电子邮件地址是否有效的最佳方法是将其作为注册过程的一部分,其中电子邮件是必需的。其他任何方式都是为了小小的收益而带来大量工作。

4
我不同意这个答案。对电子邮件地址进行硬核验证是最全面的电子邮件验证方法,但也是最不切实际的。你需要的是一个自定义的Postgres域名,它不会排除任何一个有效的电子邮件地址。然后你只需要验证那些通过了这个非常低的标准的电子邮件地址。这使你能够丢弃立即的垃圾邮件,并请求用户澄清,而不会向你的邮件服务器轰炸明显的垃圾邮件。演示:https://dev59.com/SG445IYBdhLWcg3wWY8U#22671557 - Evan Carroll
嗯,我明白你的观点,但我认为最好确保有某种速率控制机制,以防止有人向您发送真实或虚假地址的大量请求。之后,无论您是否向电子邮件服务器发送垃圾邮件都不是什么大问题,因为每小时几个错误的电子邮件地址并不值得太担心,并且在我的设备上安装“plperlu”也不值得。 - Scott Marlowe

5
我建议使用PL/Perl和Email::Address模块来解决问题。以下是一个示例:
CREATE OR REPLACE FUNCTION email_valid(email text) RETURNS bool
LANGUAGE plperlu
AS $$
use Email::Address;
my @addresses = Email::Address->parse($_[0]);
return scalar(@addresses) > 0 ? 1 : 0;
$$;

请参阅http://wiki.postgresql.org/wiki/Email_address_parsing,该网页与电子邮件地址解析有关。

你了解关于perl、plpgsql和sql函数性能方面的一些内容吗?假设只需要对值进行正则表达式匹配。 - Daniel
1
您可以使用您的特定数据和所选择的实现来轻松测试自己。 - Peter Eisentraut
谢谢你的帮助,彼得,但我没有perl。你们能找出这个函数有什么问题吗? - Nick Binnet

2

不要试图创建用于验证电子邮件的正则表达式!

这是非常困难的。以下是更好的解决方案:

假设您的数据库主机已安装Perl,请使用CPAN在同一主机上安装Email::Valid模块:

you@localhost$ cpan Email::Valid

请确保您已安装PL/Perl。在psql中连接到您的数据库,并将plperlu添加为一种语言:

CREATE EXTENSION plperlu;

请记住,这是一种不受信任的语言,因此您将为您的数据库提供直接文件访问权限,如果有人在您的Perl模块或数据库函数中插入恶意代码,则可能存在安全风险。但是,您需要执行此操作以进行下一步操作。

将以下函数添加到您的数据库中:

CREATE FUNCTION validate_email() RETURNS trigger AS $$
  use Email::Valid;
  return if Email::Valid->address($_TD->{new}{email});
  elog(ERROR, "invalid email address $_TD->{new}{email} inserted into $_TD->{table_name}(email)");
  return "SKIP";
$$ LANGUAGE plperlu;

在您的表上为您的列添加触发约束(假设您的表名为“users”,您的列名为“email”):
CREATE TRIGGER users_valid_email_trig
  BEFORE INSERT OR UPDATE ON users
  FOR EACH ROW EXECUTE PROCEDURE validate_email();

完成了!

这种解决方案使用Email::Valid Perl模块来处理验证,该模块又依赖于一个正则表达式来确保RFC 822的兼容性。但是,它是一个庞大的正则表达式,所以不要尝试自己编写。

如果您不喜欢启用plperlu而不是普通的plperl,您可能可以将相关函数移植到您的数据库中。


这绝不是一个好主意。不要使用触发器,使用域。 - Evan Carroll
4
因为该触发器将在每次行更新时应用,而不管该字段是否已被修改。此外,该类型的元数据缺失。如果要扩展到另一个表,则需要维护更多的触发器。 - Evan Carroll

2
如果你能够想出一个符合你要求的匹配电子邮件地址的正则表达式,考虑一下它是否作为域名而不是检查约束更有用。关于一些陷阱,请参见regular-expressions.info

域是烘焙蛋糕的更好理念! - Nick Binnet

0
  • 领域 email.sql - 基本的最小验证,但速度快。因为验证将在每次 UPDATE 查询时 触发, 即使字段的值没有改变!
  • 函数 is_email.sql - 验证几乎按照规范,但速度较慢。

0

对我有用:

psql(9.0.2)
键入“help”获取帮助。

postgres => CREATE OR REPLACE FUNCTION“f_IsValidEmail”(text)RETURNS BOOLEAN AS
postgres -> '选择$1〜'' ^ [^ @ \ s] + @ [^ @ \ s] +(\。[^ @ \ s] +)+ $ ''作为结果
postgres' - > 'LANGUAGE sql;
创造功能
postgres => commit;
提交
postgres => SELECT“f_IsValidEmail”('myemail@address.com');
 f_IsValidEmail
----------------
t
(1行)

postgres =>

这对我不起作用。我正在使用脚本在pgAdmin III上的新查询窗口中。 - Nick Binnet
在pgAdmin中对我有效,你试过psql了吗? - user330315
我已经上传了一个pgAdmin新查询的截图。您能否请看一下,看看那里是否有什么问题? - Nick Binnet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接