【openGauss】正则表达式次数符号“{}“在ORACLE和openGauss中的差异

一、前言

正则作为一种常用的字符串处理方式,在各种开发语言,甚至数据库中,都有自带的正则函数。但是正则函数有很多标准,不同标准对正则表达式的解析方式不一样,本次在迁移一个ORACLE数据库到openGauss时发现了一个关于 {}的差异点。

二、{}是做什么用的

在绝大部分的正则表达式规则中 {}表示对前面字符的重复次数,支持的形式为 {m}{m,}{,n}{m,n},其中m和n均为自然数,例如

表达式说明
b{1}匹配1次b
b{2,}匹配2次到无穷次b
b{,3}匹配0次到3次b
b{2,3}匹配2次到3次b

三、{}的使用歧义

以下三条SQL均可以在ORACLE中执行

--匹配一个 $符号,此时 {}里的1表示 $的出现次数
select regexp_substr('aaaa${1}bbb','(\${1})') from dual;--匹配${0个或任意个数的1},此时{}以及{}内的字符按照字符串识别
select regexp_substr('aaaa${1}bbb','(\${1*})') from dual;--匹配 ${一个空格加上0个或任意个数的1} ,此时{}以及{}内的字符按照字符串识别
select regexp_substr('aaaa${ }bbb','(\${ 1*})') from dual;

这里的规则在ORACLE中大概可以这么描述:
{}内如果不满足 {m}{m,}{,n}{m,n}这四者之一的格式,则 {}不作为次数的声明符号,而是作为常规字符串进行识别。

但是上面第二个表达式在openGaussDB中会报错,因为这里还有一个规则:
如果 {}内的第一个字符是数字,则开始进入次数的解析逻辑,若解析不符合次数的规则,就报错。
查看openGauss源码,发现这段逻辑来自1998年的PG源码,数十年来未曾变过。
这里注意,此处并非BUG,只是正则标准不一致,我使用了7种开发语言来验证,发现JAVA和RUST中也同样是报错的,而PHP/JS/PYTHON/.NET/GO 中都不报错。

image-ssmg.png

image-woln.png

可以使用以下链接测试该正则表达式在不同开发语言中的表现
https://regex101.com/r/APc3is/1

四、相关源码

使用openGauss分析这个逻辑的时候,我断了几个点,找了几段源码

6       breakpoint     keep y   0x0000000000fc4cd7 in parseqatom(vars*, int, int, state*, state*, subre*) at regcomp.cpp:915breakpoint already hit 2 times
7       breakpoint     keep y   0x0000000000fc42c4 in parsebranch(vars*, int, int, state*, state*, int) at regcomp.cpp:719breakpoint already hit 2 times
8       breakpoint     keep y   0x0000000000fc5040 in parseqatom(vars*, int, int, state*, state*, subre*) at regcomp.cpp:965
9       breakpoint     keep y   0x0000000000fc510c in parseqatom(vars*, int, int, state*, state*, subre*) at regcomp.cpp:984regc_lex.cpp, line 412.
regcomp.cpp, line 966.

当第一个字符是数字,而第二个不是期望的字符(0-9以及",“和”}"),就走到default报错

case '{':NEXT();m = scannum(v); //扫描数字
static int scannum(struct vars* v)
{int n = 0;while (SEE(DIGIT) && n < DUPMAX) {n = n * 10 + v->nextvalue;NEXT();}if (SEE(DIGIT) || n > DUPMAX) {ERR(REG_BADBR);return 0;}return n;
}
case L_EBND:switch (c) {case CHR('0'):case CHR('1'):case CHR('2'):case CHR('3'):case CHR('4'):case CHR('5'):case CHR('6'):case CHR('7'):case CHR('8'):case CHR('9'):RETV(DIGIT, (chr)DIGITVAL(c)); // {1*} 会在处理1的时候走到这里break;case CHR(','):RET(',');break;case CHR('}'): /* ERE bound ends with } */if (INCON(L_EBND)) {INTOCON(L_ERE);if ((v->cflags & REG_ADVF) && NEXT1('?')) {v->now++;NOTE(REG_UNONPOSIX);RETV('}', 0);}RETV('}', 1);} elseFAILW(REG_BADBR);break;case CHR('\\'): /* BRE bound ends with \} */if (INCON(L_BBND) && NEXT1('}')) {v->now++;INTOCON(L_BRE);RET('}');} elseFAILW(REG_BADBR);break;default:FAILW(REG_BADBR); // {1*} 会在处理*的时候走到这里break;}

有兴趣的可以自己下载源码去调试分析一下,这里我就不详细解读源码了。

五、其他国产数据库对{}的处理

DM8和YASHAN和ORACLE保持一致,能在 {}内不为次数时正确当成字符串;而其他几款基于PG、OG的数据库以及纯自研的OCEANBASE在这种情况下都会报错(mysql系不报错,但执行返回空)。

  • DM 8
SQL> select regexp_substr('aaaa${1}bbb','(\${1*})') ;LINEID     REGEXP_SUBSTR('aaaa${1}bbb','(\${1*})')
---------- ---------------------------------------
1          ${1}
  • YASHAN 23
SQL> select regexp_substr('aaaa${1}bbb','(\${1*})') from dual;REGEXP_SUBSTR('AAAA$
--------------------
${1}
  • KINGBASE 9
kingbase=# select regexp_substr('aaaa${1}bbb','(\${1*})') ;
kingbase-# /
ERROR:  invalid regular expression: invalid repetition count(s)
  • HIGHGO 6
highgo=# select regexp_substr('aaaa${1}bbb','(\${1*})') ;
ERROR:  invalid regular expression: invalid
  • GAUSSDB 503
gaussdb=# select regexp_substr('aaaa${1}bbb','(\${1*})') ;
ERROR:  invalid regular expression: invalid repetition count(s)
CONTEXT:  referenced column: regexp_substr
  • OPENGAUSS 6.0
openGauss=# select regexp_substr('aaaa${1}bbb','(\${1*})') ;
ERROR:  invalid regular expression: invalid repetition count(s)
CONTEXT:  referenced column: regexp_substr
  • GBASE 8c
postgres=# select regexp_substr('aaaa${1}bbb','(\${1*})') ;
ERROR:  invalid regular expression: invalid repetition count(s)
CONTEXT:  referenced column: regexp_substr
  • VASTBASE v2.2 build 16
postgres=# select regexp_substr('aaaa${1}bbb','(\${1*})') ;
ERROR:  invalid regular expression: invalid repetition count(s)
CONTEXT:  referenced column: regexp_substr
  • OCEANBASE 4.3
执行以下 SQL 失败
select regexp_substr('aaaa${1}bbb','(\${1*})') from dual
失败原因:
ErrorCode = 600, SQLState = 42000, Details = OBE-00600: internal error code, arguments: -5115, Got error 'U_REGEX_BAD_INTERVAL' from regexp

六、回到业务应用

其实本文中这种歧义用法,虽然在ORACLE中不报错,但是正确的编码方式应该是,对于想要识别成字符的保留符号,需要加上\进行转义,即(\$\{1*\})
但结合实际业务规则来看,加转义的方式虽然看上去结果是对的,但逻辑其实是错的。

该段业务程序是在做模板字符串处理,系统中配置了多个字符串模板,模板中使用${1} ${2}这样的标记作为填充值的占位符。如果使用占位符使用到了 ${11} ,则(\$\{1*\})也能匹配上,导致结果错误。所以准确的做法应该为(\$\{1\}),即不应该有这个*,此时想替换第几个参数均能正确匹配。而为什么之前的业务代码中会有这个*,我猜想大概是当时的开发人员写的(\${1})匹配不到想要的数据时,发现加一个*就能匹配上,就这么用下去了,而该套系统多年以来,从未有超过9个参数的模板,因此该BUG一直未被人发现,直到进行本次国产化改造才挖出来。

七、总结

有很多所谓的"标准功能",在不同的环境下有不同的"标准",这些"标准"各有各的准则,经过多年的发展,很难强求其一致性。就连正则表达式这样常用的功能都有不同的标准,就不要指望ANSI SQL能让任意相同语句在每个数据库中执行结果完全一致了。在去O的过程中,经常能发现以往很多写得不标准的应用代码,此时正是好机会将这些代码变得更加规范。

  • 本文作者: DarkAthena
  • 本文链接: https://www.darkathena.top/archives/regexp-diff-with-repetition-count-between-opengauss-and-oracle
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【代码随想录|完全背包问题】

518.零钱兑换|| 题目链接&#xff1a;518. 零钱兑换 II - 力扣&#xff08;LeetCode&#xff09; 这里求的是组合数&#xff0c;就是不强调元素排列的顺序&#xff0c;211和121是同一个数那种&#xff0c;要先遍历物品&#xff0c;这样的话我算出来的每个值才是按顺序121&…

go语言的成神之路-筑基篇-gin常用功能

第一节-gin参数绑定 目录 第一节-?gin参数绑定 ShouldBind简要概述 功能&#xff1a; 使用场景&#xff1a; 可能的错误&#xff1a; 实例代码 效果展示 第二节-gin文件上传 选择要上传的文件 选择要上传的文件。 效果展示? 代码部分 第三节-gin请求重定向 第…

Qt 12.28 day3

作业&#xff1a; 1】 思维导图 2】 在登录界面的登录取消按钮进行以下设置&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&a…

mybatis-plus 用法总结

MyBatis-Plus&#xff08;简称 MP&#xff09;是 MyBatis 的增强工具&#xff0c;旨在简化开发者的 CRUD 操作。它在 MyBatis 的基础上提供了更多的功能和便利性&#xff0c;如代码生成器、分页插件、性能分析插件等&#xff0c;使开发者能够更高效地进行数据库操作。MyBatis-P…

Rust: enum 和 i32 的区别和互换

在Rust编程语言中&#xff0c;enum&#xff08;枚举&#xff09;和i32是两种不同类型的数据结构&#xff0c;它们各自有不同的用途和特性。 i32 i32是一个32位的有符号整数类型。它用于存储整数值&#xff0c;范围从-2,147,483,648到2,147,483,647。i32是Rust中的基本数据类型…

迁移学习 详解及应用示例

简介&#xff1a; 迁移学习是一种机器学习技术&#xff0c;其核心思想是利用在一个任务上已经学到的知识&#xff08;源任务&#xff1a;任务已经有一个训练好的模型&#xff0c;然后我们将这个模型的某些部分或知识迁移到一个新的但相关的“目标任务”上。&#xff09;来帮助解…

【ETCD】【实操篇(十五)】etcd集群成员管理:如何高效地添加、删除与更新节点

etcd 是一个高可用的分布式键值存储&#xff0c;广泛应用于存储服务发现、配置管理等场景。为了确保集群的稳定性和可扩展性&#xff0c;管理成员节点的添加、删除和更新变得尤为重要。本文将指导您如何在etcd集群中处理成员管理&#xff0c;帮助您高效地维护集群节点。 目录 …

前端 学习

vue结构 package.json 作用&#xff1a;记录项目的元信息&#xff0c;包括依赖包、脚本命令、项目名称、版本号等。 常见字段&#xff1a; dependencies&#xff1a;运行时依赖的 npm 包。 devDependencies&#xff1a;开发时使用的依赖包。 scripts&#xff1a;定义 npm 脚本…

矩阵的因子分解1-奇异值分解

文章目录 矩阵的因子分解1-奇异值分解求法归纳例1. 对矩阵 A ( 0 1 − 1 0 0 2 1 0 ) A \begin{pmatrix} 0 & 1 \\ -1 & 0 \\ 0 & 2 \\ 1 & 0 \end{pmatrix} A ​0−101​1020​ ​ 进行奇异值分解1. 计算 A H A A^H A AHA 的特征值和特征向量2. 将奇异值按…

网易企业邮箱登陆:保障数据安全

网易企业邮箱是一款为企业提供安全可靠的电子邮件服务的工具。通过网易企业邮箱&#xff0c;企业可以实现员工之间的高效沟通和信息共享&#xff0c;同时保障数据的安全性。 企业邮箱的安全性是企业信息保护的重要组成部分。网易企业邮箱采用了多层加密技术&#xff0c;确保邮件…

王佩丰24节Excel学习笔记——第二十二讲:制作甘特图与动态甘特图

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章技巧】 插入图表&#xff0c;针对每一个图表上的元素&#xff0c;都可以选中选右键进行修改数据&#xff1b;本章中的向两端延伸&#xff0c;设置数据的原理&#xff1b;数据格式的显示方式&…

LeetCode 1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解

【LetMeFly】1705.吃苹果的最大数目&#xff1a;贪心(优先队列) - 清晰题解 力扣题目链接&#xff1a;https://leetcode.cn/problems/maximum-number-of-eaten-apples/ 有一棵特殊的苹果树&#xff0c;一连 n 天&#xff0c;每天都可以长出若干个苹果。在第 i 天&#xff0c;…

Docmatix:突破性的文档视觉问答数据集

Docmatix&#xff1a;突破性的文档视觉问答数据集 1. 数据集概述 1.1 规模与创新 数据规模&#xff1a; 240万张图像950万个问答对来自130万份PDF文档较现有数据集扩大240倍 应用领域&#xff1a;文档视觉问答(DocVQA)可访问性&#xff1a;通过HuggingFace平台开放获取 2. …

Doris使用注意点

自己学习过程中整理,非官方 dws等最后用于查询的表可以考虑使用row存储加快查询,即用空间换时间duplicate key的选择要考虑最常查询使用适当使用bloomfilter 加速查询适当使用aggregate 模式降低max,avg,min之类的计算并加快查询,比如加速明细和汇总的一体化查询使用ALTER…

Kubernetes之NodeSelector与NodeName实战

目录 目标 版本 官网 概述 实战 NodeName实战 NodeSelector实战 目标 通过配置NodeSelector与NodeName实现Pod运行&#xff08;或优先运行&#xff09;在我们期望的节点之上。了解这两种实现方法的区别。 版本 Kubernets v1.25.0 官网 将Pod分配给节点https://kubernet…

【docker系列】打造个人私有网盘zfile

1. 介绍 是一个适用于个人的在线网盘(列目录)程序&#xff0c;可以将你各个存储类型的存储源&#xff0c;统一到一个网页中查看、预览、维护&#xff0c;再也不用去登录各种各样的网页登录后管理文件 2. 需要环境 2.1 硬件需求 CPU&#xff1a;至少1核 内存&#xff1a;推荐…

系统思考—冰山模型

“卓越不是因机遇而生&#xff0c;而是智慧的选择与用心的承诺。”—— 亚里士多德 卓越&#xff0c;从来不是一次性行为&#xff0c;而是一种习惯。正如我们在日常辅导中常提醒自己&#xff1a;行为的背后&#xff0c;隐藏着选择的逻辑&#xff0c;而选择的根源&#xff0c;源…

麒麟信安参展南京软博会,支持信创PC的新一代云桌面及全行业解决方案备受瞩目

12月20日至22日&#xff0c;由中国软件行业协会、江苏省软件行业协会等单位联合主办的2024中国&#xff08;南京&#xff09;软件产业博览会在南京国际博览中心隆重开幕。本届博览会以“软件驱动未来&#xff0c;数字闪耀金陵”为主题&#xff0c;吸引了各界目光&#xff0c;省…

【PLL】电荷泵锁相环各个环路参数意义

电荷泵锁相环(CPPLL)在模拟锁相环占据主导&#xff0c; 因为在环路中实现了积分器&#xff0c;而没有有缘放大器即&#xff1a;type 2锁相环可以使用无源RC滤波器实现&#xff0c;简化了PLL设计。 简单CPPLL 与C1串联电阻R1形成零点。 电容累积相位误差&#xff0c;提供积分路…

Java 网络原理 ①-IO多路复用 || 自定义协议 || XML || JSON

这里是Themberfue 在学习完简单的网络编程后&#xff0c;我们将更加深入网络的学习——HTTP协议、TCP协议、UDP协议、IP协议........... IO多路复用 ✨在上一节基于 TCP 协议 编写应用层代码时&#xff0c;我们通过一个线程处理连接的申请&#xff0c;随后通过多线程或者线程…