Giới thiệu
Vì phiên bản của MiniOB liên tục được cập nhật, nhiều video hướng dẫn trước đây về cách thực hiện lệnh DROP TABLE đã lỗi thời. Bài viết này tổng kết lại quá trình học tập cá nhân nhằm cung cấp một hướng tiếp cận rõ ràng và dễ hiểu cho việc triển khai chức năng này trong hệ thống cơ sở dữ liệu MiniOB.
I. Kiến trúc tổng thể
Luồng thực thi của lệnh DROP TABLE:
- Phân tích cú pháp SQL xử lý câu lệnh
DROP TABLE. - Trình thực thi gọi logic xóa từ lớp
Db. - Lớp
Dbtìm đối tượng bảng và gọi lớpTableđể thực hiện xóa. - Lớp
Tabletiến hành xóa các tệp chỉ mục và dữ liệu. - Cuối cùng là dọn dẹp tệp meta và các đối tượng bộ nhớ.
II. Triển khai ở tầng phân tích cú pháp
Bước 1: Tạo lớp DropTableStmt
// src/observer/sql/stmt/drop_table_stmt.h
#pragma once
#include "common/lang/string.h"
#include "common/lang/vector.h"
#include "sql/stmt/stmt.h"
class Db;
/**
* @brief Biểu diễn câu lệnh xóa bảng
* @ingroup Statement
*/
class DropTableStmt : public Stmt
{
public:
DropTableStmt(const string &table_name)
: table_name_(table_name)
{}
virtual ~DropTableStmt() = default;
StmtType type() const override { return StmtType::DROP_TABLE; }
const string &table_name() const { return table_name_; }
static RC create(Db *db, const DropTableSqlNode &drop_table, Stmt *&stmt);
private:
string table_name_;
};
// src/observer/sql/stmt/drop_table_stmt.cpp
#include "common/log/log.h"
#include "common/types.h"
#include "sql/stmt/drop_table_stmt.h"
#include "event/sql_debug.h"
RC DropTableStmt::create(Db *db, const DropTableSqlNode &drop_table, Stmt *&stmt)
{
stmt = new DropTableStmt(drop_table.relation_name);
return RC::SUCCESS;
}
Bước 2: Mở rộng trình phân tích lệnh
// src/observer/sql/stmt/stmt.cpp
#include "sql/stmt/drop_table_stmt.h"
RC Stmt::create_stmt(Db *db, const ParsedSqlNode *sql_node, Stmt *&stmt) {
switch (sql_node->flag) {
case SCF_DROP_TABLE: {
return DropTableStmt::create(db, sql_node->sstr.drop_table, stmt);
}
// Các trường hợp khác...
}
}
Điểm quan trọng:
- Sử dụng
SCF_DROP_TABLEđể nhận diện loại lệnh. - Chuyển đổi cấu trúc SQL thành đối tượng
DropTableStmt. - Ghi log để hỗ trợ debug.
III. Triển khai ở tầng thực thi
Bước 1: Tạo lớp DropTableExecutor
// src/observer/sql/executor/drop_table_executor.h
class SQLStageEvent;
/**
* @brief Thực thi lệnh DROP TABLE
* @ingroup Executor
*/
class DropTableExecutor
{
public:
DropTableExecutor() = default;
virtual ~DropTableExecutor() = default;
RC execute(SQLStageEvent *sql_event);
};
Bước 2: Đăng ký vào trình thực thi lệnh
// src/observer/sql/executor/drop_table_executor.cpp
RC DropTableExecutor::execute(SQLStageEvent *sql_event)
{
Stmt *stmt = sql_event->stmt();
Session *session = sql_event->session_event()->session();
DropTableStmt *drop_table_stmt = static_cast<DropTableStmt *>(stmt);
const char *table_name = drop_table_stmt->table_name().c_str();
RC rc = session->get_current_db()->drop_table(table_name);
return rc;
}
Điểm quan trọng:
- Lấy database hiện tại từ session.
- Gọi phương thức
drop_table()trong lớpDb. - Xử lý lỗi và ghi log chi tiết.
IV. Triển khai ở tầng Db
Mở rộng lớp Db
// src/observer/storage/db/db.h
class Db {
public:
RC drop_table(const char *table_name);
// src/observer/storage/db/db.cpp
RC Db::drop_table(const char *table_name)
{
string table_file_path = table_meta_file(path_.c_str(), table_name);
Table *table = find_table(table_name);
if (table == nullptr) {
LOG_ERROR("Table %s not found in opened tables.", table_name);
return RC::SCHEMA_TABLE_NOT_EXIST;
}
LOG_INFO("Starting to drop table %s", table_name);
table->drop(table_file_path.c_str(), path_.c_str());
delete table;
opened_tables_.erase(table_name);
return RC::SUCCESS;
}
Điểm quan trọng:
- Kiểm tra sự tồn tại của bảng trước khi xóa.
- Thứ tự xóa: đối tượng bảng → dữ liệu trong bộ nhớ → tệp meta.
- Sử dụng
unlink()để xóa tệp vật lý. - Ghi log cho từng bước thực hiện.
V. Triển khai ở tầng Table (quan trọng)
Mở rộng lớp Table
// src/observer/storage/table/table.h
class Table {
public:
RC drop(const char* path);
};
RC Table::drop(const char *path, const char *base_dir)
{
if (engine_) {
RC rc = engine_->destroy_indexes();
if (rc != RC::SUCCESS) {
LOG_ERROR("Failed to destroy indexes for table %s", table_meta_.name());
}
}
engine_.reset(nullptr);
if (::remove(path) < 0) {
return RC::INTERNAL;
}
string data_file = table_data_file(base_dir, table_meta_.name());
BufferPoolManager &bpm = db_->buffer_pool_manager();
RC rc = bpm.remove_file(data_file.c_str());
if (rc != RC::SUCCESS) {
return rc;
}
else {
LOG_INFO("Successfully removed data file. File name=%s", data_file.c_str());
}
return RC::SUCCESS;
}
Thứ tự xóa:
- Xóa tất cả các tệp chỉ mục (qua engine).
- Xóa tệp dữ liệu bảng.
- Ghi log cho từng bước.
VI. Hệ thống chỉ mục
Bước 1: Mở rộng interface Index
// src/observer/storage/index/index.h
class Index {
public:
virtual RC destroy() = 0; // Thêm phương thức hủy
};
Bước 2: Triển khai B+ Tree
// src/observer/storage/index/bplus_tree_index.cpp
RC BplusTreeIndex::destroy() {
return handler_.destroy(); // Giao tiếp với handler
}
// src/observer/storage/index/bplus_tree_handler.cpp
RC BplusTreeIndex::destroy()
{
LOG_INFO("Destroying index %s, field: %s", index_meta_.name(), index_meta_.field());
index_handler_.destroy();
return RC::SUCCESS;
}
Điểm quan trọng:
- Việc hủy chỉ mục cần xóa tệp vật lý.
- Sử dụng
BufferPoolManagerđể xử lý việc xóa tệp.
VII. Quản lý bộ đệm (BufferPoolManager)
Giao diện xóa tệp
// src/observer/storage/buffer/disk_buffer_pool.cpp
RC DiskBufferPool::remove_file()
{
bp_manager_.remove_file(file_name_.c_str());
return RC::SUCCESS;
}
RC BufferPoolManager::remove_file(const char *_file_name)
{
std::string file_name(_file_name);
RC rc = close_file(file_name.c_str());
if (rc != RC::SUCCESS) {
LOG_WARN("Failed to close file %s. rc=%d, but trying to remove it anyway", file_name.c_str(), rc);
}
if (::remove(file_name.c_str()) != 0) {
int err = errno;
LOG_ERROR("Failed to remove file %s. errno=%d:%s", file_name.c_str(), err, strerror(err));
return (err == ENOENT) ? RC::FILE_NOT_EXIST : RC::IOERR_CLOSE;
} else {
LOG_INFO("Successfully removed file %s.", file_name.c_str());
}
return RC::SUCCESS;
}
Điểm quan trọng:
- Đóng file nếu có.
- Dùng
unlink()để xóa tệp. - Ghi log kết quả.
VIII. Tích hợp với engine bảng
Bước 1: Mở rộng interface TableEngine
// src/observer/storage/table/table_engine.h
class TableEngine {
public:
virtual RC destroy_indexes() = 0; // Giao diện xóa chỉ mục
};
Bước 2: Triển khai HeapTableEngine
// src/observer/storage/table/heap_table_engine.cpp
RC HeapTableEngine::destroy_indexes() {
RC rc = RC::SUCCESS;
if (indexes_.empty()) {
LOG_INFO("No indexes to destroy for table %s", table_meta_->name());
return rc;
}
for (auto index : indexes_) {
RC tmp_rc = index->destroy(); // Xóa tệp chỉ mục
if (tmp_rc != RC::SUCCESS) {
LOG_ERROR("Failed to destroy index %s. rc=%s", index->index_meta().name(), strrc(tmp_rc));
rc = tmp_rc;
}
delete index;
}
indexes_.clear();
return rc;
}
Điểm quan trọng:
- Lặp qua tất cả chỉ mục và gọi
destroy(). - Tiếp tục xóa các chỉ mục còn lại dù có lỗi.
- Xóa đối tượng chỉ mục khỏi bộ nhớ.
IX. Tổng kết luồng xử lý
Luồng gọi hoàn chỉnh:
1. SQLParser -> DropTableStmt::create()
2. CommandExecutor -> DropTableExecutor::execute()
3. Db::drop_table()
│
├── Table::drop()
│ ├── TableEngine::destroy_indexes()
│ │ ├── BplusTreeIndex::destroy()
│ │ │ └── BplusTreeHandler::destroy()
│ │ │ └── BufferPoolManager::remove_file()
│ │ └── (Xóa đối tượng chỉ mục trong bộ nhớ)
│ │
│ └── BufferPoolManager::remove_file() // Xóa tệp dữ liệu
│
├── (Xóa đối tượng bảng trong bộ nhớ)
│
└── unlink(tệp meta của bảng)
Thứ tự dọn dẹp tài nguyên:
- Tệp chỉ mục (nhiều tệp).
- Tệp dữ liệu bảng.
- Đối tượng bảng trong bộ nhớ.
- Tệp meta bảng.