Skip to content

Commit 20aa4a5

Browse files
authored
feat: support multi-key indices (#524)
Signed-off-by: Alex Chi <[email protected]>
1 parent 04ddafb commit 20aa4a5

13 files changed

+200
-112
lines changed

src/common/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ add_library(
22
bustub_common
33
OBJECT
44
bustub_instance.cpp
5+
bustub_ddl.cpp
56
config.cpp
67
util/string_util.cpp)
78

89
set(ALL_OBJECT_FILES
9-
${ALL_OBJECT_FILES} $<TARGET_OBJECTS:bustub_common>
10-
PARENT_SCOPE)
10+
${ALL_OBJECT_FILES} $<TARGET_OBJECTS:bustub_common>
11+
PARENT_SCOPE)

src/common/bustub_ddl.cpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// DDL (Data Definition Language) statement handling in BusTub, including create table, create index, and set/show
2+
// variable.
3+
4+
#include <optional>
5+
#include <shared_mutex>
6+
#include <string>
7+
#include <tuple>
8+
9+
#include "binder/binder.h"
10+
#include "binder/bound_expression.h"
11+
#include "binder/bound_statement.h"
12+
#include "binder/statement/create_statement.h"
13+
#include "binder/statement/explain_statement.h"
14+
#include "binder/statement/index_statement.h"
15+
#include "binder/statement/select_statement.h"
16+
#include "binder/statement/set_show_statement.h"
17+
#include "buffer/buffer_pool_manager.h"
18+
#include "catalog/schema.h"
19+
#include "catalog/table_generator.h"
20+
#include "common/bustub_instance.h"
21+
#include "common/enums/statement_type.h"
22+
#include "common/exception.h"
23+
#include "common/util/string_util.h"
24+
#include "concurrency/lock_manager.h"
25+
#include "concurrency/transaction.h"
26+
#include "execution/execution_engine.h"
27+
#include "execution/executor_context.h"
28+
#include "execution/executors/mock_scan_executor.h"
29+
#include "execution/expressions/abstract_expression.h"
30+
#include "execution/plans/abstract_plan.h"
31+
#include "fmt/core.h"
32+
#include "fmt/format.h"
33+
#include "optimizer/optimizer.h"
34+
#include "planner/planner.h"
35+
#include "recovery/checkpoint_manager.h"
36+
#include "recovery/log_manager.h"
37+
#include "storage/disk/disk_manager.h"
38+
#include "storage/disk/disk_manager_memory.h"
39+
#include "type/value_factory.h"
40+
41+
namespace bustub {
42+
43+
void BustubInstance::HandleCreateStatement(Transaction *txn, const CreateStatement &stmt, ResultWriter &writer) {
44+
std::unique_lock<std::shared_mutex> l(catalog_lock_);
45+
auto info = catalog_->CreateTable(txn, stmt.table_, Schema(stmt.columns_));
46+
l.unlock();
47+
48+
if (info == nullptr) {
49+
throw bustub::Exception("Failed to create table");
50+
}
51+
WriteOneCell(fmt::format("Table created with id = {}", info->oid_), writer);
52+
}
53+
54+
void BustubInstance::HandleIndexStatement(Transaction *txn, const IndexStatement &stmt, ResultWriter &writer) {
55+
std::vector<uint32_t> col_ids;
56+
for (const auto &col : stmt.cols_) {
57+
auto idx = stmt.table_->schema_.GetColIdx(col->col_name_.back());
58+
col_ids.push_back(idx);
59+
if (stmt.table_->schema_.GetColumn(idx).GetType() != TypeId::INTEGER) {
60+
throw NotImplementedException("only support creating index on integer column");
61+
}
62+
}
63+
auto key_schema = Schema::CopySchema(&stmt.table_->schema_, col_ids);
64+
65+
// TODO(spring2023): If you want to support composite index key for leaderboard optimization, remove this assertion
66+
// and create index with different key type that can hold multiple keys based on number of index columns.
67+
//
68+
// You can also create clustered index that directly stores value inside the index by modifying the value type.
69+
70+
if (col_ids.empty() || col_ids.size() > 2) {
71+
throw NotImplementedException("only support creating index with exactly one or two columns");
72+
}
73+
74+
std::unique_lock<std::shared_mutex> l(catalog_lock_);
75+
auto info = catalog_->CreateIndex<IntegerKeyType, IntegerValueType, IntegerComparatorType>(
76+
txn, stmt.index_name_, stmt.table_->table_, stmt.table_->schema_, key_schema, col_ids, TWO_INTEGER_SIZE,
77+
IntegerHashFunctionType{});
78+
l.unlock();
79+
80+
if (info == nullptr) {
81+
throw bustub::Exception("Failed to create index");
82+
}
83+
WriteOneCell(fmt::format("Index created with id = {}", info->index_oid_), writer);
84+
}
85+
86+
void BustubInstance::HandleExplainStatement(Transaction *txn, const ExplainStatement &stmt, ResultWriter &writer) {
87+
std::string output;
88+
89+
// Print binder result.
90+
if ((stmt.options_ & ExplainOptions::BINDER) != 0) {
91+
output += "=== BINDER ===";
92+
output += "\n";
93+
output += stmt.statement_->ToString();
94+
output += "\n";
95+
}
96+
97+
std::shared_lock<std::shared_mutex> l(catalog_lock_);
98+
99+
bustub::Planner planner(*catalog_);
100+
planner.PlanQuery(*stmt.statement_);
101+
102+
bool show_schema = (stmt.options_ & ExplainOptions::SCHEMA) != 0;
103+
104+
// Print planner result.
105+
if ((stmt.options_ & ExplainOptions::PLANNER) != 0) {
106+
output += "=== PLANNER ===";
107+
output += "\n";
108+
output += planner.plan_->ToString(show_schema);
109+
output += "\n";
110+
}
111+
112+
// Print optimizer result.
113+
bustub::Optimizer optimizer(*catalog_, IsForceStarterRule());
114+
auto optimized_plan = optimizer.Optimize(planner.plan_);
115+
116+
l.unlock();
117+
118+
if ((stmt.options_ & ExplainOptions::OPTIMIZER) != 0) {
119+
output += "=== OPTIMIZER ===";
120+
output += "\n";
121+
output += optimized_plan->ToString(show_schema);
122+
output += "\n";
123+
}
124+
125+
WriteOneCell(output, writer);
126+
}
127+
128+
void BustubInstance::HandleVariableShowStatement(Transaction *txn, const VariableShowStatement &stmt,
129+
ResultWriter &writer) {
130+
auto content = GetSessionVariable(stmt.variable_);
131+
WriteOneCell(fmt::format("{}={}", stmt.variable_, content), writer);
132+
}
133+
134+
void BustubInstance::HandleVariableSetStatement(Transaction *txn, const VariableSetStatement &stmt,
135+
ResultWriter &writer) {
136+
session_variables_[stmt.variable_] = stmt.value_;
137+
}
138+
139+
} // namespace bustub

src/common/bustub_instance.cpp

Lines changed: 5 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -216,98 +216,27 @@ auto BustubInstance::ExecuteSqlTxn(const std::string &sql, ResultWriter &writer,
216216
switch (statement->type_) {
217217
case StatementType::CREATE_STATEMENT: {
218218
const auto &create_stmt = dynamic_cast<const CreateStatement &>(*statement);
219-
220-
std::unique_lock<std::shared_mutex> l(catalog_lock_);
221-
auto info = catalog_->CreateTable(txn, create_stmt.table_, Schema(create_stmt.columns_));
222-
l.unlock();
223-
224-
if (info == nullptr) {
225-
throw bustub::Exception("Failed to create table");
226-
}
227-
WriteOneCell(fmt::format("Table created with id = {}", info->oid_), writer);
219+
HandleCreateStatement(txn, create_stmt, writer);
228220
continue;
229221
}
230222
case StatementType::INDEX_STATEMENT: {
231223
const auto &index_stmt = dynamic_cast<const IndexStatement &>(*statement);
232-
233-
std::vector<uint32_t> col_ids;
234-
for (const auto &col : index_stmt.cols_) {
235-
auto idx = index_stmt.table_->schema_.GetColIdx(col->col_name_.back());
236-
col_ids.push_back(idx);
237-
if (index_stmt.table_->schema_.GetColumn(idx).GetType() != TypeId::INTEGER) {
238-
throw NotImplementedException("only support creating index on integer column");
239-
}
240-
}
241-
if (col_ids.size() != 1) {
242-
throw NotImplementedException("only support creating index with exactly one column");
243-
}
244-
auto key_schema = Schema::CopySchema(&index_stmt.table_->schema_, col_ids);
245-
246-
std::unique_lock<std::shared_mutex> l(catalog_lock_);
247-
auto info = catalog_->CreateIndex<IntegerKeyType, IntegerValueType, IntegerComparatorType>(
248-
txn, index_stmt.index_name_, index_stmt.table_->table_, index_stmt.table_->schema_, key_schema, col_ids,
249-
INTEGER_SIZE, IntegerHashFunctionType{});
250-
l.unlock();
251-
252-
if (info == nullptr) {
253-
throw bustub::Exception("Failed to create index");
254-
}
255-
WriteOneCell(fmt::format("Index created with id = {}", info->index_oid_), writer);
224+
HandleIndexStatement(txn, index_stmt, writer);
256225
continue;
257226
}
258227
case StatementType::VARIABLE_SHOW_STATEMENT: {
259228
const auto &show_stmt = dynamic_cast<const VariableShowStatement &>(*statement);
260-
auto content = GetSessionVariable(show_stmt.variable_);
261-
WriteOneCell(fmt::format("{}={}", show_stmt.variable_, content), writer);
229+
HandleVariableShowStatement(txn, show_stmt, writer);
262230
continue;
263231
}
264232
case StatementType::VARIABLE_SET_STATEMENT: {
265233
const auto &set_stmt = dynamic_cast<const VariableSetStatement &>(*statement);
266-
session_variables_[set_stmt.variable_] = set_stmt.value_;
234+
HandleVariableSetStatement(txn, set_stmt, writer);
267235
continue;
268236
}
269237
case StatementType::EXPLAIN_STATEMENT: {
270238
const auto &explain_stmt = dynamic_cast<const ExplainStatement &>(*statement);
271-
std::string output;
272-
273-
// Print binder result.
274-
if ((explain_stmt.options_ & ExplainOptions::BINDER) != 0) {
275-
output += "=== BINDER ===";
276-
output += "\n";
277-
output += explain_stmt.statement_->ToString();
278-
output += "\n";
279-
}
280-
281-
std::shared_lock<std::shared_mutex> l(catalog_lock_);
282-
283-
bustub::Planner planner(*catalog_);
284-
planner.PlanQuery(*explain_stmt.statement_);
285-
286-
bool show_schema = (explain_stmt.options_ & ExplainOptions::SCHEMA) != 0;
287-
288-
// Print planner result.
289-
if ((explain_stmt.options_ & ExplainOptions::PLANNER) != 0) {
290-
output += "=== PLANNER ===";
291-
output += "\n";
292-
output += planner.plan_->ToString(show_schema);
293-
output += "\n";
294-
}
295-
296-
// Print optimizer result.
297-
bustub::Optimizer optimizer(*catalog_, IsForceStarterRule());
298-
auto optimized_plan = optimizer.Optimize(planner.plan_);
299-
300-
l.unlock();
301-
302-
if ((explain_stmt.options_ & ExplainOptions::OPTIMIZER) != 0) {
303-
output += "=== OPTIMIZER ===";
304-
output += "\n";
305-
output += optimized_plan->ToString(show_schema);
306-
output += "\n";
307-
}
308-
309-
WriteOneCell(output, writer);
310-
239+
HandleExplainStatement(txn, explain_stmt, writer);
311240
continue;
312241
}
313242
default:

src/include/common/bustub_instance.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ class CheckpointManager;
4141
class Catalog;
4242
class ExecutionEngine;
4343

44+
class CreateStatement;
45+
class IndexStatement;
46+
class VariableSetStatement;
47+
class VariableShowStatement;
48+
class ExplainStatement;
49+
4450
class ResultWriter {
4551
public:
4652
ResultWriter() = default;
@@ -271,6 +277,13 @@ class BustubInstance {
271277
void CmdDisplayIndices(ResultWriter &writer);
272278
void CmdDisplayHelp(ResultWriter &writer);
273279
void WriteOneCell(const std::string &cell, ResultWriter &writer);
280+
281+
void HandleCreateStatement(Transaction *txn, const CreateStatement &stmt, ResultWriter &writer);
282+
void HandleIndexStatement(Transaction *txn, const IndexStatement &stmt, ResultWriter &writer);
283+
void HandleExplainStatement(Transaction *txn, const ExplainStatement &stmt, ResultWriter &writer);
284+
void HandleVariableShowStatement(Transaction *txn, const VariableShowStatement &stmt, ResultWriter &writer);
285+
void HandleVariableSetStatement(Transaction *txn, const VariableSetStatement &stmt, ResultWriter &writer);
286+
274287
std::unordered_map<std::string, std::string> session_variables_;
275288
};
276289

src/include/execution/plans/seq_scan_plan.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ class SeqScanPlanNode : public AbstractPlanNode {
5757
/** The table name */
5858
std::string table_name_;
5959

60-
/** The predicate to filter in seqscan. It will ALWAYS be nullptr until you enable the MergeFilterScan rule.
61-
You don't need to handle it to get a perfect score as of in Fall 2022.
60+
/** The predicate to filter in seqscan. It will ALWAYS be nullptr unless you enable the MergeFilterScan rule.
61+
You don't need to handle it to get a perfect score in project 3 / project 4 in Spring 2023.
6262
*/
6363
AbstractExpressionRef filter_predicate_;
6464

src/include/storage/index/b_plus_tree_index.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class BPlusTreeIndex : public Index {
2929
public:
3030
BPlusTreeIndex(std::unique_ptr<IndexMetadata> &&metadata, BufferPoolManager *buffer_pool_manager);
3131

32-
void InsertEntry(const Tuple &key, RID rid, Transaction *transaction) override;
32+
auto InsertEntry(const Tuple &key, RID rid, Transaction *transaction) -> bool override;
3333

3434
void DeleteEntry(const Tuple &key, RID rid, Transaction *transaction) override;
3535

@@ -50,10 +50,10 @@ class BPlusTreeIndex : public Index {
5050

5151
/** We only support index table with one integer key for now in BusTub. Hardcode everything here. */
5252

53-
constexpr static const auto INTEGER_SIZE = 4;
54-
using IntegerKeyType = GenericKey<INTEGER_SIZE>;
53+
constexpr static const auto TWO_INTEGER_SIZE = 8;
54+
using IntegerKeyType = GenericKey<TWO_INTEGER_SIZE>;
5555
using IntegerValueType = RID;
56-
using IntegerComparatorType = GenericComparator<INTEGER_SIZE>;
56+
using IntegerComparatorType = GenericComparator<TWO_INTEGER_SIZE>;
5757
using BPlusTreeIndexForOneIntegerColumn = BPlusTreeIndex<IntegerKeyType, IntegerValueType, IntegerComparatorType>;
5858
using BPlusTreeIndexIteratorForOneIntegerColumn =
5959
IndexIterator<IntegerKeyType, IntegerValueType, IntegerComparatorType>;

src/include/storage/index/extendible_hash_table_index.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ExtendibleHashTableIndex : public Index {
3333

3434
~ExtendibleHashTableIndex() override = default;
3535

36-
void InsertEntry(const Tuple &key, RID rid, Transaction *transaction) override;
36+
auto InsertEntry(const Tuple &key, RID rid, Transaction *transaction) -> bool override;
3737

3838
void DeleteEntry(const Tuple &key, RID rid, Transaction *transaction) override;
3939

src/include/storage/index/index.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,9 @@ class Index {
156156
* @param key The index key
157157
* @param rid The RID associated with the key
158158
* @param transaction The transaction context
159+
* @returns whether insertion is successful
159160
*/
160-
virtual void InsertEntry(const Tuple &key, RID rid, Transaction *transaction) = 0;
161+
virtual auto InsertEntry(const Tuple &key, RID rid, Transaction *transaction) -> bool = 0;
161162

162163
/**
163164
* Delete an index entry by key.

src/include/storage/index/linear_probe_hash_table_index.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class LinearProbeHashTableIndex : public Index {
3333

3434
~LinearProbeHashTableIndex() override = default;
3535

36-
void InsertEntry(const Tuple &key, RID rid, Transaction *transaction) override;
36+
auto InsertEntry(const Tuple &key, RID rid, Transaction *transaction) -> bool override;
3737

3838
void DeleteEntry(const Tuple &key, RID rid, Transaction *transaction) override;
3939

0 commit comments

Comments
 (0)