淘宝数据库OceanBase SQL编译器部分 源码阅读--生成逻辑计划
SQL编译解析三部曲分为:构建语法树,生成逻辑计划,指定物理执行计划。第一步骤,在我的上一篇博客淘宝数据库OceanBase SQL编译器部分 源码阅读--解析SQL语法树里做了介绍,这篇博客主要研究第二步,生成逻辑计划。
一、 什么是逻辑计划?
我们已经知道,语法树就是一个树状的结构组织,每个节点代表一种类型的语法含义。如
update student set sex="M" where name ="小明";
这条SQL的语法树形状为:
|Update Stmt
|----Table:student
|----TargeList:
|--------sex = "M"
|----Qualifications:
|--------name="小明"
但是仅仅语法树并不能知道数据库中是否存在student这张表,这张表是否有sex,name这两个字段,我们是否有权限修改这条记录等。语法树只能判断这条SQL的写法是否正确,不能确定这条SQL是否可以执行。
逻辑计划需要明确SQL语句中所涉及到的表,字段,表达式等是否有效。这个的逻辑计划与在《数据库系统实现》等书中描述的逻辑查询计划不同。逻辑查询计划将SQL语句直接转为可运算的关系表达式。在OceanBase中,逻辑计划则只是查找或生成涉及到的表的ID,涉及字段的ID,涉及表达式的ID等,逻辑计划是不可运算的。
二、逻辑计划包含哪些内容?
简单来说,逻辑计划要弄清楚,这条SQL可以分解为几条stmt,每条stmt包含了哪些表,字段和表达式。在此基础上,如果是insert的Stmt,要加上设置哪些值;如果是update的stmt,要加上需要更新的列和对应的值,等等。
在一个逻辑计划中,每一个查询有一个唯一标识qid,每一张表有一个唯一的标识tid,每一个列有一个唯一的标识cid,每一个表达式有一个唯一的标识eid。
来看OceanBase中的逻辑计划的结构(省略无关方法和变量).
class ObLogicalPlan
{
//...
oceanbase::common::ObVector stmts_; //存储该逻辑计划的所有stmt
oceanbase::common::ObVector exprs_; //逻辑计划的所有表达式
oceanbase::common::ObVector raw_exprs_store_;//存储逻辑计划的所有表达式
uint64_t new_gen_tid_;//用于生成新的tid
uint64_t new_gen_cid_;//用于生成新的cid
uint64_t new_gen_qid_;//用于生成新的qid
uint64_t new_gen_eid_;//用于生成新的eid
};
oceanbase::common::ObVector是OceanBase中自己实现的泛型容器之一,作用与STL的vector相同。
stmts_存储该逻辑计划的所有stmt;
raw_exprs_store_仅仅用于存储表达式,exprs_则引用raw_exprs_store_中的内容。
new_gen_tid_等4个变量是用来生成新的标识时使用,一个逻辑是可以用多个tid,多个cid,多个eid,多个qid的。这些标识分布于存储的stmt和表达式中。
注:stmt实在不知道中文该怎么称呼,就不改中文名了。
2.1 逻辑计划中表的定义
struct TableItem
{
uint64_t table_id_;
common::ObString table_name_;
common::ObString alias_name_;
TableType type_;
uint64_t ref_id_;
};
table_id_唯一标识一个关系表,其类型分为基本表,引用表和子查询关系。
对同一个实体表,ref_id_与table_id_相同; 如果是一个引用别名的表,则table_id_是新生成的,ref_id_与这个表真正的table_id_相同;如果是一个子查询,则table_id_是新生成的,ref_id_是对子查询的引用。
对同一个实体表,它在所有线程使用的table_id_都是相同的;如果是生成的标识,则仅在该线程会话期间是唯一的。
2.2 逻辑计划中列的定义
struct ColumnItem
{
uint64_t column_id_;
common::ObString column_name_;
uint64_t table_id_;
uint64_t query_id_;
bool is_name_unique_;
bool is_group_based_;
common::ObObjType data_type_;
};
column_id_唯一标识一个列,table_id_和query_id_为该列所属的关系表和stmt。is_name_unique_仅用在解析逻辑计划期间,标记该列的名称是否在所有表的字段中都是唯一的。is_group_based_标记该列是否用于分组。data_type_标识该列的数据类型。
2.3 逻辑计划中的表达式的定义
逻辑计划的中表达式有多种类型,其基类为ObRawExpr.包括两个成员变量,type_表示表达式的类型,result_type_表示表达式值的类型。
class ObRawExpr
{
//省略其他方法
private:
ObItemType type_;
common::ObObjType result_type_;
}
表达式分为常量表达式, 一元引用表达式,二元引用表达式,一元操作符表达式,二元操作符表达式,三元操作符表达式,多元操作符表达式,case操作符表达式,聚集函数表达式,系统函数表达式,SQL原生表达式等。继承关系如下。
namespace sql
{
//原生表达式基类
class ObRawExpr
//常量表达式
class ObConstRawExpr : public ObRawExpr
//一元引用表达式
class ObUnaryRefRawExpr : public ObRawExpr
//二元引用表达式
class ObBinaryRefRawExpr : public ObRawExpr
//一元操作符表达式
class ObUnaryOpRawExpr : public ObRawExpr
//二元操作符表达式
class ObBinaryOpRawExpr : public ObRawExpr
//三元操作符表达式
class ObTripleOpRawExpr : public ObRawExpr
//多元操作符表达式
class ObMultiOpRawExpr : public ObRawExpr
//case操作符表达式
class ObCaseOpRawExpr : public ObRawExpr
//聚集函数表达式
class ObAggFunRawExpr : public ObRawExpr
//系统函数表达式
class ObSysFunRawExpr : public ObRawExpr
//SQL原生表达式
class ObSqlRawExpr : public ObRawExpr
};
class Ob