miniob记录
那个分支看上去随时可能会挂掉所以记录一下。
实现Drop table功能
要求
实现 Drop Table 功能:删除表并清除表相关的资源(描述表的文件、数据文件、索引文件),能够成功执行下面语句。
create table t(id int, age int);
drop table t;
create table t(id int, name char);
实现
在
default_storage_stage.cpp
中SQL 流转到default_storge
阶段的时候,在处理 SQL 的函数中,新增一个drop_table
的case
。case SCF_DROP_TABLE: { // TODO: 拿到要 drop 的表 const DropTable &drop_table = sql->sstr.drop_table; // TODO: 调用drop_table接口,drop_table 要在 handler_ 中实现 rc = handler_->drop_table( current_db, drop_table.relation_name); // TODO: 返回结果,带不带换行符都可以 snprintf(response, sizeof(response), "%s\n", rc == RC::SUCCESS ? "SUCCESS" : "FAILURE"); }break;
在
default_handler.cpp
文件中,实现handler_
的drop_table
接口。RC DefaultHandler::drop_table(const char *dbname, const char *relation_name) { // TODO 查找对应的数据库 Db *db = find_db(dbname); // TODO 如果数据库不存在返回错误,如果存在调用db的drop_table接口 if (db == nullptr) { return RC::SCHEMA_DB_NOT_EXIST; } return db->drop_table(relation_name); }
在
db.cpp
中,实现drop_table
接口。RC Db::drop_table(const char* table_name) { RC rc = RC::SUCCESS; //TODO 从表list(opened_tables_)中找出表指针 auto it = opened_tables_.find(table_name); //TODO 找不到表,要返回错误 if (it == opened_tables_.end()) { LOG_WARN("%s isn't exists.", table_name); return RC::SCHEMA_TABLE_NOT_EXIST; } //TODO 调用 table->destroy 函数,让表自己销毁资源 Table *table = opened_tables_[table_name]; rc = table->destroy(path_.c_str()); if (rc != RC::SUCCESS) { LOG_ERROR("Failed to drop table %s.", table_name); return rc; } //TODO 删除成功的话,从表list中将它删除 opened_tables_.erase(table_name); LOG_INFO("Drop table success. table name=%s", table_name); return RC::SUCCESS; }
在
table.cpp
中实现destroy
接口,清理文件和相关数据。RC Table::destroy(const char* dir) { //刷新所有脏页 RC rc = sync(); if(rc != RC::SUCCESS) return rc; //TODO 删除描述表元数据的文件 std::string meta_file = table_meta_file(dir, name()); // 获取元数据文件路径(.table) if (unlink(meta_file.c_str()) != 0) { // unlink()返回值0表示成功 -1表示失败 return RC::GENERIC_ERROR; } //TODO 删除表数据文件 std::string data_file = table_data_file(dir, name()); // 获取表数据文件路径(.data) if (unlink(data_file.c_str()) != 0) { // unlink()返回值0表示成功 -1表示失败 return RC::GENERIC_ERROR; } //TODO 清理所有的索引相关文件数据与索引元数据 const int index_num = table_meta_.index_num(); for (int i = 0; i < index_num; i++) { // 遍历每个索引 const IndexMeta *index_meta = table_meta_.index(i); const FieldMeta *field_meta = table_meta_.field(index_meta->field()); if (field_meta == nullptr) { LOG_ERROR("Found invalid index meta info which has a non-exists field. table=%s, index=%s, field=%s", name(), index_meta->name(), index_meta->field()); // skip cleanup // do all cleanup action in destructive Table function return RC::GENERIC_ERROR; } std::string index_file = table_index_file(dir, name(), index_meta->name()); // 获取索引文件名(.index) if (unlink(index_file.c_str()) != 0) { return RC::GENERIC_ERROR; } } return RC::SUCCESS; }
增加Date字段
要求
修改数据库使其支持DATE字段,DATE支持范围不超过2038年2月,不小于1970年1月1号,请注意处理非法的DATE输入,并要求能够成功执行下面语句。
实现
DATE的存储
在
parse.cpp
文件中,实现check_date
函数对日期的合法性做校验。bool check_date(int y, int m, int d) { // TODO 根据 y:year,m:month,d:day 校验日期是否合法 // TODO 合法 return 1 // TODO 不合法 return 0 if (y < 1970 || y > 2038) return 0; if (m < 1 || m > 12) return 0; int mx_day; // mx_day记录当月最大天数 if (m == 2) { if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) mx_day = 29; // 闰年 else mx_day = 28; } else if (m <= 7) { if (m % 2 == 1) mx_day = 31; else mx_day = 30; } else if (m % 2 == 1) mx_day = 30; else mx_day = 31; if (d > mx_day) return 0; // TODO more judgement return 1; }
在
parse.cpp
文件中,实现value_init_date
函数将字符格式的DATE
转换成INT
格式存储。int value_init_date(Value *value, const char *v) { // TODO 将 value 的 type 属性修改为日期属性:DATES value->type = DATES; // 从lex的解析中读取 year,month,day 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; // TODO 将日期转换成整数 int date_value = y * 400 + m * 35 + d; // TODO 将value 的 data 属性修改为转换后的日期 value->data = malloc(sizeof(int)); memcpy(value->data, &date_value, sizeof(int)); return 0; }
添加DATE的元数据
在
field_meta.cpp
文件中,我们需要在 table schema 支持的属性中添加DATE属性。// TODO 向属性元数据中增加dates属性 const char *ATTR_TYPE_NAME[] = {"undefined", "chars", "ints", "floats", "dates"};
并且修改下方校验属性的范围,将DATE改为合法属性
const char *attr_type_to_string(AttrType type) { //TODO 修改范围,将DATES添加到type的检验中 if (type >= UNDEFINED && type <= DATES) { return ATTR_TYPE_NAME[type]; } return "unknown"; }
DATE的比较
在
condition_filter.cpp
文件中,实现DATE字段的比较。case DATES: { //TODO 参考INTS进行大小段转换 int left = *(int *)left_value; int right = *(int *)right_value; cmp_result = left - right; } break;
DATE的索引
在
bplus_tree.cpp
文件中,有8处需要添加 case DATE 的处理方法,根据函数内容,将case DATE添加到case INTS或case CHARS即可。DATE的输出
在
tuple.cpp
文件中,我们需要在代码执行到add_record时做格式转换,需要按照输出要求,将日期类型数据,转换成合适的字符串。case DATES: { // TODO 从record中读取存储的日期 int value = *(int *)(record + field_meta->offset()); // TODO 将日期转换为满足输出格式的字符串,注意这里月份和天数,不足两位时需要填充0 int y = value / 400; int m = (value % 400) / 35; int d = value % 400 % 35; char *date_str; date_str = (char *)malloc(sizeof(char) * 20); sprintf(date_str, "%04d-%02d-%02d", y, m, d); // TODO 将字符串添加到tuple中 tuple.add(date_str, strlen(date_str)); }break;