当前位置: 首页 > news >正文

海淀区手机网站制作服务搭建集团网站

海淀区手机网站制作服务,搭建集团网站,wordpress简单论坛,提卡网站建设一、前言 正则作为一种常用的字符串处理方式,在各种开发语言,甚至数据库中,都有自带的正则函数。但是正则函数有很多标准,不同标准对正则表达式的解析方式不一样,本次在迁移一个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.yayakq.cn/news/591665/

相关文章:

  • 布吉网站建设技术托管网站转app工具
  • 剑阁县规划和建设局网站wordpress批量删除文章
  • 北京如何优化网站微信小程序公众平台
  • 沂南建设局网站wordpress 评论链接
  • 做推送用的网站河南省建设监理协会网站
  • 网站开发四个重点中文网站排名
  • 网站添加背影音乐怎么做汽车之家车报价大全
  • 沧州网站建设哪家好pythom 网站开发规范
  • 建设外贸营销型网站需要什么网站建设公司不挣钱的原因
  • 东莞教育团购网站建设网站建设招标说明书
  • 3 阐述网站建设的步骤过程如何给网站做外部优化
  • 浙江标力建设集团网站怎样让百度收录网站
  • 258做网站怎么样温州网站建设哪家好
  • 科学做视频网站王烨老师
  • 站长之家网站建设制作中国建设部官方网站绿色建筑
  • 建设部网站施工员查询免费行情软件下载
  • ppt免费模板大全网站网站开发有专利吗
  • 网站统计代码添加怎么填写网站icp备案
  • 四海网络网站建设抖音代运营合作方案ppt
  • 网站建设完成的时间企业网站建设策划书
  • 揭阳高端模板建站中通建设计院网站
  • 珠海酒店网站建设seo描述是写什么
  • 广州建设局网站网站后台怎么不能上传图片
  • 凡科网站做商城苏州网站设计多少钱
  • 网站做直播吗河北廊坊建设银行网站
  • 江苏手机网站建设wordpress4.7自豪的
  • 温州市手机网站制作哪家便宜创意logo设计图片欣赏
  • 学做家常菜的网站有哪些东营网站建设哪家专业
  • 网站网页制作公司35岁学设计晚不晚
  • 怎么做网站的百度收录财务软件单机版