如何在MySQL中屏蔽正则表达式无效时的报错并返回0?
问题
我有一个运行用户提供的任意正则表达式的应用,已经配置了regexp_stack_limit和regexp_time_limit来覆盖大部分性能问题,但当正则表达式格式错误时,查询会直接抛出错误,打断现有工作流。
示例报错:
mysql> select "a" regexp "(a|b"; ERROR 3691 (HY000): Mismatched parenthesis in regular expression.
还有诸多正则相关错误,比如:
ERROR 3684 (HY000): An output string could not be zero-terminated because the length exceeds the buffer capacity. ERROR 3685 (HY000): Illegal argument to a regular expression. ERROR 3686 (HY000): Index out of bounds in regular expression search. ERROR 3687 (HY000): Internal error in the regular expression library. ERROR 3688 (HY000): Syntax error in regular expression on line %u, character %u. ERROR 3689 (HY000): Unrecognized escape sequence in regular expression. ERROR 3690 (HY000): The regular expression contains a feature that is not implemented in this library version. ERROR 3691 (HY000): Mismatched parenthesis in regular expression. ERROR 3692 (HY000): Incorrect description of a {min,max} interval. ERROR 3693 (HY000): The maximum is less than the minumum in a {min,max} interval. ERROR 3694 (HY000): Invalid back-reference in regular expression. ERROR 3695 (HY000): The look-behind assertion exceeds the limit in regular expression. ERROR 3696 (HY000): The regular expression contains an unclosed bracket expression. ERROR 3697 (HY000): The regular expression contains an [x-y] character range where x comes after y. ERROR 3698 (HY000): Overflow in the regular expression backtrack stack. ERROR 3699 (HY000): Timeout exceeded in regular expression match. ERROR 3700 (HY000): The regular expression pattern exceeds limits on size or complexity.
期望效果是:当正则模式无效时直接返回0,示例如下:
mysql> select "a" regexp "(a|b"; +--------------------+ | "a" regexp "(a|b" | +--------------------+ | 0 | +--------------------+
解决方案
1. 自定义存储函数(带错误捕获)
创建一个封装REGEXP操作的存储函数,利用MySQL的DECLARE HANDLER捕获所有正则相关错误,返回0替代报错。示例代码:
DELIMITER // CREATE FUNCTION safe_regexp_match(str VARCHAR(255), pattern VARCHAR(255)) RETURNS INT DETERMINISTIC BEGIN DECLARE EXIT HANDLER FOR SQLSTATE 'HY000' BEGIN RETURN 0; END; RETURN IF(str REGEXP pattern, 1, 0); END // DELIMITER ;
使用时直接调用这个函数即可:
SELECT safe_regexp_match("a", "(a|b");
这个函数会捕获所有HY000状态码的错误(包含你列出的所有正则相关错误),报错时返回0;匹配成功返回1,匹配失败也返回0。
2. 先验证正则模式有效性
在执行匹配前,先通过预处理语句尝试编译正则模式,捕获错误后再决定是否执行匹配。可以写一个辅助函数来做验证:
DELIMITER // CREATE FUNCTION is_valid_regex(pattern VARCHAR(255)) RETURNS INT DETERMINISTIC BEGIN DECLARE EXIT HANDLER FOR SQLSTATE 'HY000' BEGIN RETURN 0; END; -- 用空字符串触发模式验证 SET @test = '' REGEXP pattern; RETURN 1; END // DELIMITER ;
然后在查询中组合使用:
SELECT IF(is_valid_regex("(a|b"), "a" REGEXP "(a|b", 0);
这种方式先判断模式是否合法,合法则执行匹配,不合法直接返回0。
3. MySQL 8.0+ 用REGEXP_LIKE封装
如果用的是MySQL 8.0及以上版本,支持REGEXP_LIKE函数,同样可以用存储函数封装:
DELIMITER // CREATE FUNCTION safe_regexp_like(str VARCHAR(255), pattern VARCHAR(255)) RETURNS INT DETERMINISTIC BEGIN DECLARE EXIT HANDLER FOR SQLSTATE 'HY000' BEGIN RETURN 0; END; RETURN IF(REGEXP_LIKE(str, pattern), 1, 0); END // DELIMITER ;
效果和第一个函数一致,适配新版MySQL的正则函数。
注意事项
- 存储函数的参数长度可以根据实际需求调整(比如改成
TEXT类型支持更长的字符串和正则)。 regexp_time_limit超时的错误(ERROR 3699)也会被上述方法捕获,因为它属于HY000状态码。- 创建自定义函数需要对应的权限,执行时确保用户有
EXECUTE权限。
内容的提问来源于stack exchange,提问作者Alain
相关产品推荐
相关产品推荐

