代码传送门:
3.1.2 参考代码(解析)
3.2.3 参考代码(执行)
3.1 delete 语句的解析
3.1.1
解析 sql 语句需要了解一些分词器和解析器
简要流程:
- 匹配
delete
:使用matchToken(TOKEN_RESERVED_WORD, "delete")
匹配- 获取表名:使用 if 判断是否是
TOKEN_WORD
类型,如果是,获得表名;给字符串指针开空间的时候可以使用new_id_name()
函数- 匹配
where
:matchToken(TOKEN_RESERVED_WORD,"where")
,注意不是所有delete
语句都伴随一个where
表达式- 使用
parseExpressionRD()
获取值(或者表达式),并包裹在SRA_t
中(SRA_SELECT
类型),作为sql_stmt_delete->where
的值。(语句若不伴随where
则仅需构造SRA_TABLE
,具体实现可参考exp_01_stmt_parser/exp_01_03_select.cpp
)- 创建
sql_stmt_delete
类型的指针,分配内存空间并对各字段进行赋值,返回该指针
常用函数:
push_back(elem);
//向 vector 尾部加入数据Expression *Parser::parseExpressionRD();
//递归下降法解析表达式 详情看src/parser/parser.cpp
中SRA_t *SRATable(TableReference_t *ref);
//SRA_TABLE
构造函数SRA_t *SRASelect(SRA_t *sra, Expression *cond);
//SRA_SELECT
构造函数void SRA_print(SRA_t *sra);
//输出SRA_t
中的数据(调试用)
3.1.2 参考代码
#include <dongmensql/sqlstatement.h>
#include <parser/StatementParser.h>
/**
* 在现有实现基础上,实现delete from子句
*
* 支持的delete语法:
*
* DELETE FROM <table_nbame>
* WHERE <logical_expr>
*
* 解析获得 sql_stmt_delete 结构
*/
sql_stmt_delete *DeleteParser::parse_sql_stmt_delete(){
char *tableName;
SRA_t *where;
Token *token = this->parseNextToken();
if (!this->matchToken( TOKEN_RESERVED_WORD, "delete")) {
return NULL;
}
token = this->parseNextToken();
if (token->type == TOKEN_WORD) {
tableName = new_id_name();
strcpy(tableName, token->text);
} else {
strcpy(this->parserMessage, "invalid sql: missing table name.");
return NULL;
}
token = this->parseEatAndNextToken();
// where 语句
where = SRATable(TableReference_make(tableName, nullptr));
if(this->matchToken(TOKEN_RESERVED_WORD, "where")){
/*解析where子句中的条件表达式*/
Expression *whereExpr = this->parseExpressionRD();
if (this->parserStateType == PARSER_WRONG) {
return NULL;
}
where = SRASelect(where, whereExpr);
}
//进行赋值
sql_stmt_delete *sqlStmtDelete = (sql_stmt_delete *)calloc(1, sizeof(sql_stmt_delete));
sqlStmtDelete->tableName = tableName;
sqlStmtDelete->where = where;
return sqlStmtDelete;
};
3.2 delete 语句执行
3.2.1 物理操作
首先创建一个表扫描计划,然后用 Scan
类中的 next()
函数迭代数据库数据条目,对满足条件的条目进行修改。
Scan
类中有针对不同的物理计划的虚拟函数:
class Scan {
public:
DongmenDB *m_db;
Transaction *m_tx;
virtual int beforeFirst()=0;
virtual int next()=0;
virtual int close()=0;
virtual variant *getValueByIndex(int index)=0;
virtual int getIntByIndex(int index)=0;
virtual string getStringByIndex(int index)=0;
virtual int getInt(string tableName, string fieldName) = 0;
virtual variant* getValue(string fieldName)=0;
virtual string getString(string tableName, string fieldName)=0;
virtual int hasField(string tableName,string fieldName)=0;
virtual FieldInfo * getField(string tableName, string fieldName)=0;
virtual vector<char*> getFieldsName(string tableName)=0;
virtual int setInt(string tableName, string fieldName, int value)=0;
virtual int setString(string tableName, string fieldName, string value)=0;
virtual int deleteRecord()=0;
virtual int insertRecord()=0;
virtual int getRID(RecordID *recordID)=0;
virtual int moveTo(RecordID *recordID)=0;
Expression * evaluateExpression(Expression *expr, Scan* scan, variant *var);
};
3.2.2 实现
函数的具体实现在 src/physicalplan/
下 Scan
的多个子类中
主要思路:
- 使用
SRA_t
类型的where
构造执行计划(Scan
对象);- 遍历计划
- 在遍历计划过程的循环中,调用
scan->deleteRecord()
删除记录- 在循环中更新删除的记录条数,作为最终的返回值。
3.2.3 参考代码
#include <physicalplan/ExecutionPlan.h>
#include <physicalplan/TableScan.h>
#include <physicalplan/Select.h>
#include <physicalplan/Project.h>
#include <physicalplan/Join.h>
/*执行delete语句的物理计划,返回删除的记录条数
* 返回大于等于0的值,表示删除的记录条数;
* 返回小于0的值,表示删除过程中出现错误。
* */
int ExecutionPlan::executeDelete(DongmenDB *db, sql_stmt_delete *sqlStmtDelete, Transaction *tx){
/*删除语句以select的物理操作为基础实现。
* 1. 使用 sql_stmt_delete 的条件参数,调用 physical_scan_select_create 创建select的物理计划并初始化;
* 2. 执行 select 的物理计划,完成 delete 操作
* */
int count = 0;
Scan* scan = generateScan(db, sqlStmtDelete->where, tx);
scan->beforeFirst();
while(scan->next())
{
scan->deleteRecord();
count++;
}
scan->close();
return count;
};
指导文档由山东科技大学各位老师完成。