miniob记录

那个分支看上去随时可能会挂掉所以记录一下。

实现Drop table功能

要求

实现 Drop Table 功能:删除表并清除表相关的资源(描述表的文件、数据文件、索引文件),能够成功执行下面语句。

create table t(id int, age int);
drop table t;
create table t(id int, name char);

实现

  1. default_storage_stage.cpp中SQL 流转到 default_storge 阶段的时候,在处理 SQL 的函数中,新增一个 drop_tablecase

    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;
    
  2. 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);
    }
    
  3. 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;
    }
    
  4. 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输入,并要求能够成功执行下面语句。

实现

  1. 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;
    }
    
  2. 添加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";
     }
    
  3. DATE的比较

    condition_filter.cpp 文件中,实现DATE字段的比较。

    case DATES: {
        //TODO 参考INTS进行大小段转换
        int left = *(int *)left_value;
        int right = *(int *)right_value;
        cmp_result = left - right;
    } break;
    
  4. DATE的索引

    bplus_tree.cpp 文件中,有8处需要添加 case DATE 的处理方法,根据函数内容,将case DATE添加到case INTS或case CHARS即可。

  5. 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;