未验证 提交 f4e52131 编写于 作者: 羽飞's avatar 羽飞 提交者: GitHub

增加一些文档 (#200)

### What problem were solved in this pull request?

Issue Number: ref #165 
ref #174 

Problem:
一些文档需要优化
上级 380fea38
......@@ -4,7 +4,11 @@ MiniOB 是 [OceanBase](https://github.com/oceanbase/oceanbase) 与华中科技
MiniOB 设计的目标是面向在校学生、数据库从业者、爱好者,或者对基础技术有兴趣的爱好者, 整体代码量少,易于上手并学习, 是一个系统性的数据库学习项目。miniob 设置了一系列由浅入深的题目,以帮助同学们"零"基础入门, 让同学们快速了解数据库并深入学习数据库内核,期望通过相关训练之后,能够熟练掌握数据库内核模块的功能与协同关系, 并能够在使用数据库时,设计出高效的 SQL 。miniob 为了更好的学习数据库实现原理, 对诸多模块都做了简化,比如不考虑并发操作, 安全特性, 复杂的事物管理等功能。
## 快速上手
# GitHub Pages
代码配套文档和相关代码注释已经生成文档并通过 GitHub Pages 发布, 可以直接访问:
[MiniOB GitHub Pages](https://oceanbase.github.io/miniob/).
# 快速上手
为了帮助开发者更好的上手并学习 miniob, 建议阅读:
......@@ -17,8 +21,6 @@ MiniOB 设计的目标是面向在校学生、数据库从业者、爱好者,
6. [MiniOB 各模块文档](docs/src/design/introduction.md)
7. [doxygen 代码文档](docs/doxy/html/index.html)
或者直接看 [MiniOB GitHub Pages](https://oceanbase.github.io/miniob/).
更多的文档, 可以参考 docs 目录下的文档, 为了帮助大家更好的学习数据库基础知识, OceanBase 社区提供了一系列教程, 建议学习:
1. [《从0到1数据库内核实战教程》 视频教程](https://open.oceanbase.com/activities/4921877?id=4921946)
......@@ -53,7 +55,7 @@ MiniOB 整体架构如下图所示:
- 客户端:作为测试工具,接收用户请求,向服务端发起请求。
## [OceanBase 大赛](https://open.oceanbase.com/competition)
# [OceanBase 大赛](https://open.oceanbase.com/competition)
2022 OceanBase 数据库大赛是由中国计算机学会(CCF)数据库专业委员会指导,OceanBase 与蚂蚁技术研究院学术合作团队联合举办的数据库内核实战赛事。本次大赛主要面向全国爱好数据库的高校学生,以“竞技、交流、成长”为宗旨,搭建基于赛事的技术交流平台,促进高校创新人才培养机制,不仅帮助学生从0开始系统化学习数据库理论知识,提升学生数据库实践能力,更能帮助学生走向企业积累经验,促进国内数据库人才的发展,碰撞出创新的火花。
......@@ -81,7 +83,7 @@ OceanBase 初赛基于一套适合初学者实践的数据库实训平台 miniob
[大赛 FAQ ](https://ask.oceanbase.com/t/topic/35601465)
## 在线开发平台
# 在线开发平台
本仓库基于 Gitpod 建立了快速在线开发平台, 点击下面按钮一键体验(建议使用 Chrome 浏览器)
......@@ -89,15 +91,15 @@ OceanBase 初赛基于一套适合初学者实践的数据库实训平台 miniob
首次进入 Gitpod 时需要安装一些依赖,依赖安装完成后,终端界面会显示 "Dependency installed successfully"。运行 `bash build.sh --make -j4` 命令即可编译miniob。
## Contributing
# Contributing
OceanBase 社区热情欢迎每一位对数据库技术热爱的开发者,期待携手开启思维碰撞之旅。无论是文档格式调整或文字修正、问题修复还是增加新功能,都是对 OceanBase 社区参与和贡献方式之一,立刻开启您的 First Contribution 吧!更多详情, 请参考 [社区贡献](CONTRIBUTING.md).
## License
# License
MiniOB 采用 [木兰宽松许可证,第2版](https://license.coscl.org.cn/MulanPSL2), 可以自由拷贝和使用源码, 当做修改或分发时, 请遵守 [木兰宽松许可证,第2版](https://license.coscl.org.cn/MulanPSL2).
## 社区组织
# 社区组织
- [OceanBase 社区交流群 33254054](https://h5.dingtalk.com/circle/healthCheckin.html?corpId=dingd88359ef5e4c49ef87cda005313eea7a&1fe0ca69-72d=16c86a07-83c&cbdbhh=qwertyuiop&origin=1)
- [OceanBase 大赛官方交流群 35326455](https://qr.dingtalk.com/action/joingroup?code=v1,k1,g61jI0RwHQA8UMocuTbys2cyM7vck2c6jNE87vdxz9o=&_dt_no_comment=1&origin=11)
......
......@@ -16,7 +16,7 @@ See the Mulan PSL v2 for more details. */
#include <benchmark/benchmark.h>
#include "storage/index/bplus_tree.h"
#include "storage/default/disk_buffer_pool.h"
#include "storage/buffer/disk_buffer_pool.h"
#include "common/log/log.h"
#include "integer_generator.h"
......
......@@ -18,7 +18,7 @@ See the Mulan PSL v2 for more details. */
#include <benchmark/benchmark.h>
#include "storage/record/record_manager.h"
#include "storage/default/disk_buffer_pool.h"
#include "storage/buffer/disk_buffer_pool.h"
#include "storage/common/condition_filter.h"
#include "storage/trx/vacuous_trx.h"
#include "common/log/log.h"
......
......@@ -9,6 +9,7 @@
- [开发环境搭建(远程调试, 适用于 Window, Linux 和 Mac)](./dev-env/how_to_dev_in_docker_container_by_vscode.md)
- [Windows 使用Docker开发MiniOB](./dev-env/how_to_dev_miniob_by_docker_on_windows.md)
- [使用Docker开发MiniOB](./dev-env/how-to-dev-using-docker.md)
- [MiniOB 调试](./dev-env/miniob-how-to-debug.md)
- [功能模块设计说明文档](./design/introduction.md)
- [事务](./design/miniob-transaction.md)
......@@ -20,8 +21,12 @@
- [提交测试需要满足的输出要求](./game/miniob-output-convention.md)
- [2021届大赛题目介绍](./game/miniob_topics.md)
- [大赛手把手入门教程](./game/gitee-instructions.md)
- [date 测试说明](./game/miniob-test-comment-date.md)
- [date 实现解析](./game/miniob-date-implementation.md)
- [drop table 实现解析](./game/miniob-drop-table-implementation.md)
- [数据库基础理论课程](./lectures/index.md)
- [版权声明](./lectures/copyright.md)
- [第1章 数据库管理系统概述](./lectures/lecture-1.md)
- [第2章 数据库的存储结构](./lectures/lecture-2.md)
- [第3章 索引结构](./lectures/lecture-3.md)
......@@ -29,4 +34,3 @@
- [第5章 查询优化](./lectures/lecture-5.md)
- [第6章 事务处理](./lectures/lecture-6.md)
- [参考资料](./lectures/references.md)
- [版权声明](./lectures/copyright.md)
\ No newline at end of file
......@@ -6,4 +6,5 @@ MiniOB 当前可以在Linux/MacOS上编译,所以开发环境最好是Linux或
- [开发环境搭建(本地调试, 适用 Linux 和 Mac)](how_to_dev_miniob_by_vscode.md)
- [开发环境搭建(远程调试, 适用于 Window, Linux 和 Mac)](how_to_dev_in_docker_container_by_vscode.md)
- [Windows 使用Docker开发MiniOB](how_to_dev_miniob_by_docker_on_windows.md)
- [使用Docker开发MiniOB](how-to-dev-using-docker.md)
\ No newline at end of file
- [使用Docker开发MiniOB](how-to-dev-using-docker.md)
- [MiniOB 调试](./dev-env/miniob-how-to-debug.md)
\ No newline at end of file
# miniob调试篇
-- by caizj
调试c/c++程序,常用的有两种方式,一是打印日志调试,二是gdb调试,调试不仅可以定位问题,也可以用来熟悉代码。
## miniob关键代码
首先,拿到一份陌生的代码,要先确定代码的大致结构,一些关键数据结构和方法,这里的技巧和经验不展开了
### miniob的关键数据结构
部分关键数据结构:
```c++
parse_def.h:
struct Selects;//查询相关
struct CreateTable;//建表相关
struct DropTable;//删表相关
enum SqlCommandFlag;//sql语句对应的command枚举
union Queries;//各类dml和ddl操作的联合
table.h
class Table;
db.h
class Db;
```
### miniob的关键接口
部分关键接口
```c++
RC parse(const char *st, Query *sqln);//sql parse入口
ExecuteStage::handle_request
ExecuteStage::do_select
DefaultStorageStage::handle_event
DefaultHandler::create_index
DefaultHandler::insert_record
DefaultHandler::delete_record
DefaultHandler::update_record
Db::create_table
Db::find_table
Table::create
Table::scan_record
Table::insert_record
Table::update_record
Table::delete_record
Table::scan_record
Table::create_index
```
### 打印日志调试
miniob提供的日志接口
```c++
deps/common/log/log.h:
#define LOG_PANIC(fmt, ...)
#define LOG_ERROR(fmt, ...)
#define LOG_WARN(fmt, ...)
#define LOG_INFO(fmt, ...)
#define LOG_DEBUG(fmt, ...)
#define LOG_TRACE(fmt, ...)
```
日志相关配置项`observer.ini`
```
LOG_FILE_NAME = observer.log
# LOG_LEVEL_PANIC = 0,
# LOG_LEVEL_ERR = 1,
# LOG_LEVEL_WARN = 2,
# LOG_LEVEL_INFO = 3,
# LOG_LEVEL_DEBUG = 4,
# LOG_LEVEL_TRACE = 5,
# LOG_LEVEL_LAST
LOG_FILE_LEVEL=5
LOG_CONSOLE_LEVEL=1
```
### gdb调试
调试工具有很多种,但是它们的关键点都是类似的,比如关联到进程、运行时查看变量值、单步运行、跟踪变量等。GDB是在Linux环境中常用的调试工具。其它环境上也有类似的工具,比如LLDB,或者Windows可能使用Visual Studio直接启动调试。Java的调试工具是jdb。
另外,很多同学喜欢使用Visual Studio Code(vscode)开发项目,vscode提供了很多插件,包括调试的插件,这些调试插件支持gdb、lldb等,可以按照自己的平台环境,设置不同的调试工具。
这里介绍了gdb的基本使用,其它工具的使用方法类似。
1. Attach进程
```
[caizj@localhost run]$ gdb -p `pidof observer`
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8 Copyright (C) 2018 Free Software Foundation, Inc.
(gdb)
```
2. 设置断点
```
(gdb) break do_select
Breakpoint 1 at 0x44b636: file /home/caizj/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp, line 526.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000044b636 in ExecuteStage::do_select(char const*, Query*, SessionEvent*)
at /home/caizj/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526
```
```
(gdb) break Table::scan_record
Breakpoint 2 at 0x50b82b: Table::scan_record. (2 locations)
(gdb) inf b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000044b636 in ExecuteStage::do_select(char const*, Query*, SessionEvent*)
at /home/caizj/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526
2 breakpoint keep y <MULTIPLE>
2.1 y 0x000000000050b82b in Table::scan_record(Trx*, ConditionFilter*, int, void*, void (*)(char const*, void*))
at /home/caizj/source/stunning-engine/src/observer/storage/common/table.cpp:421
2.2 y 0x000000000050ba00 in Table::scan_record(Trx*, ConditionFilter*, int, void*, RC (*)(Record*, void*))
at /home/caizj/source/stunning-engine/src/observer/storage/common/table.cpp:426
(gdb)
```
3. 继续执行
```
(gdb) c
Continuing.
```
4. 触发断点
执行:miniob > select * from t1;
```
[Switching to Thread 0x7f51345f9700 (LWP 54706)]
Thread 8 "observer" hit Breakpoint 1, ExecuteStage::do_select (this=0x611000000540,
db=0x6040000005e0 "sys", sql=0x620000023080, session_event=0x608000003d20)
at /home/caizj/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526
526 RC rc = RC::SUCCESS;
(gdb)
```
5. 单步调式
```
575 std::vector<TupleSet> tuple_sets;
(gdb) next
576 for (SelectExeNode *&node: select_nodes) {
(gdb) n
577 TupleSet tuple_set;
(gdb)
578 rc = node->execute(tuple_set);
(gdb)
```
6. 跳入
跟踪到函数内部
```
(gdb) s
SelectExeNode::execute (this=0x60700002ce80, tuple_set=...)
at /home/caizj/source/stunning-engine/src/observer/sql/executor/execution_node.cpp:43
43 CompositeConditionFilter condition_filter;
(gdb)
```
7. 打印变量
```
(gdb) p tuple_set
$3 = (TupleSet &) @0x7f51345f1760: {tuples_ = std::vector of length 0, capacity 0, schema_ = {
fields_ = std::vector of length 0, capacity 0}}
(gdb)
```
8. watch变量
```
(gdb) n
443 RC rc = RC::SUCCESS;
(gdb) n
444 RecordFileScanner scanner;
(gdb) n
445 rc = scanner.open_scan(*data_buffer_pool_, file_id_, filter);
(gdb) watch -l rc
Hardware watchpoint 3: -location rc
(gdb) c
Continuing.
Thread 8 "observer" hit Hardware watchpoint 3: -location rc
Old value = SUCCESS
New value = RECORD_EOF
0x000000000050c2de in Table::scan_record (this=0x60f000007840, trx=0x606000009920,
filter=0x7f51345f12a0, limit=2147483647, context=0x7f51345f11c0,
record_reader=0x50b74a <scan_record_reader_adapter(Record*, void*)>)
at /home/caizj/source/stunning-engine/src/observer/storage/common/table.cpp:454
454 for ( ; RC::SUCCESS == rc && record_count < limit; rc = scanner.get_next_record(&record)) {
(gdb)
```
9. 结束函数调用
```
(gdb) finish
Run till exit from #0 0x000000000050c2de in Table::scan_record (this=0x60f000007840,
trx=0x606000009920, filter=0x7f51345f12a0, limit=2147483647, context=0x7f51345f11c0,
record_reader=0x50b74a <scan_record_reader_adapter(Record*, void*)>)
at /home/caizj/source/stunning-engine/src/observer/storage/common/table.cpp:454
```
10. 结束调试
```
(gdb) quit
A debugging session is active.
Inferior 1 [process 54699] will be detached.
Quit anyway? (y or n) y
Detaching from program: /home/caizj/local/bin/observer, process 54699
[Inferior 1 (process 54699) detached]
```
\ No newline at end of file
......@@ -10,6 +10,11 @@
作为参考,这里有第一届数据库大赛的题目介绍:
[第一届数据库大赛题目介绍](./miniob_topics.md)
还有往届选手给出了一些题解:
- [date 测试说明](./miniob-test-comment-date.md)
- [date 实现解析](./miniob-date-implementation.md)
- [drop table 实现解析](./miniob-drop-table-implementation.md)
在参赛前,除了学习基础的理论知识,还可以使用OceanBase提供的训练营,来快速上手:
[训练营](https://open.oceanbase.com/train?questionId=200001)
......
# Date实现解析
> 此实现解析有往届选手提供。具体代码实现已经有所变更,因此仅供参考。
- by caizj
## DATE的存储
一种实现方式:date以int类型的YYYYMMDD格式保存,比如2021-10-21,保存为整数就是2021\*1000 + 10\*100 + 21,在select展示时转成字符串YYYY-MM-DD格式,注意月份和天数要使用0填充。
在parse.cpp中,参考
```c++
int value_init_date(Value* value, const char* v) {
value->type = DATES;
int y,m,d;
sscanf(v, "%d-%d-%d", &y, &m, &d);//not check return value eq 3, lex guarantee
bool b = check_date(y,m,d);
if(!b) return -1;
int dv = y*10000+m*100+d;
value->data = malloc(sizeof(dv));//TODO:check malloc failure
memcpy(value->data, &dv, sizeof(dv));
return 0;
}
```
## 修改点
### 语法上修改支持
需要可匹配date的token词和DATE_STR值(一定要先于SSS,因为date的输入DATE_STR是SSS的子集)
语法(yacc文件)上增加type,value里增加DATE_STR值
```c++
[Dd][Aa][Tt][Ee] RETURN_TOKEN(DATE_T); // 增加DATE的token,需要在yacc文件中增加DATE_T的token
{QUOTE}[0-9]{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01]){QUOTE} yylval->string=strdup(yytext); RETURN_TOKEN(DATE_STR); // 使用正则表达式过滤DATE。需要在yacc文件中增加 %token <string> DATE_STR
```
同时,需要增加一个DATE类型,与INTS,FLOATS等含义相同:
```c++
// in parse_defs.h
typedef enum { UNDEFINED, CHARS, INTS, FLOATS, DATES, TEXTS, NULLS } AttrType;
```
### Date的合法性判断
输入日期的格式可以在词法分析时正则表达式里过滤掉。润年,大小月日期的合法性在普通代码中再做进一步判断。
在parse阶段,对date做校验,并格式化成int值保存(参考最前面的代码),同时对日期的合法性做校验,参考:
```c++
bool check_date(int y, int m, int d)
{
static int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool leap = (y%400==0 || (y%100 && y%4==0));
return y > 0
&& (m > 0)&&(m <= 12)
&& (d > 0)&&(d <= ((m==2 && leap)?1:0) + mon[m]);
}
```
### 增加新的类型date枚举
代码里有多处和类型耦合地方(增加一个类型,要动很多处离散的代码,基础代码在这方面的可维护性不好)
包括不限于,以下几处:
- DefaultConditionFilter
需要增加DATES类型的数据对比。因为这里将date作为整数存储,那么可以直接当做INTS来对比,比如:
```c++
case INTS:
case DATES: {
// 没有考虑大小端问题
// 对int和float,要考虑字节对齐问题,有些平台下直接转换可能会跪
int left = *(int *)left_value;
int right = *(int *)right_value;
cmp_result = left - right;
} break;
```
- BplusTreeScanner
`DefaultConditionFilter` 类似,也需要支持DATE类型的对比,可以直接当做整数比较。参考其中一块代码:
```c++
case INTS:case DATES: {
i1 = *(int *) pdata;
i2 = *(int *) pkey;
if (i1 > i2)
return 1;
if (i1 < i2)
return -1;
if (i1 == i2)
return 0;
}
break;
```
- ATTR_TYPE_NAME(storage/common/field_meta.cpp)
保存元数据时,需要这里的信息,比较简单,参考:
```c++
const char *ATTR_TYPE_NAME[] = {
"undefined",
"chars",
"ints",
"floats",
"dates"
};
```
- insert_record_from_file(storage/default/default_storage_stage.cpp)
这个接口主要是为了支持从文件导入数据的,同样,实现可以与int类型保持一致。
```c++
switch (field->type()) {
case INTS: case DATES:{
deserialize_stream.clear(); // 清理stream的状态,防止多次解析出现异常
deserialize_stream.str(file_value);
int int_value;
deserialize_stream >> int_value;
if (!deserialize_stream || !deserialize_stream.eof()) {
errmsg << "need an integer but got '" << file_values[i]
<< "' (field index:" << i << ")";
rc = RC::SCHEMA_FIELD_TYPE_MISMATCH;
} else {
value_init_integer(&record_values[i], int_value);
}
}
```
### Date select展示
TupleRecordConverter::add_record时做格式转换,需要按照输出要求,将日期类型数据,转换成合适的字符串。参考:
```c++
case DATES: {
int value = *(int*)(record + field_meta->offset());
char buf[16] = {0};
snprintf(buf,sizeof(buf),"%04d-%02d-%02d",value/10000, (value%10000)/100,value%100); // 注意这里月份和天数,不足两位时需要填充0
tuple.add(buf,strlen(buf));
}
break;
```
### 异常失败处理
只要输入的日期不合法,输出都是FAILURE\n。包括查询的where条件、插入的日期值、更新的值等。这里在解析时(parse.cpp)中就可以直接返回错误。
## 自测覆盖点
1. 日期输入,包括合法和非法日期格式。非法日期可以写单元测试做。
2. 日期值比较=、 >、 < >=、 <=
3. 日期字段当索引。很多同学漏掉了这个点。
4. 日期展示格式,注意月份和天数补充0
\ No newline at end of file
# miniob - drop table 实现解析
> 此实现解析有往届选手提供。具体代码实现已经有所变更,因此仅供参考。
**代码部分主要添加在:**
drop table 与create table相反,要清理掉所有创建表和表相关联的资源,比如描述表的文件、数据文件以及索引等相关数据和文件。
sql流转到default_storge阶段的时候,在处理sql的函数中,新增一个drop_table的case。
drop table就是删除表,在`create table t`时,会新建一个t.table文件,同时为了存储数据也会新建一个t.data文件存储下来。同时创建索引的时候,也会创建记录索引数据的文件,在删除表时也要一起删除掉。
那么删除表,就需要**删除t.table文件、t.data文件和关联的索引文件**
同时由于buffer pool的存在,在新建表和插入数据的时候,会写入buffer pool缓存。所以drop table,不仅需要删除文件,也需要**清空buffer pool** ,防止在数据没落盘的时候,再建立同名表,仍然可以查询到数据。
如果建立了索引,比如t_id on t(id),那么也会新建一个t_id.index文件,也需要删除这个文件。
这些东西全部清空,那么就完成了drop table。
具体的代码实现如下:
在default_storage_stage.cpp 中的处理SQL语句的case中增加一个
```c++
case SCF_DROP_TABLE: {
const DropTable& drop_table = sql->sstr[sql->q_size-1].drop_table; // 拿到要drop 的表
rc = handler_->drop_table(current_db,drop_table.relation_name); // 调用drop table接口,drop table要在handler中实现
snprintf(response,sizeof(response),"%s\n", rc == RC::SUCCESS ? "SUCCESS" : "FAILURE"); // 返回结果,带不带换行符都可以
}
break;
```
在default_handler.cpp文件中,实现handler的drop_table接口:
```c++
RC DefaultHandler::drop_table(const char *dbname, const char *relation_name) {
Db *db = find_db(dbname); // 这是原有的代码,用来查找对应的数据库,不过目前只有一个库
if(db == nullptr) {
return RC::SCHEMA_DB_NOT_OPENED;
}
return db->drop_table(relation_name); // 直接调用db的删掉接口
}
```
在db.cpp中,实现drop_table接口
```c++
RC Db::drop_table(const char* table_name)
{
auto it = opened_tables_.find(table_name);
if (it == opened_tables_.end())
{
return SCHEMA_TABLE_NOT_EXIST; // 找不到表,要返回错误,测试程序中也会校验这种场景
}
Table* table = it->second;
RC rc = table->destroy(path_.c_str()); // 让表自己销毁资源
if(rc != RC::SUCCESS) return rc;
opened_tables_.erase(it); // 删除成功的话,从表list中将它删除
delete table;
return RC::SUCCESS;
}
```
table.cpp中清理文件和相关数据
```c++
RC Table::destroy(const char* dir) {
RC rc = sync();//刷新所有脏页
if(rc != RC::SUCCESS) return rc;
std::string path = table_meta_file(dir, name());
if(unlink(path.c_str()) != 0) {
LOG_ERROR("Failed to remove meta file=%s, errno=%d", path.c_str(), errno);
return RC::GENERIC_ERROR;
}
std::string data_file = std::string(dir) + "/" + name() + TABLE_DATA_SUFFIX;
if(unlink(data_file.c_str()) != 0) { // 删除描述表元数据的文件
LOG_ERROR("Failed to remove data file=%s, errno=%d", data_file.c_str(), errno);
return RC::GENERIC_ERROR;
}
std::string text_data_file = std::string(dir) + "/" + name() + TABLE_TEXT_DATA_SUFFIX;
if(unlink(text_data_file.c_str()) != 0) { // 删除表实现text字段的数据文件(后续实现了text case时需要考虑,最开始可以不考虑这个逻辑)
LOG_ERROR("Failed to remove text data file=%s, errno=%d", text_data_file.c_str(), errno);
return RC::GENERIC_ERROR;
}
const int index_num = table_meta_.index_num();
for (int i = 0; i < index_num; i++) { // 清理所有的索引相关文件数据与索引元数据
((BplusTreeIndex*)indexes_[i])->close();
const IndexMeta* index_meta = table_meta_.index(i);
std::string index_file = index_data_file(dir, name(), index_meta->name());
if(unlink(index_file.c_str()) != 0) {
LOG_ERROR("Failed to remove index file=%s, errno=%d", index_file.c_str(), errno);
return RC::GENERIC_ERROR;
}
}
return RC::SUCCESS;
}
```
\ No newline at end of file
......@@ -6,6 +6,8 @@
输出是指服务端返回给客户端的数据。为了可以做测试,需要对输出的格式做约定。
NOTE:后台测试程序,是将预先编辑好的Case执行后,将执行结果与预期输出结果(预先编写完成)做对比,与mysql test工作原理类似,因此需要严格按照输出约束来输出。
> 这里虽然列出了很多约束条件,但是同学们并不需要担心,当前的实现已经满足了这些约束条件,或者给出了满足约束的帮助函数,只要按照要求使用即可。
1. 语法解析错误,返回 FAILURE(只返回这个字符串,不带任何多余字符)。