【ORACLE】一个允许关键字作为别名所引起的语法歧义场景

news2024/12/20 13:20:27

前言

最近在看SQL语法解析器,发现了antlr4提供的PlSql语法树存在一个BUG,然后我顺着这个BUG,构造了一条SQL,在ORACLE执行,如下

image-ghns.png

然后神奇的事情出现了,这个查询竟然没有返回行!t1表左关联t2,而且对t1表没有过滤条件,那么无论如何t1表中的数据应该是全部展现的!
然后有人可能认为ORACLE就是这样的,但下面这个图的出现可能会让人瞬间抓狂!

image-nzct.png

两条长得完全一样的SQL,就是简单的两个表关联查询,竟然出现了结果不一致的情况!

起因

在客户应用迁移到某国产数据库中,有一条类似这样的SQL

select * from t1 left join t2 on t1.id=t2.ids(+);

在该国产数据库中会报错 ERROR: Operator "(+)" can only be used in WhereClause of Select-Statement or Subquery.

即不允许操作符 (+)出现在select语句的where子句以外的地方。
于是我们开始分析这样的SQL基于语法树应该如何改写。
首先这个SQL是两个表的left join,一般正常的写法是

select * from t1,t2 where t1.id=t2.ids(+);

select * from t1 left join t2 on t1.id=t2.ids;

即两个表的左关联不需要同时使用 left join(+),两种用法重复了,最简单的改法就是在原SQL上去掉 (+)号。
但是只能针对两表的 left joinright join,对于 join则不能简单的去掉,因为 join的时候,(+)就有意义了。
于是乎,我们试图通过语法树来看是否可以区分出 joinleft join

join_clause
    : query_partition_clause? (CROSS | NATURAL)? (INNER | outer_join_type)? JOIN table_ref_aux query_partition_clause? (
        join_on_part
        | join_using_part
    )*
    | (CROSS | OUTER) APPLY table_ref_aux
    ;
outer_join_type
    : (FULL | LEFT | RIGHT) OUTER?
    ;

从语法解析规则文件来看,似乎是可以区分的,然后解析一下这条SQL:

select * from t1 left join t2 on t1.id=t2.ids;
^Z
(sql_script
   (unit_statement
      (data_manipulation_language_statements
         (select_statement
            (select_only_statement
               (subquery
                  (subquery_basic_elements
                     (query_block select
                        (selected_list *)
                        (from_clause from
                           (table_ref_list
                              (table_ref
                                 (table_ref_aux
                                    (table_ref_aux_internal
                                       (dml_table_expression_clause
                                          (tableview_name
                                             (identifier
                                                (id_expression
                                                   (regular_id t1))))))
                                    (table_alias
                                       (identifier
                                          (id_expression
                                             (regular_id
                                                (non_reserved_keywords_pre12c left))))))
                                 (join_clause join
                                    (table_ref_aux
                                       (table_ref_aux_internal
                                          (dml_table_expression_clause
                                             (tableview_name
                                                (identifier
                                                   (id_expression
                                                      (regular_id t2)))))))
                                    (join_on_part on
                                       (condition
                                          (expression
                                             (logical_expression
                                                (unary_logical_expression
                                                   (multiset_expression
                                                      (relational_expression
                                                         (relational_expression
                                                            (compound_expression
                                                               (concatenation
                                                                  (model_expression
                                                                     (unary_expression
                                                                        (atom
                                                                           (general_element
                                                                              (general_element
                                                                                 (general_element_part
                                                                                    (id_expression
                                                                                       (regular_id t1)))) .
                                                                              (general_element_part
                                                                                 (id_expression
                                                                                    (regular_id
                                                                                       (non_reserved_keywords_pre12c id)))))))))))
                                                         (relational_operator =)
                                                         (relational_expression
                                                            (compound_expression
                                                               (concatenation
                                                                  (model_expression
                                                                     (unary_expression
                                                                        (atom
                                                                           (general_element
                                                                              (general_element
                                                                                 (general_element_part
                                                                                    (id_expression
                                                                                       (regular_id t2)))) .
                                                                              (general_element_part
                                                                                 (id_expression
                                                                                    (regular_id ids)))))))))))))))))))))))))))) ; <EOF>)

结果发现这里的 left竟然被当成了t1表的别名!ORACLE对于关键字作为别名的一个重要特点就是可以不加 as,而且ORACLE也的确支持 left作为表的别名。于是乎这里的语法似乎也可以说得过去,不是外关联,而是 left这个表和 t2表做 join。(其实是开源的语法解析器的问题https://github.com/antlr/grammars-v4/issues/1726 )

而ORACLE自然是不会允许这种情况发生的,ORACLE在语法解析的时候,读到left,会往后匹配一个词,如果是 joinouter join,则一起识别为 left join,否则,就把left识别为表的别名。但这样就会导致要执行更多的判断逻辑。而其他数据库禁止使用left作为别名,或者禁止不带 as直接作为别名时,则不需要有这样判断,并且不会引起语法上的歧义。

答案揭晓

在做这个语法分析的过程中,我们发现了这一语法歧义,自然就可以想到,我们能不能通过某种手段,在oracle里让这个 left真的变为别名。
其实很简单,left里有一个特殊的字母 e,经常做信息安全方面的人,以及经常识别假冒账号的人,对这个 e可以说是非常熟悉,比如下面这4个进程名,去对比字符,可以发现是完全不同的四个进程名

explorer.exe
еxplorеr.exe
еxplorer.exe
explorеr.exe
SQL> with t as (
  2  select 'explorer.exe' c from dual union all
  3  select 'еxplorеr.exe' from dual union all
  4  select 'еxplorer.exe' from dual union all
  5  select 'explorеr.exe' from dual)
  6  select c,utl_raw.cast_to_raw(c) from t;

C              UTL_RAW.CAST_TO_RAW(C)
-------------- --------------------------------------------------------------------------------
explorer.exe   6578706C6F7265722E657865
еxplorеr.exe   A7D678706C6F72A7D6722E657865
еxplorer.exe   A7D678706C6F7265722E657865
explorеr.exe   6578706C6F72A7D6722E657865

其实这里的 е不是 e,而是俄文的 е,没错,俄文里的 e,字形和英文字母的 e是一模一样的,但其实是两个不同的字符。
所以文章开始的那两个SQL就是下面的看似一样的两条SQL查出来的

with t1 as (select 1 id from dual),
t2 as (select 0 ids from dual)
select * from t1 lеft join t2 on id=ids;

with t1 as (select 1 id from dual),
t2 as (select 0 ids from dual)
select * from t1 left join t2 on id=ids;

总结

虽然在国内的实际生产应用中,这里出现俄文的 е,概率是极低的,但养成良好的开发习惯,不过于依赖ORACLE这种宽松的SQL标准,比如别名就得加 as,不要混用 join(+)等,这样能在异构数据库兼容改造时,省去不少麻烦。

  • 本文作者: DarkAthena
  • 本文链接: https://www.darkathena.top/archives/A-Scenario-of-Syntactic-Ambiguity-Caused-by-Allowing-Keywords-as-Aliases
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处

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

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

相关文章

【前端】Jquery拍照,通过PHP将base64编码数据转换成PNG格式,并保存图像到本地

目录 一、需求 二、开发语言 三、效果 四、业务逻辑&#xff1a; 五、web端调用摄像头 六、示例代码 1、前端 2、后端 一、需求 web端使用jquery调用摄像头拍照&#xff0c;并使用PHP把base64编码转换成png格式图片&#xff0c;下载到本地。 由于js不能指定图片存储的…

本地摄像头视频流在html中打开

1.准备ffmpeg 和(rtsp-simple-server srs搭建流媒体服务器)视频服务器. 2.解压视频流服务器修改配置文件mediamtx.yml ,hlsAlwaysRemux: yes 3.双击运行服务器。 4&#xff0c;安装ffmpeg ,添加到环境变量。 5.查询本机设备列表 ffmpeg -list_devices true -f dshow -i d…

机器情绪及抑郁症识别算法(六)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

34. Three.js案例-创建球体与模糊阴影

34. Three.js案例-创建球体与模糊阴影 实现效果 知识点 WebGLRenderer WebGLRenderer 是 Three.js 中用于渲染 3D 场景的核心类。它负责将场景中的对象绘制到画布上。 构造器 new THREE.WebGLRenderer(parameters)参数类型描述parametersObject可选参数对象&#xff0c;包…

服务器数据恢复—RAIDZ离线硬盘数超过热备盘数导致阵列崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; ZFS Storage 7320存储阵列中有32块硬盘。32块硬盘分为4组&#xff0c;每组8块硬盘&#xff0c;共组建了3组RAIDZ&#xff0c;每组raid都配置了热备盘。 服务器存储故障&#xff1a; 服务器存储运行过程中突然崩溃&#xff0c;排除人为误操…

108. 将有序数组转换为二叉搜索树(java)

题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,…

电子应用设计方案-60:智能床垫系统方案设计

智能床垫系统方案设计 一、引言 智能床垫作为智能家居的一部分&#xff0c;旨在为用户提供更舒适的睡眠体验和健康监测功能。本方案将详细描述智能床垫系统的设计理念、功能模块及技术实现。 二、系统概述 1. 系统目标 - 实时监测睡眠状态&#xff0c;包括心率、呼吸、体动等…

YOLOv8目标检测(六)_封装API接口

YOLOv8目标检测(一)_检测流程梳理&#xff1a;YOLOv8目标检测(一)_检测流程梳理_yolo检测流程-CSDN博客 YOLOv8目标检测(二)_准备数据集&#xff1a;YOLOv8目标检测(二)_准备数据集_yolov8 数据集准备-CSDN博客 YOLOv8目标检测(三)_训练模型&#xff1a;YOLOv8目标检测(三)_训…

CSDN数据大屏可视化【开源】

项目简介 本次基于版本3 开源 版本3开源地址&#xff1a;https://github.com/nangongchengfeng/CsdnBlogBoard.git 版本1开源地址&#xff1a;https://github.com/nangongchengfeng/CSDash.git 这是一个基于 Python 的 CSDN 博客数据可视化看板项目&#xff0c;通过爬虫采…

Moretl安全日志采集工具

永久免费: 至Gitee下载 使用教程: Moretl使用说明 使用咨询: 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架…

无人机航测系统技术特点!

一、无人机航测系统的设计逻辑 无人机航测系统的设计逻辑主要围绕实现高效、准确、安全的航空摄影测量展开。其设计目标是通过无人机搭载相机和传感器&#xff0c;利用先进的飞行控制系统和数据处理技术&#xff0c;实现对地表信息的全方位、高精度获取。 需求分析&#xff1…

Java学习笔记(13)——面向对象编程

面向对象基础 目录 面向对象基础 方法重载 练习&#xff1a; 继承 继承树 protected super 阻止继承 向上转型 向下转型 区分继承和组合 练习 小结&#xff1a; 方法重载 如果有一系列方法&#xff0c;功能类似&#xff0c;只是参数有所不同&#xff0c;就可以把…

Running CMake (运行 CMake)

Running CMake {运行 CMake} 1. CLion - Create a new CMake project2. Running CMake (运行 CMake)2.1. Building a project (构建项目)2.2. Picking a compiler (指定编译器)2.3. Verbose and partial builds (详细和部分的构建)2.4. Options (选项)2.4.1. Standard options …

穷举vs暴搜vs深搜vs回溯vs剪枝专题一>子集

题目&#xff1a; 两个方法本质就是决策树的画法不同 方法一解析&#xff1a; 代码&#xff1a; class Solution {private List<List<Integer>> ret;//返回结果private List<Integer> path;//记录路径&#xff0c;注意返回现场public List<List<Int…

MTU 使用使用解释

MTU (Maximum Transmission Unit&#xff0c;最大传输单元) 指的是网络链路层 (例如以太网) 能够传输的最大数据帧大小&#xff0c;以字节为单位。理解 MTU 对网络性能和可靠性至关重要&#xff0c;因为它直接影响数据包的分片 (Fragmentation) 和重组。本文档将详细解释 MTU 的…

uniapp v-tabs修改了几项功能,根据自己需求自己改

根据自己的需求都可以改 这里写自定义目录标题 1.数组中的名字过长&#xff0c;导致滑动异常2.change 事件拿不到当前点击的数据&#xff0c;通过index在原数组中查找得到所需要的id 各种字段麻烦3.添加指定下标下新加红点显示样式 1.数组中的名字过长&#xff0c;导致滑动异常…

iOS - 超好用的隐私清单修复脚本(持续更新)

文章目录 前言开发环境项目地址下载安装隐私访问报告隐私清单模板最后 前言 在早些时候&#xff0c;提交应用到App Store审核&#xff0c;大家应该都收到过类似这样的邮件&#xff1a; Although submission for App Store review was successful, you may want to correct th…

c语言-----数组

基本概念 数组是C语言中一种用于存储多个相同类型数据的数据结构。这些数据在内存中是连续存储的&#xff0c;可以通过索引&#xff08;下标&#xff09;来访问数组中的各个元素。数组的索引从0开始&#xff0c;这是C语言的规定。例如&#xff0c;一个有n个元素的数组&#xff…

社区版 IDEA 开发webapp 配置tomcat

1.安装tomcat 参考Tomcat配置_tomcat怎么配置成功-CSDN博客 2.构建webapp项目结构 新建一个普通项目 然后添加webapp的目录结构&#xff1a; main目录下新建 webapp 文件夹 webapp文件夹下新建WEB_INF文件夹 *WEB_INF目录下新建web.xml wenapp文件夹下再新建index.html …

全面解析 Kubernetes 流量负载均衡:iptables 与 IPVS 模式

目录 Kubernetes 中 Service 的流量负载均衡模式 1. iptables 模式 工作原理 数据路径 优点 缺点 适用场景 2. IPVS 模式 工作原理 数据路径 优点 缺点 适用场景 两种模式的对比 如何切换模式 启用 IPVS 模式 验证模式 总结 Kubernetes 中 Service 的流量负载…