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

news2025/1/4 8:03:50

一、前言

正则作为一种常用的字符串处理方式,在各种开发语言,甚至数据库中,都有自带的正则函数。但是正则函数有很多标准,不同标准对正则表达式的解析方式不一样,本次在迁移一个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:915
        breakpoint already hit 2 times
7       breakpoint     keep y   0x0000000000fc42c4 in parsebranch(vars*, int, int, state*, state*, int) at regcomp.cpp:719
        breakpoint 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:984


regc_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);
                    } else
                        FAILW(REG_BADBR);
                    break;
                case CHR('\\'): /* BRE bound ends with \} */
                    if (INCON(L_BBND) && NEXT1('}')) {
                        v->now++;
                        INTOCON(L_BRE);
                        RET('}');
                    } else
                        FAILW(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.coloradmin.cn/o/2269072.html

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

相关文章

SSM-Spring-IOC/DI对应的配置开发

目录 一、IOC 控制反转 1.什么是控制反转呢 2. Spring和IOC之间的关系是什么呢? 3.IOC容器的作用以及内部存放的是什么? 4.当IOC容器中创建好service和dao对象后&#xff0c;程序能正确执行么? 5.Spring 容器管理什么内容&#xff1f; 6.如何将需要管理的对象交给 …

docker中使用nginx

宿主机和docker中nginx做映射 宿主机中nginx 映射目录 /root/myDockerData/devnginx 在容器中相关位置分别是&#xff1a; 配置文件位置&#xff1a;/etc/nginx/ 日志位置&#xff1a;/var/log/nginx/ 项目位置&#xff1a;/usr/share/nginx/html 如下配置启动命令行&#x…

Rocky Linux下安装meld

背景介绍&#xff1a; meld是一款Linux系统下的用于 文件夹和文件的比对软件&#xff0c;非常常用&#xff1b; 故障现象&#xff1a; 输入安装命令后&#xff0c;sudo yum install meld&#xff0c;报错。 12-31 22:12:17 ~]$ sudo yum install meld Last metadata expirat…

Python中PDF转Word的技术

Python PDF转Word技术概述 在日常办公和数据处理中&#xff0c;经常需要将PDF文档转换为Word文档&#xff0c;以便进行编辑、修改或格式调整。Python作为一种强大的编程语言&#xff0c;提供了多种库和工具来实现这一功能。以下是对Python中PDF转Word技术的详细介绍。 一、技…

混合并行训练框架性能对比

混合并行训练框架性能对比 1. 框架类型 DeepSpeed、Megatron - LM、Colossal - AI、SageMaker、Merak、FasterMoE、Tutel、Whale、Alpa、DAPPLE、Mesh - TensorFlow 2. 可用并行性(Available parallelisms) DNN framework(深度神经网络框架)DP(数据并行,Data Parallelis…

python24-常用的第三方库02:openpyxl模块、pdfplumber模块

一、openpyxl模块 openpyxl 是一个用于读取和写入 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库。 它允许你以编程方式操作 Excel 文件&#xff0c;包括创建新的工作簿、修改现有的工作簿、添加或删除工作表、读写单元格数据等。 1-1、安装openpyxl模块 验证&#xff1a…

npm ERR! ECONNRESET 解决方法

问题&#xff1a;npm 命令遇到的错误是 ECONNRESET&#xff0c;这通常与网络连接问题相关。设置代理解决问题。 一、查看当前代理设置 npm config get proxy npm config get https-proxy二、设置代理 npm config set proxy http://your-proxy-address:port npm config set h…

单元测试3.0+ @RunWith(JMockit.class)+mock+Expectations

Jmockit使用笔记_基本功能使用Tested_Injectable_Mocked_Expectations_jmockit.class-CSDN博客 测试框架Jmockit集合junit使用 RunWith(JMockit.class) 写在测试案例类上的注解 Tested 在测试案例中,写在我们要测试的类上面, 一般用实现类 Injectable 在测试案例中声明…

网络渗透测试实验二:网络嗅探与身份认证

1.实验目的和要求 1、通过使用Wireshark软件掌握Sniffer&#xff08;嗅探器&#xff09;工具的使用方法&#xff0c;实现捕捉HTTP等协议的数据包&#xff0c;以理解TCP/IP协议中多种协议的数据结构、通过实验了解HTTP等协议明文传输的特性。 2、研究交换环境下的网络嗅探实现…

mqtt连接onenet云平台

密码 version2018-10-31&resproducts%2FlzNd7drwE2%2Fdevices%2Flocation_1&et1756617761&methodmd5&sign52jsIUhK7i2zXjlEtkwDhQ%3D%3D 设备名称&#xff1a;temperatureAndHumidity 设备密钥&#xff1a;bE5OSHBlTHU3TDNSUUVoTmY0WWZqbThDVzNjOUJ3Y1Y 产品i…

Neo4j GDS 2.0 安装与配置

Neo4j GDS 2.0 安装与配置 GDS插件安装&#xff1a;Neo4j官方文档 1. GDS简介 Neo4j Graph Data Science (GDS) 库作为 Neo4j Graph Database 的插件提供。该插件需要安装到数据库中并在 Neo4j 配置中列入白名单。有两种主要方法可以实现这一点&#xff0c;我们将在本章中详…

艾体宝方案丨全面提升API安全:AccuKnox 接口漏洞预防与修复

一、API 安全&#xff1a;现代企业的必修课 在现代技术生态中&#xff0c;应用程序编程接口&#xff08;API&#xff09;扮演着不可或缺的角色。从数据共享到跨平台集成&#xff0c;API 成为连接企业系统与外部服务的桥梁。然而&#xff0c;伴随云计算的普及与微服务架构的流行…

使用JMeter对Linux生产服务器进行压力测试

安装 JMeter wget https://downloads.apache.org/jmeter/binaries/apache-jmeter-5.4.1.tgz tar -xzf apache-jmeter-5.4.1.tgz cd apache-jmeter-5.4.1创建 JMeter 脚本 设置中文 选择Options—>Choose Language—>选择其他语言&#xff08;例如&#xff1a;Chinese&am…

位置编码--RPE

相对位置编码 (Relative Position Encoding, RPE) 1. 相对位置编码 相对位置编码是 Transformer 中的一种改进位置编码方式&#xff0c;它的主要目的是通过直接建模序列中元素之间的相对位置&#xff0c;而不是绝对位置&#xff0c;从而更好地捕捉序列元素之间的依赖关系&#…

《代码随想录》Day21打卡!

写在前面&#xff1a;祝大家新年快乐&#xff01;&#xff01;&#xff01;2025年快乐&#xff0c;2024年拜拜~~~ 《代码随想录》二叉树&#xff1a;修剪二叉搜索树 本题的完整题目如下&#xff1a; 本题的完整思路如下&#xff1a; 1.本题使用递归进行求解&#xff0c;所以分…

【mysql】linux安装mysql客户端

参考文章&#xff1a; MySQL系列之如何在Linux只安装客户端 linux下安装mysql客户端client MySQL Community Downloads 查看linux版本方法&#xff1a; lsb_release -a cat /proc/version下载文件&#xff1a; rpm -ivh mysql-community-*可以删除错误的包&#xff1a; RP…

HTML——26.像素单位

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>像素</title></head><body><!--像素&#xff1a;1.指设备屏幕上的一个点&#xff0c;单位px&#xff0c;如led屏上的小灯朱2.当屏幕分辨率固定时&…

一键闪测仪:MLCC尺寸测量解决方案

MLCC是电子行业中常用的陶瓷电容器&#xff0c;其尺寸影响物理占用空间、电气性能和可靠性等&#xff0c;因此MLCC尺寸管控对产品质量至关重要。 在此&#xff0c;小优博士给各位介绍MLCC的概况以及MLCC尺寸快速测量解决方案。 一、MLCC概述 MLCC&#xff08;Multi-layer Cer…

Spring API 接口加密/解密

API 接口加密/解密 为了安全性需要对接口的数据进行加密处理&#xff0c;不能明文暴露数据。为此应该对接口进行加密/解密处理&#xff0c;对于接口的行为&#xff0c;分别有&#xff1a; 入参&#xff0c;对传过来的加密参数解密。接口处理客户端提交的参数时候&#xff0c;…

学习threejs,导入pdb格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.PDBLoader pdb模型加…