您的位置:首页 > 数据库

OceanBase SQL解析源码分析(三)

2016-03-02 14:28 756 查看
原创文章,转载请注明: 转载自 镜中影的技术博客

本文链接地址: 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,要加上需要更新的列和对应的值等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  oceanbase 源码 博客