合法性校验是SQL处理的第二步,在计算执行前,提前验证SQL正确性。该验证操作是非线性的,需要基于语法树处理各种嵌套的复杂情况。Calcite合法性校验基于SqlValidator 接口和对应实现类SqlValidatorImpl 完成。
Calcite合法性校验涉及到两个基本概念:
SqlValidatorScope基于的基本解析&验证功能列表如下所示:
基本功能 | 说明 |
---|---|
resolve | 名字解析, 根据给定的path查找SqlNode |
findQualifyingTableNames | 收集解析域内的所有数据表信息 |
findAllColumnNames | 收集解析域内所有的列名信息 |
findAliases | 收集解析域内的所有别名(alias)信息 |
fullyQualify | 返回列的全称,例如“deptno”得到“emp.deptno” |
addChild | 在解析域内注册一个SqlValidatorNamespace |
getMonotonicity | 返回某个表达式在该解析域内是否有序 |
getOrderList | 作用域内有序的表达式,若没有则返回null |
validateExpr | 在解析域范围内验证表达式的合法性 |
lookupWindow | 查找解析域内的Window表达式,未找到则返回null |
resolveTable | 解析域范围内解析数据表的信息 |
resolveColumn | 解析列名并返回其类型,不合法的抛出异常 |
Calcite合法性校验主要分为两个执行阶段:
1. 注册阶段:遍历整个SqlNode语法树,基于MAP哈希表维护各个SqlNode节点的命名空间Namespace和解析域Scope。
以如下查询SQL为例,首先识别到绿色SELECT节点会生成对应SelectNamesapce和SelectScope,继续识别紫色Table节点生成对应IdentifierNamespace和TableScope。同样的,依次不断遍历执行,完成所有节点的Namespace和Scope注册。
select emp_id, dept_id,
(select dept_name from hr.depts where dept_id = e.dept_id) as dname
from hr.emps as e
2. 校验阶段:SqlValidator定义了校验的全流程,基于各节点已注册关联的Namespace和Scope执行对应validate操作。校验处理可简化为: validate = Namespace#validate + Scope#validateExpr + 额外校验
基于CalciteSchema(元数据定义)获取表定义,校验:(1).表是否存在;(2).字段类型是否匹配。校验流程如下图所示
Calcite支持两种函数类型:
在合法性校验阶段会对使用的Function函数逐个验证,主要分为两步:
为确保数据操作的灵活性和兼容性,SQL引擎会提供数据转换功能,数据转换可分为显式转换和隐式转换两类:
各类计算引擎所具备的隐式转换能力矩阵各不相同,隐式转换能力越强,则引擎在校验阶段,对字段类型的校验越宽松。例如,Spark的隐式转换能力远胜于Presto,相同的SQL语句在Spark中可以执行成功,但在Presto中可能会直接抛出类型不匹配的错误。
为解决底层各类计算引擎隐式转换能力不同的问题,尽可能让相同语义的SQL可以在不同引擎执行成功。Calcite在validate阶段实现了一套通用的隐式转换处理机制,主要处理包括:
Calcite隐式转换如下图所示:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。