由于每个语法对应一个结构体,因此在PostgreSQL中存在很多类似的结构体,包括SelectStmt,InsertStmt,DeleteStmt等。最终这些结构体还会被统一转换成Query结构体。即Query是统一的语法树结构体。
在PostgreSQL中,示例中的SQL语法树可表示为:
|--UpdateStmt
|--type: T_UpdateStmt
|--relation: student
|--targetList:
|--targest[0]:
|--name: sex
|--val: "M"
|--whereClause:
|--expr: =
|--left: name
|--right: "小明"
RedBase的语法树的节点设计
RedBase是斯坦福的数据库系统实现这门课程(cs346)的一个项目。RedBase比起PostgreSQL,OceanBase这样的复杂数据库而言,十分的简单。但是其语法树的节点设计与其他数据库不同,因此提出来做对比。
typedef struct node{
NODEKIND kind;/*枚举类型*/
union{
/* SM component nodes */
/* create table node */
struct{
char *relname;
struct node *attrlist;
} CREATETABLE;
/*此处省略n多个结构体...*/
/* QL component nodes */
/* query node */
...
/* update node */
struct{
char *relname; /* 关系名 */
struct node *relattr; /* 属性 */
struct node *relorvalue; /* 修改后值 */
struct node *conditionlist; /* 条件列表 */
} UPDATE;
/*此处省略n多个结构体...*/
} u;
} NODE;
RedBase数据库的语法树结构体只有一个,就是NODE,但是这个NODE结构体的声明有150多行(^-^).NODE包括一个枚举类型,作用于PostgreSQL中的type一样。所有的语法结构如UPDATE,SELECT,CREATETABLE等构成巨大的联合体。针对Update语句的结构体包括了关系名,属性,修改后的值,条件列表等字段,显然这种设计只能支持简单的Update语句。
RedBase采用“巨型”联合体取代PostgreSQL中的多个结构体,免去了类型转换(语法结构体到Node*的转换)。如果把PostgreSQL语法树节点看成是“继承”结构,那么RedBase的语法树节点可以看成是“组合”结构。
在RedBase中,示例中的SQL语法树可表示为:
|--NODE:
|--kind: N_UPDATE
|--u:UPDATE
|--relname: student
|--relattr:
|--kind: N_RELATTR
|--u:RELATTR
|--relname: (null)
|--attrname: sex
|--relorvalue:
|--kind: N_RELATTR_OR_VALUE
|--u:RELATTR_OR_VALUE
|--relattr: (null)
|--value:
|--kind:N_VALUE
|--u:VALUE
|--sval = "M"
|--conditionlist:
|--kind:N_LIST
|--u: LIST
|--next: (null)
|--curr:
|--kind: N_CONDITION
|--u: CONDITION
|--lhsRelattr:
|--kind: N_RELATTR
|--u:RELATTR
|--relname: (null)
|--attrname: name
|--op:
|--kind: N_EQ
|--rhsRelattr:(null)
|--rhsValue:
|--kind:N_VALUE
|--u:VALUE
|--sval = "M"
OceanBase的语法树的节点设计
OceanBase 的语法树节点结构体也只有一个,该结构体包括一个枚举类型变量type_,和PostgreSQL与RedBase一样,代表该结构体对应的类型。还有两组属性,对应终止符节点,只能使用vakue_和str_value_两个字段,分别对应64位整形值和字符串值;非终止符节点使用最后两个字段,num_child_表示子节点的个数,children_指向子节点数组的首地址。
typedef struct _ParseNode
{
ObItemType type_;
/* 终止符节点的真实的值 */
int64_t value_;
const char* str_value_;
/* 非终止符节点的孩子节点*/
int32_t num_child_; /*孩子节点的个数*/
struct _ParseNode** children_;
// BuildPlanFunc m_fnBuildPlan;
} ParseNode;
对应一个节点而言,要么是终止符节点要么是非终止符节点,它只会使用两组属性中的一组。int,long,float,double,string等都是终止符类型,可以看出int,long都是用64位整形int64表示。float,double,string则用char *字符串表示。终止符的num_child_为0,children_为null.
PostgreSQL的子节点都是有名字的子节点,可以使用名字进行访问,如在PostgreSQL中,Update语句的where条件可以通过 updatestmt.whereClause 来访问 。 但在OceanBase中不行 , 所有的子节点都是匿名的 , 只能通过下标来访问。
打个比方,在PostgreSQL和RedBase中,孩子是有名字的,可以叫小明、小红等,根据名字你大概可以知道这个孩子是男是女;但是在OceanBase家,他们分别叫老大,老二,老三,听名字完全听不出是男是女的。OceanBase家有点不讲究^-^。
可以在运行时查看语法树的结构,也可以在代码中可以推各个子节点代表的类型,但是不如PostgreSQL和RedBase方便。在sql_parser.y文件中,定义了SQL的语法规则,同时也规定了各种类型的子节点的结构。
update_stmt:
UPDATE relation_factor SET update_asgn_list opt_where
{
ParseNode* assign_list = merge_tree(result->malloc_pool_, T_ASSIGN_LIST, $4);
$$ = new_non_terminal_node(result->malloc_pool_, T_UPDATE, 3, $2, assign_list, $5);
}
;
从上述代码可以看出,Up