OceanBase SQL解析源码分析(三)
2016-03-02 14:28
756 查看
原创文章,转载请注明: 转载自 镜中影的技术博客
本文链接地址: OceanBase SQL解析源码分析(三)
URL:http://blog.csdn.net/linkpark1904/article/details/50779680
在函数的实现中可以看到这样一段代码:
这就进入了由Flex和Bison编译生成的词法语法解析器中,最终生成的语法树就存储在parse_result中,作为后续的逻辑计划生成的前提条件。
Plan_tree_ 成员用来存储具体的逻辑计划树,name_pool_和OceanBase中的内存管理部分挂钩,schema_checker_为表结构的检查类,用来检查数据表是否有效,包括列检查等等相关操作。
在这段代码中,输入参数包括存储逻辑计划结果的result_plan以及上一步解析生成的语法树也就是parse_result.result_tree_;所以逻辑计划的核心就在resolve函数中。
在进入resolve函数中,其核心的解析代码如下(build.cpp:1674):
这里并没有列举完全,实际上就是一系列的switch……case……语句,就是根据语法树节点的类型,调用相应的resolve函数,例如,如果解析的语句是SELECT语句,那么将会调用resolve_select_stmt来解析语法树。当然,oceanbase这里只对外暴露的resolve函数,并没有暴露resolve_delete_stmt,resolve_insert_stmt等相关的内部函数。
进行分析,在词法语法解析阶段,就可以解析SQL语句,生成具体的语法解析树,那么语法树在经过resovle函数之后,由于顶层节点的语法树节点的类型为T_UPDATE,在resolve函数代码中可以看到如下代码片段:
所以,Update的解析语句主要由resolve_update_stmt(dml_build_plan.cpp)这个函数来承担。
关于ObLogicPlan就是描述整个逻辑计划的类,其数据结构包括一系列的语句以及表达式集合(stmt以及expr)剩下的就是各种表,列等的id。
进一步分析,逻辑计划表达式的类的描述如下所示:
这个是基础语句类,描述的是语法树中一系列的由关键字解析出来的语句,StmtType是一个枚举类型,query_id_代表的是语句的id,StmtType的部分声明代码如下所示:
当然,这仅仅只是一个基类,其具体的继承体系为:
实际上主要描述一个具体的语句是通过继承体系中的子类来表述的。一个比较重要的类ObStmt的描述如下:
一个具体的ObUpdateStmt类的声明如下所示(ob_update_stmt.h):
另外,一个具体的描述表的数据结构声明如下(ob_stmt.h):
一个具体的描述列属性的数据结构声明如下(ob_stmt.h):
ObRawExpr继承体系也是相对比较重要的数据结构,ObRawExpr继承体系是对具体的执行表达式的描述,表达式分为常量表达式, 一元引用表达式,二元引用表达式,一元操作符表达式,二元操作符表达式,三元操作符表达式,多元操作符表达式,case操作符表达式,聚集函数表达式,系统函数表达式,SQL原生表达式等。继承关系如下。
好,这里再反观一下表达式的数据结构设计
为主,那么解析过程的流程图也就是resolve_update_stmt()函数的执行流程大致如下:
1. 初始化逻辑计划树,生成一个唯一语句标志query_id()query_id = logical_plan->generate_query_id()
2. 将update_stmt加入逻辑计划树中ret = logical_plan->add_query(update_stmt)
3. 解析语句中的表ret = resolve_table(result_plan, update_stmt, table_node, table_id),具体的执行动作在resolve_table中,主要是通过schema_checker来验证表的正确性,生成table_item。
4. For循环遍历T_ASSIGN_LIST来读取列名和值对a)根据列名获取列b) 将该列存储到
5. 解析where子句,将表达式id放入ObVector where_expr_ids_
6. 解析when子句,将表达式id放入when_expr_ids_中
7. 解析hints
本文链接地址: OceanBase SQL解析源码分析(三)
URL:http://blog.csdn.net/linkpark1904/article/details/50779680
四、 Oceanbase SQL解析整体流程
基本上每一个SQL解析到最终物理任务执行经历的流程大同小异,从入口点开始,首先通过Flex和Bison生成的语法解析工具,对SQL进行语法解析,生成语法树。在生成的语法树的基础上,生成内部描述的逻辑计划,通过逻辑计划进行查询优化,生成具体的物理执行计划。4.1 OceanBase词法语法解析
OceanBase词法语法解析在前面大都已经叙述完毕,这里OceanBase关于SQL解析部分的入口点在ob_sql.h文件中,入口函数如下所示:/** * execute the SQL statement directly * * @param stmt [in] * @param result [out] * * @return oceanbase error code defined in ob_define.h */ static int direct_execute(const common::ObString &stmt, ObResultSet &result, ObSqlContext &context);
在函数的实现中可以看到这样一段代码:
parse_sql(&parse_result,stmt.ptr(),static_cast<size_t>(stmt.length())
这就进入了由Flex和Bison编译生成的词法语法解析器中,最终生成的语法树就存储在parse_result中,作为后续的逻辑计划生成的前提条件。
4.2 OceanBase逻辑计划生成
在OceanBase中,通过结构体ResultPlan来存储具体的逻辑计划结果,跟踪源代码,可以发现ResultPlan的定义在parse_node.h中,具体结构定义如下所示:typedef struct { void* plan_tree_; void* name_pool_; // ObStringBuf void* schema_checker_; // ObSchemaChecker ErrStat err_stat_; int is_prepare_; } ResultPlan;
Plan_tree_ 成员用来存储具体的逻辑计划树,name_pool_和OceanBase中的内存管理部分挂钩,schema_checker_为表结构的检查类,用来检查数据表是否有效,包括列检查等等相关操作。
ret = resolve(&result_plan, parse_result.result_tree_);
在这段代码中,输入参数包括存储逻辑计划结果的result_plan以及上一步解析生成的语法树也就是parse_result.result_tree_;所以逻辑计划的核心就在resolve函数中。
4.2.1 resolve函数
Resolve函数主要根据语法树来生成具体的逻辑计划,进入resolve函数后,发现实际上resolve函数是OceanBase逻辑计划生成对外暴露的一个接口,其内部由一系列reolve*函数来构成,具体代码在build_plan.cpp中,声明如下:int resolve_create_table_stmt(ResultPlan* result_plan, ParseNode* node,uint64_t& query_id); int resolve_drop_table_stmt(ResultPlan* result_plan,ParseNode* node,uint64_t& query_id); int resolve_show_stmt(ResultPlan* result_plan, ParseNode* node,uint64_t& query_id); int resolve_prepare_stmt(ResultPlan* result_plan, ParseNode* node,uint64_t& query_id);
在进入resolve函数中,其核心的解析代码如下(build.cpp:1674):
if (ret == OB_SUCCESS && node != NULL) { switch (node->type_) { case T_STMT_LIST: { ret = resolve_multi_stmt(result_plan, node); break; } case T_SELECT: { ret = resolve_select_stmt(result_plan, node, query_id); break; } case T_DELETE: { ret = resolve_delete_stmt(result_plan, node, query_id); break; } case T_INSERT: { ret = resolve_insert_stmt(result_plan, node, query_id); break; }
这里并没有列举完全,实际上就是一系列的switch……case……语句,就是根据语法树节点的类型,调用相应的resolve函数,例如,如果解析的语句是SELECT语句,那么将会调用resolve_select_stmt来解析语法树。当然,oceanbase这里只对外暴露的resolve函数,并没有暴露resolve_delete_stmt,resolve_insert_stmt等相关的内部函数。
4.2.2 resolve具体实例
还是根据SQL语句:Update student set sex = "M" where name = "小明"
进行分析,在词法语法解析阶段,就可以解析SQL语句,生成具体的语法解析树,那么语法树在经过resovle函数之后,由于顶层节点的语法树节点的类型为T_UPDATE,在resolve函数代码中可以看到如下代码片段:
case T_UPDATE: { ret = resolve_update_stmt(result_plan, node, query_id); break; }
所以,Update的解析语句主要由resolve_update_stmt(dml_build_plan.cpp)这个函数来承担。
4.2.2.1 核心数据结构
进入resolve_update_stmt函数后,可以看到几个比较重要的类,其中一个是ObLogicPlan类,其声明如下所示:class ObLogicalPlan{ private: oceanbase::common::ObVector<ObBasicStmt*> stmts_; oceanbase::common::ObVector<ObSqlRawExpr*> exprs_; oceanbase::common::ObVector<ObRawExpr*> raw_exprs_store_; int64_t question_marks_count_; ObCurTimeType cur_time_fun_type_; uint64_t new_gen_tid_; uint64_t new_gen_cid_; uint64_t new_gen_qid_; uint64_t new_gen_eid_; int64_t new_gen_wid_; // when number }
关于ObLogicPlan就是描述整个逻辑计划的类,其数据结构包括一系列的语句以及表达式集合(stmt以及expr)剩下的就是各种表,列等的id。
进一步分析,逻辑计划表达式的类的描述如下所示:
class ObBasicStmt { private: StmtType stmt_type_; uint64_t query_id_; };
这个是基础语句类,描述的是语法树中一系列的由关键字解析出来的语句,StmtType是一个枚举类型,query_id_代表的是语句的id,StmtType的部分声明代码如下所示:
enum StmtType{T_NONE,T_SELECT,T_UPDATE,……}
当然,这仅仅只是一个基类,其具体的继承体系为:
实际上主要描述一个具体的语句是通过继承体系中的子类来表述的。一个比较重要的类ObStmt的描述如下:
class ObStmt : public ObBasicStmt{ protected: common::ObStringBuf* name_pool_; common::ObVector<TableItem> table_items_; common::ObVector<ColumnItem> column_items_; private: //uint64_t where_expr_id_; common::ObVector<uint64_t> where_expr_ids_; common::ObVector<uint64_t> when_expr_ids_; common::ObVector<uint64_t> when_func_ids_; ObQueryHint query_hint_; int64_t when_number_; common::ObRowDesc tables_hash_; }; }
一个具体的ObUpdateStmt类的声明如下所示(ob_update_stmt.h):
class ObUpdateStmt : public ObStmt { private: uint64_t table_id_; oceanbase::common::ObArray<uint64_t> update_columns_; oceanbase::common::ObArray<uint64_t> update_exprs_;
另外,一个具体的描述表的数据结构声明如下(ob_stmt.h):
struct TableItem { uint64_t table_id_; common::ObString table_name_; common::ObString alias_name_; TableType type_; uint64_t ref_id_; bool has_scan_columns_; };
一个具体的描述列属性的数据结构声明如下(ob_stmt.h):
struct ColumnItem { uint64_t column_id_; common::ObString column_name_; uint64_t table_id_; uint64_t query_id_; // This attribute is used by resolver, to mark if the column name is unique of all from tables bool is_name_unique_; bool is_group_based_; // TBD, we can not calculate resulte type now. common::ObObjType data_type_; };
ObRawExpr继承体系也是相对比较重要的数据结构,ObRawExpr继承体系是对具体的执行表达式的描述,表达式分为常量表达式, 一元引用表达式,二元引用表达式,一元操作符表达式,二元操作符表达式,三元操作符表达式,多元操作符表达式,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 ObRawExpr { };
好,这里再反观一下表达式的数据结构设计
class ObUpdateStmt : public ObStmt { private: uint64_t table_id_; oceanbase::common::ObArray<uint64_t> update_columns_; oceanbase::common::ObArray<uint64_t> update_exprs_;}
/**这里的table_id,update_columns,update_exprs_分别是表id,列id以及表达式id,而具体的表信息,列信息,以及表达式信息存储在
protected:
common::ObStringBuf* name_pool_;
common::ObVector<TableItem> table_items_;
common::ObVector<ColumnItem> column_items_;
这些数据结构之中。**/
4.2.2.2 解析流程
具体的解析流程还是以SQL语句:Update student set sex = "M" where name = "小明"
为主,那么解析过程的流程图也就是resolve_update_stmt()函数的执行流程大致如下:
1. 初始化逻辑计划树,生成一个唯一语句标志query_id()query_id = logical_plan->generate_query_id()
2. 将update_stmt加入逻辑计划树中ret = logical_plan->add_query(update_stmt)
3. 解析语句中的表ret = resolve_table(result_plan, update_stmt, table_node, table_id),具体的执行动作在resolve_table中,主要是通过schema_checker来验证表的正确性,生成table_item。
4. For循环遍历T_ASSIGN_LIST来读取列名和值对a)根据列名获取列b) 将该列存储到
update_stmt的vector<ColumnItem *>中,并将列引用id添加到
update_stmt的更新列列表
ObArray<uint64_t> update_columns_中;c) 解析值表达式;
(resolve_independ_expr -> resolve_expr->T_STRING)d) 将值表达式引用id添加到更新值列表
ObArray<uint64_t> update_exprs_中去;
5. 解析where子句,将表达式id放入ObVector where_expr_ids_
6. 解析when子句,将表达式id放入when_expr_ids_中
7. 解析hints
4.3 总结
简单来说,逻辑计划要弄清楚,这条SQL可以分解为几条Stmt,每条Stmt包含了哪些表,字段和表达式。在此基础上,如果是insert的stmt,要加上设置了哪些值,如果是update的stmt,要加上需要更新的列和对应的值等等。相关文章推荐
- 从源码安装Mysql/Percona 5.5
- 新注册
- 四大漏洞入侵博客
- 浅析Ruby的源代码布局及其编程风格
- 在Ubuntu系统上安装Ghost博客平台的教程
- asp.net 抓取网页源码三种实现方法
- JS小游戏之仙剑翻牌源码详解
- JS小游戏之宇宙战机源码详解
- jQuery源码分析之jQuery中的循环技巧详解
- 本人自用的global.js库源码分享
- java中原码、反码与补码的问题分析
- PHP多用户博客系统分析[想做多用户博客的朋友,需要了解]第1/3页
- PHP网页游戏学习之Xnova(ogame)源码解读(六)
- C#获取网页HTML源码实例
- PHP网页游戏学习之Xnova(ogame)源码解读(八)
- PHP网页游戏学习之Xnova(ogame)源码解读(四)
- 基于CakePHP实现的简单博客系统实例
- 基于jsp+servlet实现的简单博客系统实例(附源码)
- JS小游戏之极速快跑源码详解
- JS小游戏之象棋暗棋源码详解