MiniOB drop-table 实现详解

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中增加一个

  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接口:

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接口

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中清理文件和相关数据

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;
}
2 个赞

好文!谢谢分享。

1 个赞