Note:执行完SQL语句后需要输出执行所用的时间,在项目验收中将会用于考察索引效果,另外对于查询语句,需要输出共查询到多少条记录,对于插入/删除/更新语句,需要输出影响了多少条记录(参考MySQL输出)。
更正:
源代码src/parser/syntax_tree.c中的 CreateSyntaxNode函数更正,详见PR。
#5.1 实验概述
Executor(执行器)的主要功能是根据解释器(Parser)生成的语法树,通过Catalog Manager 提供的信息生成执行计划,并调用 Record Manager、Index Manager 和 Catalog Manager 提供的相应接口进行执行,最后通过执行上下文ExecuteContext�将执行结果返回给上层模块。
考虑到同学们尚未接触到编译原理的相关知识,在本实验中,我们已经为同学们设计好MiniSQL中的Parser模块,与Parser模块的相关代码如下:
src/include/parser/minisql.l:SQL的词法分析规则;src/include/parser/minisql.y:SQL的文法分析规则;src/include/parser/minisql_lex.h:flex(lex)根据词法规则自动生成的代码;src/include/parser/minisql_yacc.h:bison(yacc)根据文法规则自动生成的代码;src/include/parser/parser.h:Parser模块相关的函数定义,供词法分析器和语法分析器调用存储分析结果,同时可供执行器调用获取语法树根结点;src/include/parser/syntax_tree.h:语法树相关定义,语法树各个结点的类型同样在SyntaxNodeType中被定义。#5.1.1 语法树数据结构
以下是语法树(结点)的数据结构定义,每个结点都包含了一个唯一标识符
id_,唯一标识符在调用CreateSyntaxNode函数时生成(框架中已经给出实现)。type_表示语法树结点的类型,line_no_和col_no_表示该语法树结点对应的是SQL语句的第几行第几列,child_和next_分别表示该结点的子结点和兄弟结点,val_用作一些额外信息的存储(如在kNodeString类型的结点中,val_将用于存储该字符串的字面量)。/*** Syntax node definition used in abstract syntax tree.*/struct SyntaxNode {int id_; /** node id for allocated syntax node, used for debug */SyntaxNodeType type_; /** syntax node type */int line_no_; /** line number of this syntax node appears in sql */int col_no_; /** column number of this syntax node appears in sql */struct SyntaxNode *child_; /** children of this syntax node */struct SyntaxNode *next_; /** siblings of this syntax node, linked by a single linked list */char *val_; /** attribute value of this syntax node, use deep copy */};typedef struct SyntaxNode *pSyntaxNode;
举一个简单的例子,
select * from t1 where id = 1 and name = "str";这一条SQL语句生成的语法树如下。以根结点为例说明,kNodeSelect为结点的类型,(1,47)表示该结点在规约(reduce,编译原理中的术语)后位于行的第1行第47列(语句末),id(9)表示该结点的id_为9。
#5.2 解析语法树完成命令执行
Parser模块中目前能够支持以下类型的SQL语句。其中包含了一些在语法定义上正确,但在语义上错误的SQL语句(如Line 8~10)需要同学们在执行器中对这些特殊情况进行处理。此外涉及到事务开启、提交和回滚相关的
begin、commit和rollback命令可以不做实现。create database db0;drop database db0;show databases;use db0;show tables;create table t1(a int, b char(20) unique, c float, primary key(a, c));create table t1(a int, b char(0) unique, c float, primary key(a, c));create table t1(a int, b char(-5) unique, c float, primary key(a, c));create table t1(a int, b char(3.69) unique, c float, primary key(a, c));create table t1(a int, b char(-0.69) unique, c float, primary key(a, c));create table student(sno char(8),sage int,sab float unique,primary key (sno, sab));drop table t1;create index idx1 on t1(a, b);-- "btree" can be replaced with other index typescreate index idx1 on t1(a, b) using btree;drop index idx1;show indexes;select * from t1;select id, name from t1;select * from t1 where id = 1;-- note: use left associationselect * from t1 where id = 1 and name = "str";select * from t1 where id = 1 and name = "str" or age is null and bb not null;insert into t1 values(1, "aaa", null, 2.33);delete from t1;delete from t1 where id = 1 and amount = 2.33;update t1 set c = 3;update t1 set a = 1, b = "ccc" where b = 2.33;begin;commit;rollback;quit;execfile "a.txt";
在Parser模块调用
yyparse()(一个示例在src/main.cpp中)完成SQL语句解析后,将会得到语法树的根结点pSyntaxNode,将语法树根结点传入执行器ExecuteEngine(定义于src/include/executor/execute_engine.h)后,ExecuteEngine将会根据语法树根结点的类型,分发到对应的执行函数中,以完成不同类型SQL语句的执行。
在本节中,你需要实现ExecuteEngine中所有的执行函数,它们被声明为private类型的成员,即所有的执行过程对上层模块是隐藏的,上层模块只需要调用ExecuteEngine::execute()并传入语法树结点即可无感知地获取到执行结果。ExecuteEngine::ExecuteCreateDatabase(*ast, *context)ExecuteEngine::ExecuteDropDatabase(*ast, *context)ExecuteEngine::ExecuteShowDatabases(*ast, *context)ExecuteEngine::ExecuteUseDatabase(*ast, *context)ExecuteEngine::ExecuteShowTables(*ast, *context)ExecuteEngine::ExecuteCreateTable(*ast, *context)ExecuteEngine::ExecuteDropTable(*ast, *context)ExecuteEngine::ExecuteShowIndexes(*ast, *context)ExecuteEngine::ExecuteCreateIndex(*ast, *context)ExecuteEngine::ExecuteDropIndex(*ast, *context)ExecuteEngine::ExecuteSelect(*ast, *context)ExecuteEngine::ExecuteInsert(*ast, *context)ExecuteEngine::ExecuteDelete(*ast, *context)ExecuteEngine::ExecuteUpdate(*ast, *context)ExecuteEngine::ExecuteExecfile(*ast, *context)ExecuteEngine::ExecuteQuit(*ast, *context)ExecuteEngine::ExecuteTrxBegin(*ast, *context):事务相关,可不实现ExecuteEngine::ExecuteTrxCommit(*ast, *context):事务相关,可不实现ExecuteEngine::ExecuteTrxRollback(*ast, *context):事务相关,可不实现
Note: 执行结果上下文ExecuteContext中提供了部分可能需要用到的数据,同学们在实际编程的时候根据需要自行定义ExecuteContext即可。
/**
* ExecuteContext stores all the context necessary to run in the execute engine
* This struct is implemented by student self for necessary.
*
* eg: transaction info, execute result...
*/
struct ExecuteContext {
bool flag_quit_{false};
Transaction *txn_{nullptr};
};
#5.3 模块相关代码
src/main.cppsrc/include/executor/execute_engine.hsrc/executor/execute_engine.cpp
#5.4 开发提示
- 整个MiniSQL项目推荐在夏学期第7周前完成;
- 框架中已经给出了语法树的
PrintTree()方法,它能够打印语法树中的每一个结点(输出DOT格式),具体用法和之前的B+树打印类似,输出的结果放在可视化界面中可以用作调试。此外也可以使用GDB或IDE自带的调试工具在完成SQL语法分析后得到语法树的语句打上断点以进行调试。 - 如果需要更改语法和文法以支持新的SQL命令,可以在学习LEX和YACC的相关知识后,修改
src/include/parser/minisql.l和src/include/parser/minisql.y文件,然后执行src/include/parser/compile.sh脚本(它会自动生成对应的lex和yacc代码并移动到工程的指定目录下),最后需要重新执行cmake ..完成更新; - 本模块可以不设计相关的测试代码,以手动执行SQL命令观察结果来代替测试。
#5.5 诚信守则
- 请勿从其它组或在网络上找到的其它来源中复制源代码,一经发现抄袭,成绩为
0; - 请勿将代码发布到公共Github存储库上。
