淘宝数据库OceanBase SQL编译器部分 源码阅读--生成逻辑计划

http://blog.csdn.net/qq910894904/article/details/29364871



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中的逻辑计划的结构(省略无关方法和变量).

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class ObLogicalPlan  
  2.     {  
  3.       //...    
  4.       oceanbase::common::ObVector<ObStmt*> stmts_; //存储该逻辑计划的所有stmt  
  5.       oceanbase::common::ObVector<ObSqlRawExpr*> exprs_; //逻辑计划的所有表达式  
  6.       oceanbase::common::ObVector<ObRawExpr*> raw_exprs_store_;//存储逻辑计划的所有表达式  
  7.       uint64_t  new_gen_tid_;//用于生成新的tid  
  8.       uint64_t  new_gen_cid_;//用于生成新的cid  
  9.       uint64_t  new_gen_qid_;//用于生成新的qid  
  10.       uint64_t  new_gen_eid_;//用于生成新的eid  
  11.     };  


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 逻辑计划中表的定义

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct TableItem  
  2. {  
  3.     uint64_t    table_id_;  
  4.     common::ObString    table_name_;  
  5.     common::ObString    alias_name_;  
  6.     TableType   type_;  
  7.   
  8.     uint64_t     ref_id_;  
  9. };  


table_id_唯一标识一个关系表,其类型分为基本表,引用表和子查询关系。
对同一个实体表,ref_id_table_id_相同; 如果是一个引用别名的表,则table_id_是新生成的,ref_id_与这个表真正的table_id_相同;如果是一个子查询,则table_id_是新生成的,ref_id_是对子查询的引用。

对同一个实体表,它在所有线程使用的table_id_都是相同的;如果是生成的标识,则仅在该线程会话期间是唯一的。

2.2 逻辑计划中列的定义

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct ColumnItem  
  2. {  
  3.     uint64_t    column_id_;  
  4.     common::ObString    column_name_;  
  5.     uint64_t    table_id_;  
  6.     uint64_t    query_id_;  
  7.   
  8.     bool        is_name_unique_;  
  9.     bool        is_group_based_;  
  10.   
  11.     common::ObObjType     data_type_;  
  12. };  


column_id_唯一标识一个列,table_id_query_id_为该列所属的关系表和stmt。is_name_unique_仅用在解析逻辑计划期间,标记该列的名称是否在所有表的字段中都是唯一的。is_group_based_标记该列是否用于分组。data_type_标识该列的数据类型。

2.3 逻辑计划中的表达式的定义

逻辑计划的中表达式有多种类型,其基类为ObRawExpr.包括两个成员变量,type_表示表达式的类型,result_type_表示表达式值的类型。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class ObRawExpr  
  2.  {  
  3.  //省略其他方法  
  4.  private:  
  5.     ObItemType  type_;  
  6.     common::ObObjType result_type_;  
  7.  }  


表达式分为常量表达式, 一元引用表达式,二元引用表达式,一元操作符表达式,二元操作符表达式,三元操作符表达式,多元操作符表达式,case操作符表达式,聚集函数表达式,系统函数表达式,SQL原生表达式等。继承关系如下。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. namespace sql  
  2. {  
  3.      //原生表达式基类  
  4.     class ObRawExpr  
  5.     //常量表达式  
  6.     class ObConstRawExpr        : public ObRawExpr  
  7.     //一元引用表达式  
  8.     class ObUnaryRefRawExpr     : public ObRawExpr  
  9.     //二元引用表达式  
  10.     class ObBinaryRefRawExpr    : public ObRawExpr  
  11.     //一元操作符表达式  
  12.     class ObUnaryOpRawExpr      : public ObRawExpr  
  13.     //二元操作符表达式  
  14.     class ObBinaryOpRawExpr     : public ObRawExpr  
  15.      //三元操作符表达式  
  16.     class ObTripleOpRawExpr     : public ObRawExpr  
  17.     //多元操作符表达式  
  18.     class ObMultiOpRawExpr      : public ObRawExpr  
  19.     //case操作符表达式  
  20.     class ObCaseOpRawExpr       : public ObRawExpr  
  21.     //聚集函数表达式  
  22.     class ObAggFunRawExpr       : public ObRawExpr  
  23.     //系统函数表达式  
  24.     class ObSysFunRawExpr       : public ObRawExpr  
  25.     //SQL原生表达式  
  26.     class ObSqlRawExpr          : public ObRawExpr  
  27.  };  
  28.   
  29.  class ObRawExpr  
  30.  {  
  31.   
  32.  };  


在ObLogicalPlan中,存储使用的是vector<ObRawExpr *>,使用时转为vector<ObSqlRawExpr *>.

2.4 逻辑计划中的Stmt的定义

Stmt表示一个单独的查询所包含的内容,一个逻辑计划可以包含多个Stmt.

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class ObStmt  
  2.    {  
  3.    /*省略部分内容...*/  
  4.    protected:  
  5.      common::ObVector<TableItem>    table_items_;  
  6.      common::ObVector<ColumnItem>   column_items_;  
  7.   
  8.    private:  
  9.      StmtType  type_;  
  10.      uint64_t  query_id_;  
  11.      //uint64_t  where_expr_id_;  
  12.      common::ObVector<uint64_t>     where_expr_ids_;  
  13.   
  14.    };  


Stmt包括了一个查询所有的表table_items_,列column_items_,表达式where_expr_ids_和一个唯一的查询标识query_id_。注意这里存储的只有表达式的id,而不是表达式的实际内容。

从上述的定义总结来看,一个逻辑计划拥有多条查询实例Stmt和多个表达式,一个查询实例Stmt包含了多个表和多个列及所需表达式的引用。表,列,表达式,查询实例都有唯一的标识符进行标记。

ObLogicalPlan
----ObStmt : 1...n
--------TableItem : 0...n
--------ColnumItem : 0...n
--------expr_id_ref : 0...n
----ObRawExpr : 0...n

三、 如何制定逻辑计划?

3.1 reslove系列解析函数

制定逻辑计划的源码在build_plan.h和build_plan.cpp中,在OceanBase0.4中,则增加了dml_build_plan.h和dml_build_plan.cpp。制定逻辑对外提供的接口只有两个,解析函数resolove和销毁函数destroy_plan,其他的为自用,可以浏览下其函数声明及用途,基本的结构就是这样,因为目前OceanBase中支持的SQL语句不多,相应的解析函数也比较少,还有一些没有完成,可以想见未来还会添加更多的函数。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //解析多重查询  
  2. int resolve_multi_stmt(ResultPlan* result_plan, ParseNode* node)  
  3. //解析独立表达式  
  4. int resolve_independ_expr()  
  5. //解析and表达式  
  6. int resolve_and_exprs()  
  7. //解析表达式  
  8. int resolve_expr()  
  9. //解析聚集函数  
  10. int resolve_agg_func()  
  11. //解析join表连接  
  12. int resolve_joined_table()  
  13. //解析表  
  14. int resolve_table()  
  15. //解析from子句  
  16. int resolve_from_clause()  
  17. //解析列  
  18. int resolve_table_columns()  
  19. //解析*  
  20. int resolve_star()  
  21. //解析select的投影列表  
  22. int resolve_select_clause()  
  23. //解析where子句  
  24. int resolve_where_clause()  
  25. //解析group by子句  
  26. int resolve_group_clause()  
  27. //解析having子句  
  28. int resolve_having_clause()  
  29. //解析order子句  
  30. int resolve_order_clause()  
  31. //解析limit子句  
  32. int resolve_limit_clause()  
  33. //解析select查询  
  34. int resolve_select_stmt()  
  35. //解析delete查询  
  36. int resolve_delete_stmt()  
  37. //解析insert的插入列  
  38. int resolve_insert_columns()  
  39. //解析intsert查询的插入值  
  40. int resolve_insert_values()  
  41. //解析insert查询  
  42. int resolve_insert_stmt()  
  43. //解析update查询  
  44. int resolve_update_stmt()  
  45. //解析函数。对外提供  
  46. int resolve(ResultPlan* result_plan, ParseNode* node)  
  47. //销毁函数,对外提供  
  48. extern void destroy_plan(ResultPlan* result_plan)  


resolve函数根据语法树node的类型调用不同的查询解析实例。以下是部分代码摘抄:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int resolve(ResultPlan* result_plan, ParseNode* node)  
  2. {  
  3.    /*...*/  
  4.   
  5.   uint64_t query_id = OB_INVALID_ID;  
  6.   if (ret == OB_SUCCESS && node != NULL)  
  7.   {  
  8.     switch (node->type_)  
  9.     {  
  10.       case T_STMT_LIST:  
  11.       {  
  12.         ret = resolve_multi_stmt(result_plan, node);  
  13.         break;  
  14.       }  
  15.       case T_SELECT:  
  16.       {  
  17.         ret = resolve_select_stmt(result_plan, node, query_id);  
  18.         break;  
  19.       }  
  20.       case T_DELETE:  
  21.       {  
  22.         ret = resolve_delete_stmt(result_plan, node, query_id);  
  23.         break;  
  24.       }  
  25.       case T_INSERT:  
  26.       {  
  27.         ret = resolve_insert_stmt(result_plan, node, query_id);  
  28.         break;  
  29.       }  
  30.       case T_UPDATE:  
  31.       {  
  32.         ret = resolve_update_stmt(result_plan, node, query_id);  
  33.         break;  
  34.       }  
  35.       default:  
  36.         ret = OB_ERROR;  
  37.         break;  
  38.     };  
  39.   }  
  40.   return ret;  
  41. }  
  42. int resolve_update_stmt(ResultPlan* result_plan, ParseNode* node, uint64_t& query_id)  
  43. {  
  44.   int& ret = result_plan->err_stat_.err_code_ = OB_SUCCESS;  
  45.   uint64_t table_id = OB_INVALID_ID;  
  46.   
  47.   query_id = OB_INVALID_ID;  
  48.   
  49.   ObLogicalPlan* logical_plan  logical_plan = new(logical_plan) ObLogicalPlan(name_pool);  
  50.   
  51.   result_plan->plan_tree_ = logical_plan;  
  52.   
  53.   
  54.   update_stmt = new(update_stmt) ObUpdateStmt(name_pool);  
  55.   
  56.   query_id = logical_plan->generate_query_id();  
  57.   //为update_stmt设置新的标识qid  
  58.   update_stmt->set_query_id(query_id);  
  59.   
  60.   logical_plan->add_query(update_stmt);  
  61.   
  62.   ParseNode* table_node = node->children_[0];  
  63.   //解析表    
  64.   ret = resolve_table(result_plan, update_stmt, table_node, table_id);  
  65.   
  66.   update_stmt->set_update_table(table_id);  
  67.   
  68.   ParseNode* assign_list = node->children_[1];  
  69.   
  70.   uint64_t ref_id;  
  71.   ColumnItem *column_item = NULL;  
  72.   //解析要更新的列表,如:update student set sex="M",grade="2" where name = "xiaoming";  
  73.   for (int32_t i = 0; ret == OB_SUCCESS && i < assign_list->num_child_; i++)  
  74.   {  
  75.       ParseNode* assgin_node = assign_list->children_[i];  
  76.   
  77.       /* resolve target column */  
  78.       ParseNode* column_node = assgin_node->children_[0];  
  79.   
  80.       ObString column_name;  
  81.             column_name.assign_ptr(  
  82.                 (char*)(column_node->str_value_),   
  83.                 static_cast<int32_t>(strlen(column_node->str_value_))  
  84.                 );  
  85.       //1 根据列名获取列  
  86.       column_item = update_stmt->get_column_item(NULL, column_name);  
  87.       //2 解析列到vector<ColumnItem *>  
  88.       ret = update_stmt->add_column_item(*result_plan, column_name, NULL, &column_item);  
  89.       //3 增加列引用到update_stmt    
  90.       ret = update_stmt->add_update_column(column_item->column_id_);  
  91.   
  92.             /* resolve new value expression */  
  93.       //4 解析值表达式  
  94.       ParseNode* expr = assgin_node->children_[1];  
  95.       ret = resolve_independ_expr(result_plan, update_stmt, expr, ref_id, T_UPDATE_LIMIT);  
  96.       //5 添加值表达式引用到update_stmt  
  97.       ret = update_stmt->add_update_expr(ref_id)  
  98.   }  
  99.   //解析where子句  
  100.   ret = resolve_where_clause(result_plan, update_stmt, node->children_[2]);  
  101.   
  102.   return ret;  
  103. }  


我们仍旧以update语句为例。上面是根据源代码整理的逻辑,不是源码,主要是为了理清思路。

  1. 首先是创建一个新的查询update_stmt,并为其生成一个独立的查询标识qid
  2. 解析语句中的表,并将表的标识tid添加到update_stmt的引用列表
  3. 利用for循环逐个解析要更新的列-值对: 
    (1). 根据列名获取列; 
    (2). 将该列存储到update_stmtvector<ColumnItem *>中,并将列引用id添加到update_stmt的更新列列表ObArray<uint64_t> update_columns_中; 
    (3). 解析值表达式; 
    (4). 将值表达式引用id添加到更新值列表ObArray<uint64_t> update_exprs_中去;
  4. 解析where子句.

3.2 如何解析表和列?

通过上面我们知道,逻辑计划的解析的一个重要内容就是要确定查询stmt,表,列,表达式的标识.查询和表达式的标识id都可以在解析的时候生成。因为这两项不是线程共有的,但是表和列是持久的数据,可以跨线程使用同样的id。这些表和列的信息由谁来管理?

3.2.1 使用Schema

追根溯源,你会发现实体表和列的id是在ob_schema.cpp中获取的。什么是schema?schema就是数据库对象的一个集合。网上有一个很形象的比喻,我稍微做了点改动:

什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User?我们可以可以把Database看作是一个大仓库,仓库分了很多很多的房间,Schema就是其中的房间,一个Schema代表一个房间,Table可以看作是每个Schema中的柜子,行和列就是柜子中的格子。User就是房间的主人。

OceanBase要求schema强类型约束,也就是要预先定义好schema。这样可以方便的进行各种online ddl操作。

OceanBase的表和列信息存储在Schema的一个hash_map中。关于Schema 以后再深入了解。

四、总结

制定逻辑计划,最关键的是要理解逻辑计划的设计目标,其中最主要的内容就是确定逻辑计划中使用到的查询Stmt,表,列,表达式,并为它们生成或设置唯一标识,确保在同一个逻辑计划中是不相同的。制定逻辑计划的函数只要是reslove系列函数。而表和列的信息存储在Schema中一个hash_map中。



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中的逻辑计划的结构(省略无关方法和变量).

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class ObLogicalPlan  
  2.     {  
  3.       //...    
  4.       oceanbase::common::ObVector<ObStmt*> stmts_; //存储该逻辑计划的所有stmt  
  5.       oceanbase::common::ObVector<ObSqlRawExpr*> exprs_; //逻辑计划的所有表达式  
  6.       oceanbase::common::ObVector<ObRawExpr*> raw_exprs_store_;//存储逻辑计划的所有表达式  
  7.       uint64_t  new_gen_tid_;//用于生成新的tid  
  8.       uint64_t  new_gen_cid_;//用于生成新的cid  
  9.       uint64_t  new_gen_qid_;//用于生成新的qid  
  10.       uint64_t  new_gen_eid_;//用于生成新的eid  
  11.     };  


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 逻辑计划中表的定义

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct TableItem  
  2. {  
  3.     uint64_t    table_id_;  
  4.     common::ObString    table_name_;  
  5.     common::ObString    alias_name_;  
  6.     TableType   type_;  
  7.   
  8.     uint64_t     ref_id_;  
  9. };  


table_id_唯一标识一个关系表,其类型分为基本表,引用表和子查询关系。
对同一个实体表,ref_id_table_id_相同; 如果是一个引用别名的表,则table_id_是新生成的,ref_id_与这个表真正的table_id_相同;如果是一个子查询,则table_id_是新生成的,ref_id_是对子查询的引用。

对同一个实体表,它在所有线程使用的table_id_都是相同的;如果是生成的标识,则仅在该线程会话期间是唯一的。

2.2 逻辑计划中列的定义

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct ColumnItem  
  2. {  
  3.     uint64_t    column_id_;  
  4.     common::ObString    column_name_;  
  5.     uint64_t    table_id_;  
  6.     uint64_t    query_id_;  
  7.   
  8.     bool        is_name_unique_;  
  9.     bool        is_group_based_;  
  10.   
  11.     common::ObObjType     data_type_;  
  12. };  


column_id_唯一标识一个列,table_id_query_id_为该列所属的关系表和stmt。is_name_unique_仅用在解析逻辑计划期间,标记该列的名称是否在所有表的字段中都是唯一的。is_group_based_标记该列是否用于分组。data_type_标识该列的数据类型。

2.3 逻辑计划中的表达式的定义

逻辑计划的中表达式有多种类型,其基类为ObRawExpr.包括两个成员变量,type_表示表达式的类型,result_type_表示表达式值的类型。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class ObRawExpr  
  2.  {  
  3.  //省略其他方法  
  4.  private:  
  5.     ObItemType  type_;  
  6.     common::ObObjType result_type_;  
  7.  }  


表达式分为常量表达式, 一元引用表达式,二元引用表达式,一元操作符表达式,二元操作符表达式,三元操作符表达式,多元操作符表达式,case操作符表达式,聚集函数表达式,系统函数表达式,SQL原生表达式等。继承关系如下。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. namespace sql  
  2. {  
  3.      //原生表达式基类  
  4.     class ObRawExpr  
  5.     //常量表达式  
  6.     class ObConstRawExpr        : public ObRawExpr  
  7.     //一元引用表达式  
  8.     class ObUnaryRefRawExpr     : public ObRawExpr  
  9.     //二元引用表达式  
  10.     class ObBinaryRefRawExpr    : public ObRawExpr  
  11.     //一元操作符表达式  
  12.     class ObUnaryOpRawExpr      : public ObRawExpr  
  13.     //二元操作符表达式  
  14.     class ObBinaryOpRawExpr     : public ObRawExpr  
  15.      //三元操作符表达式  
  16.     class ObTripleOpRawExpr     : public ObRawExpr  
  17.     //多元操作符表达式  
  18.     class ObMultiOpRawExpr      : public ObRawExpr  
  19.     //case操作符表达式  
  20.     class ObCaseOpRawExpr       : public ObRawExpr  
  21.     //聚集函数表达式  
  22.     class ObAggFunRawExpr       : public ObRawExpr  
  23.     //系统函数表达式  
  24.     class ObSysFunRawExpr       : public ObRawExpr  
  25.     //SQL原生表达式  
  26.     class ObSqlRawExpr          : public ObRawExpr  
  27.  };  
  28.   
  29.  class ObRawExpr  
  30.  {  
  31.   
  32.  };  


在ObLogicalPlan中,存储使用的是vector<ObRawExpr *>,使用时转为vector<ObSqlRawExpr *>.

2.4 逻辑计划中的Stmt的定义

Stmt表示一个单独的查询所包含的内容,一个逻辑计划可以包含多个Stmt.

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class ObStmt  
  2.    {  
  3.    /*省略部分内容...*/  
  4.    protected:  
  5.      common::ObVector<TableItem>    table_items_;  
  6.      common::ObVector<ColumnItem>   column_items_;  
  7.   
  8.    private:  
  9.      StmtType  type_;  
  10.      uint64_t  query_id_;  
  11.      //uint64_t  where_expr_id_;  
  12.      common::ObVector<uint64_t>     where_expr_ids_;  
  13.   
  14.    };  


Stmt包括了一个查询所有的表table_items_,列column_items_,表达式where_expr_ids_和一个唯一的查询标识query_id_。注意这里存储的只有表达式的id,而不是表达式的实际内容。

从上述的定义总结来看,一个逻辑计划拥有多条查询实例Stmt和多个表达式,一个查询实例Stmt包含了多个表和多个列及所需表达式的引用。表,列,表达式,查询实例都有唯一的标识符进行标记。

ObLogicalPlan
----ObStmt : 1...n
--------TableItem : 0...n
--------ColnumItem : 0...n
--------expr_id_ref : 0...n
----ObRawExpr : 0...n

三、 如何制定逻辑计划?

3.1 reslove系列解析函数

制定逻辑计划的源码在build_plan.h和build_plan.cpp中,在OceanBase0.4中,则增加了dml_build_plan.h和dml_build_plan.cpp。制定逻辑对外提供的接口只有两个,解析函数resolove和销毁函数destroy_plan,其他的为自用,可以浏览下其函数声明及用途,基本的结构就是这样,因为目前OceanBase中支持的SQL语句不多,相应的解析函数也比较少,还有一些没有完成,可以想见未来还会添加更多的函数。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //解析多重查询  
  2. int resolve_multi_stmt(ResultPlan* result_plan, ParseNode* node)  
  3. //解析独立表达式  
  4. int resolve_independ_expr()  
  5. //解析and表达式  
  6. int resolve_and_exprs()  
  7. //解析表达式  
  8. int resolve_expr()  
  9. //解析聚集函数  
  10. int resolve_agg_func()  
  11. //解析join表连接  
  12. int resolve_joined_table()  
  13. //解析表  
  14. int resolve_table()  
  15. //解析from子句  
  16. int resolve_from_clause()  
  17. //解析列  
  18. int resolve_table_columns()  
  19. //解析*  
  20. int resolve_star()  
  21. //解析select的投影列表  
  22. int resolve_select_clause()  
  23. //解析where子句  
  24. int resolve_where_clause()  
  25. //解析group by子句  
  26. int resolve_group_clause()  
  27. //解析having子句  
  28. int resolve_having_clause()  
  29. //解析order子句  
  30. int resolve_order_clause()  
  31. //解析limit子句  
  32. int resolve_limit_clause()  
  33. //解析select查询  
  34. int resolve_select_stmt()  
  35. //解析delete查询  
  36. int resolve_delete_stmt()  
  37. //解析insert的插入列  
  38. int resolve_insert_columns()  
  39. //解析intsert查询的插入值  
  40. int resolve_insert_values()  
  41. //解析insert查询  
  42. int resolve_insert_stmt()  
  43. //解析update查询  
  44. int resolve_update_stmt()  
  45. //解析函数。对外提供  
  46. int resolve(ResultPlan* result_plan, ParseNode* node)  
  47. //销毁函数,对外提供  
  48. extern void destroy_plan(ResultPlan* result_plan)  


resolve函数根据语法树node的类型调用不同的查询解析实例。以下是部分代码摘抄:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int resolve(ResultPlan* result_plan, ParseNode* node)  
  2. {  
  3.    /*...*/  
  4.   
  5.   uint64_t query_id = OB_INVALID_ID;  
  6.   if (ret == OB_SUCCESS && node != NULL)  
  7.   {  
  8.     switch (node->type_)  
  9.     {  
  10.       case T_STMT_LIST:  
  11.       {  
  12.         ret = resolve_multi_stmt(result_plan, node);  
  13.         break;  
  14.       }  
  15.       case T_SELECT:  
  16.       {  
  17.         ret = resolve_select_stmt(result_plan, node, query_id);  
  18.         break;  
  19.       }  
  20.       case T_DELETE:  
  21.       {  
  22.         ret = resolve_delete_stmt(result_plan, node, query_id);  
  23.         break;  
  24.       }  
  25.       case T_INSERT:  
  26.       {  
  27.         ret = resolve_insert_stmt(result_plan, node, query_id);  
  28.         break;  
  29.       }  
  30.       case T_UPDATE:  
  31.       {  
  32.         ret = resolve_update_stmt(result_plan, node, query_id);  
  33.         break;  
  34.       }  
  35.       default:  
  36.         ret = OB_ERROR;  
  37.         break;  
  38.     };  
  39.   }  
  40.   return ret;  
  41. }  
  42. int resolve_update_stmt(ResultPlan* result_plan, ParseNode* node, uint64_t& query_id)  
  43. {  
  44.   int& ret = result_plan->err_stat_.err_code_ = OB_SUCCESS;  
  45.   uint64_t table_id = OB_INVALID_ID;  
  46.   
  47.   query_id = OB_INVALID_ID;  
  48.   
  49.   ObLogicalPlan* logical_plan  logical_plan = new(logical_plan) ObLogicalPlan(name_pool);  
  50.   
  51.   result_plan->plan_tree_ = logical_plan;  
  52.   
  53.   
  54.   update_stmt = new(update_stmt) ObUpdateStmt(name_pool);  
  55.   
  56.   query_id = logical_plan->generate_query_id();  
  57.   //为update_stmt设置新的标识qid  
  58.   update_stmt->set_query_id(query_id);  
  59.   
  60.   logical_plan->add_query(update_stmt);  
  61.   
  62.   ParseNode* table_node = node->children_[0];  
  63.   //解析表    
  64.   ret = resolve_table(result_plan, update_stmt, table_node, table_id);  
  65.   
  66.   update_stmt->set_update_table(table_id);  
  67.   
  68.   ParseNode* assign_list = node->children_[1];  
  69.   
  70.   uint64_t ref_id;  
  71.   ColumnItem *column_item = NULL;  
  72.   //解析要更新的列表,如:update student set sex="M",grade="2" where name = "xiaoming";  
  73.   for (int32_t i = 0; ret == OB_SUCCESS && i < assign_list->num_child_; i++)  
  74.   {  
  75.       ParseNode* assgin_node = assign_list->children_[i];  
  76.   
  77.       /* resolve target column */  
  78.       ParseNode* column_node = assgin_node->children_[0];  
  79.   
  80.       ObString column_name;  
  81.             column_name.assign_ptr(  
  82.                 (char*)(column_node->str_value_),   
  83.                 static_cast<int32_t>(strlen(column_node->str_value_))  
  84.                 );  
  85.       //1 根据列名获取列  
  86.       column_item = update_stmt->get_column_item(NULL, column_name);  
  87.       //2 解析列到vector<ColumnItem *>  
  88.       ret = update_stmt->add_column_item(*result_plan, column_name, NULL, &column_item);  
  89.       //3 增加列引用到update_stmt    
  90.       ret = update_stmt->add_update_column(column_item->column_id_);  
  91.   
  92.             /* resolve new value expression */  
  93.       //4 解析值表达式  
  94.       ParseNode* expr = assgin_node->children_[1];  
  95.       ret = resolve_independ_expr(result_plan, update_stmt, expr, ref_id, T_UPDATE_LIMIT);  
  96.       //5 添加值表达式引用到update_stmt  
  97.       ret = update_stmt->add_update_expr(ref_id)  
  98.   }  
  99.   //解析where子句  
  100.   ret = resolve_where_clause(result_plan, update_stmt, node->children_[2]);  
  101.   
  102.   return ret;  
  103. }  


我们仍旧以update语句为例。上面是根据源代码整理的逻辑,不是源码,主要是为了理清思路。

  1. 首先是创建一个新的查询update_stmt,并为其生成一个独立的查询标识qid
  2. 解析语句中的表,并将表的标识tid添加到update_stmt的引用列表
  3. 利用for循环逐个解析要更新的列-值对: 
    (1). 根据列名获取列; 
    (2). 将该列存储到update_stmtvector<ColumnItem *>中,并将列引用id添加到update_stmt的更新列列表ObArray<uint64_t> update_columns_中; 
    (3). 解析值表达式; 
    (4). 将值表达式引用id添加到更新值列表ObArray<uint64_t> update_exprs_中去;
  4. 解析where子句.

3.2 如何解析表和列?

通过上面我们知道,逻辑计划的解析的一个重要内容就是要确定查询stmt,表,列,表达式的标识.查询和表达式的标识id都可以在解析的时候生成。因为这两项不是线程共有的,但是表和列是持久的数据,可以跨线程使用同样的id。这些表和列的信息由谁来管理?

3.2.1 使用Schema

追根溯源,你会发现实体表和列的id是在ob_schema.cpp中获取的。什么是schema?schema就是数据库对象的一个集合。网上有一个很形象的比喻,我稍微做了点改动:

什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User?我们可以可以把Database看作是一个大仓库,仓库分了很多很多的房间,Schema就是其中的房间,一个Schema代表一个房间,Table可以看作是每个Schema中的柜子,行和列就是柜子中的格子。User就是房间的主人。

OceanBase要求schema强类型约束,也就是要预先定义好schema。这样可以方便的进行各种online ddl操作。

OceanBase的表和列信息存储在Schema的一个hash_map中。关于Schema 以后再深入了解。

四、总结

制定逻辑计划,最关键的是要理解逻辑计划的设计目标,其中最主要的内容就是确定逻辑计划中使用到的查询Stmt,表,列,表达式,并为它们生成或设置唯一标识,确保在同一个逻辑计划中是不相同的。制定逻辑计划的函数只要是reslove系列函数。而表和列的信息存储在Schema中一个hash_map中。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值