Skip to content

Commit f83f6c6

Browse files
committed
add http service, plan parsing, catalog serialization
Signed-off-by: Alex Chi <[email protected]>
1 parent f85a1d5 commit f83f6c6

File tree

7 files changed

+264
-0
lines changed

7 files changed

+264
-0
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ set(BUSTUB_THIRD_PARTY_INCLUDE_DIR
139139
${PROJECT_SOURCE_DIR}/third_party/argparse/include
140140
${PROJECT_SOURCE_DIR}/third_party/cpp_random_distributions
141141
${PROJECT_SOURCE_DIR}/third_party/backward-cpp/include
142+
${PROJECT_SOURCE_DIR}/third_party/json/include
142143
)
143144

144145
include_directories(${BUSTUB_SRC_INCLUDE_DIR} ${BUSTUB_TEST_INCLUDE_DIR} ${BUSTUB_THIRD_PARTY_INCLUDE_DIR})

src/common/bustub_instance.cpp

+44
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,50 @@ auto BustubInstance::ExecuteSql(const std::string &sql, ResultWriter &writer,
207207
}
208208
}
209209

210+
auto BustubInstance::ExecutePlan(const AbstractPlanNodeRef &plan, ResultWriter &writer) -> bool {
211+
auto txn = txn_manager_->Begin();
212+
try {
213+
auto result = ExecutePlanTxn(plan, txn, writer);
214+
txn_manager_->Commit(txn);
215+
delete txn;
216+
return result;
217+
} catch (bustub::Exception &ex) {
218+
txn_manager_->Abort(txn);
219+
delete txn;
220+
throw ex;
221+
}
222+
}
223+
224+
auto BustubInstance::ExecutePlanTxn(const AbstractPlanNodeRef &plan, Transaction *txn, ResultWriter &writer) -> bool {
225+
// Execute the query.
226+
auto exec_ctx = MakeExecutorContext(txn, false);
227+
std::vector<Tuple> result_set{};
228+
auto is_successful = execution_engine_->Execute(plan, &result_set, txn, exec_ctx.get());
229+
230+
// Return the result set as a vector of string.
231+
auto schema = plan->OutputSchema();
232+
233+
// Generate header for the result set.
234+
writer.BeginTable(false);
235+
writer.BeginHeader();
236+
for (const auto &column : schema.GetColumns()) {
237+
writer.WriteHeaderCell(column.GetName());
238+
}
239+
writer.EndHeader();
240+
241+
// Transforming result set into strings.
242+
for (const auto &tuple : result_set) {
243+
writer.BeginRow();
244+
for (uint32_t i = 0; i < schema.GetColumnCount(); i++) {
245+
writer.WriteCell(tuple.GetValue(&schema, i).ToString());
246+
}
247+
writer.EndRow();
248+
}
249+
writer.EndTable();
250+
251+
return is_successful;
252+
}
253+
210254
auto BustubInstance::ExecuteSqlTxn(const std::string &sql, ResultWriter &writer, Transaction *txn,
211255
std::shared_ptr<CheckOptions> check_options) -> bool {
212256
if (!sql.empty() && sql[0] == '\\') {

src/include/common/bustub_instance.h

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "common/config.h"
2727
#include "common/util/string_util.h"
2828
#include "execution/check_options.h"
29+
#include "execution/plans/abstract_plan.h"
2930
#include "libfort/lib/fort.hpp"
3031
#include "type/value.h"
3132

@@ -236,6 +237,9 @@ class BustubInstance {
236237
auto ExecuteSqlTxn(const std::string &sql, ResultWriter &writer, Transaction *txn,
237238
std::shared_ptr<CheckOptions> check_options = nullptr) -> bool;
238239

240+
auto ExecutePlan(const AbstractPlanNodeRef &plan, ResultWriter &writer) -> bool;
241+
auto ExecutePlanTxn(const AbstractPlanNodeRef &plan, Transaction *txn, ResultWriter &writer) -> bool;
242+
239243
/**
240244
* FOR TEST ONLY. Generate test tables in this BusTub instance.
241245
* It's used in the shell to predefine some tables, as we don't support

third_party/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@ add_subdirectory(utf8proc)
1717
add_subdirectory(backward-cpp)
1818

1919
add_subdirectory(readerwriterqueue)
20+
21+
add_subdirectory(json)
22+
23+
add_subdirectory(cpp-httplib)

tools/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_subdirectory(terrier_bench)
77
add_subdirectory(bpm_bench)
88
add_subdirectory(btree_bench)
99
add_subdirectory(htable_bench)
10+
add_subdirectory(http-server)
1011

1112
add_backward(shell)
1213
add_backward(sqllogictest)

tools/http-server/CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
set(HTTP_SERVER_SOURCES http-server.cpp)
2+
add_executable(http-server ${HTTP_SERVER_SOURCES})
3+
4+
target_link_libraries(http-server bustub)
5+
set_target_properties(http-server PROPERTIES OUTPUT_NAME bustub-http-server)

tools/http-server/http-server.cpp

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include <iostream>
2+
#include <memory>
3+
#include <nlohmann/json.hpp>
4+
#include <sstream>
5+
#include <string>
6+
#include <vector>
7+
#include "binder/table_ref/bound_join_ref.h"
8+
#include "catalog/column.h"
9+
#include "catalog/schema.h"
10+
#include "common/bustub_instance.h"
11+
#include "common/exception.h"
12+
#include "cpp-httplib/httplib.h"
13+
#include "execution/expressions/abstract_expression.h"
14+
#include "execution/expressions/column_value_expression.h"
15+
#include "execution/expressions/comparison_expression.h"
16+
#include "execution/plans/abstract_plan.h"
17+
#include "execution/plans/nested_loop_join_plan.h"
18+
#include "execution/plans/projection_plan.h"
19+
#include "execution/plans/seq_scan_plan.h"
20+
#include "type/type.h"
21+
#include "type/type_id.h"
22+
23+
using json = nlohmann::json;
24+
25+
auto TransformType(const std::string &ft) -> bustub::TypeId {
26+
if (ft == "INTEGER") {
27+
return bustub::TypeId::INTEGER;
28+
}
29+
if (ft == "BOOLEAN") {
30+
return bustub::TypeId::BOOLEAN;
31+
}
32+
throw bustub::Exception("unsupported field type");
33+
}
34+
35+
auto TransformExpr(json expr, const std::vector<bustub::AbstractPlanNodeRef> &children)
36+
-> bustub::AbstractExpressionRef {
37+
bustub::TypeId out_type = TransformType(expr["outType"]);
38+
if (expr.contains("op")) {
39+
json op = expr["op"];
40+
std::string name = op["name"];
41+
std::vector<json> operands_json = expr["operands"];
42+
std::vector<bustub::AbstractExpressionRef> operands;
43+
operands.reserve(operands_json.size());
44+
for (const auto &operand_json : operands_json) {
45+
operands.emplace_back(TransformExpr(operand_json, children));
46+
}
47+
if (name == "=") {
48+
return std::make_shared<bustub::ComparisonExpression>(operands[0], operands[1], bustub::ComparisonType::Equal);
49+
}
50+
throw bustub::Exception("unsupported op");
51+
}
52+
53+
// column value expression
54+
std::string name = expr["name"];
55+
size_t input = expr["input"];
56+
size_t i = 0;
57+
for (; i < children.size(); i++) {
58+
size_t cnt = children[i]->OutputSchema().GetColumnCount();
59+
if (input < cnt) {
60+
break;
61+
}
62+
input -= cnt;
63+
}
64+
return std::make_shared<bustub::ColumnValueExpression>(i, input, out_type);
65+
}
66+
67+
auto TransformRootRel(bustub::BustubInstance &bustub, const std::map<std::string, json> &rels, json root_rel)
68+
-> bustub::AbstractPlanNodeRef {
69+
std::string rel_op = root_rel["relOp"];
70+
std::vector<std::string> inputs = root_rel["inputs"];
71+
std::vector<bustub::AbstractPlanNodeRef> input_plan_nodes;
72+
for (const auto &input : inputs) {
73+
auto input_rel = rels.find(input)->second;
74+
auto input_plan_node = TransformRootRel(bustub, rels, input_rel);
75+
input_plan_nodes.emplace_back(std::move(input_plan_node));
76+
}
77+
std::vector<std::string> fields = root_rel["fields"];
78+
std::vector<std::string> field_types = root_rel["fieldTypes"];
79+
std::vector<bustub::Column> columns;
80+
for (size_t i = 0; i < fields.size(); i++) {
81+
auto ft = field_types[i];
82+
columns.emplace_back(fields[i], TransformType(ft));
83+
}
84+
bustub::SchemaRef schema = std::make_shared<bustub::Schema>(columns);
85+
if (rel_op == "org.apache.calcite.interpreter.Bindables$BindableJoin") {
86+
std::string join_type = root_rel["joinType"];
87+
bustub::JoinType jt;
88+
if (join_type == "inner") {
89+
jt = bustub::JoinType::INNER;
90+
} else {
91+
throw bustub::Exception("unsupported join type");
92+
}
93+
auto predicate = TransformExpr(root_rel["condition"], input_plan_nodes);
94+
return std::make_shared<bustub::NestedLoopJoinPlanNode>(std::move(schema), input_plan_nodes[0], input_plan_nodes[1],
95+
predicate, jt);
96+
}
97+
if (rel_op == "org.apache.calcite.interpreter.Bindables$BindableTableScan") {
98+
std::string table_name = root_rel["table"][0];
99+
auto table_info = bustub.catalog_->GetTable(table_name);
100+
return std::make_shared<bustub::SeqScanPlanNode>(std::move(schema), table_info->oid_, table_name);
101+
}
102+
if (rel_op == "org.apache.calcite.interpreter.Bindables$BindableProject") {
103+
std::vector<bustub::AbstractExpressionRef> exprs;
104+
std::vector<json> exprs_json = root_rel["exprs"];
105+
exprs.reserve(exprs_json.size());
106+
for (const auto &expr_json : exprs_json) {
107+
exprs.emplace_back(TransformExpr(expr_json, input_plan_nodes));
108+
}
109+
return std::make_shared<bustub::ProjectionPlanNode>(std::move(schema), exprs, input_plan_nodes[0]);
110+
}
111+
throw bustub::Exception("unsupported rel type");
112+
}
113+
114+
auto BuildPlanNodeFromJson(bustub::BustubInstance &bustub, json plan) -> bustub::AbstractPlanNodeRef {
115+
std::map<std::string, json> rels;
116+
std::vector<json> json_rels = plan["rels"];
117+
for (const auto &rel : json_rels) {
118+
rels[rel["id"]] = rel;
119+
}
120+
json root_rel = *json_rels.rbegin();
121+
return TransformRootRel(bustub, rels, root_rel);
122+
}
123+
124+
// NOLINTNEXTLINE
125+
auto main(int argc, char **argv) -> int {
126+
auto bustub = std::make_unique<bustub::BustubInstance>();
127+
128+
// HTTP
129+
httplib::Server svr;
130+
131+
svr.set_exception_handler([](const auto &req, auto &res, std::exception_ptr ep) {
132+
std::string exception;
133+
try {
134+
std::rethrow_exception(ep);
135+
} catch (bustub::Exception &e) {
136+
exception = e.what();
137+
} catch (std::exception &e) {
138+
exception = e.what();
139+
} catch (...) { // See the following NOTE
140+
exception = "unknown exception";
141+
}
142+
res.set_content(exception, "text/plain");
143+
res.status = 500;
144+
});
145+
146+
svr.Post("/sql", [&bustub](const httplib::Request &req, httplib::Response &res) {
147+
std::stringstream ss;
148+
bustub::SimpleStreamWriter writer(ss, false);
149+
json data = json::parse(req.body);
150+
std::cerr << "SQL request: " << data["sql"] << std::endl;
151+
bustub->ExecuteSql(data["sql"], writer);
152+
res.set_content(ss.str(), "text/plain");
153+
});
154+
155+
svr.Post("/plan", [&bustub](const httplib::Request &req, httplib::Response &res) {
156+
std::stringstream ss;
157+
bustub::SimpleStreamWriter writer(ss, false);
158+
std::cerr << "Plan request:";
159+
json json_plan = json::parse(req.body);
160+
std::cerr << json_plan << std::endl;
161+
auto plan = BuildPlanNodeFromJson(*bustub, json_plan);
162+
std::cerr << "BusTub plan:" << std::endl << plan->ToString(true) << std::endl;
163+
bustub->ExecutePlan(plan, writer);
164+
res.set_content(ss.str(), "text/plain");
165+
});
166+
167+
svr.Get("/catalog", [&bustub](const httplib::Request &req, httplib::Response &res) {
168+
std::cerr << "Catalog request" << std::endl;
169+
auto tables = bustub->catalog_->GetTableNames();
170+
std::vector<json> catalog;
171+
for (const auto &tbl_name : tables) {
172+
auto table = bustub->catalog_->GetTable(tbl_name);
173+
std::vector<json> schema;
174+
for (size_t c = 0; c < table->schema_.GetColumnCount(); c++) {
175+
auto col = table->schema_.GetColumn(c);
176+
switch (col.GetType()) {
177+
case bustub::TypeId::BIGINT: {
178+
schema.emplace_back(std::map<std::string, std::string>{{"name", col.GetName()}, {"type", "bigint"}});
179+
break;
180+
}
181+
case bustub::TypeId::INTEGER: {
182+
schema.emplace_back(std::map<std::string, std::string>{{"name", col.GetName()}, {"type", "integer"}});
183+
break;
184+
}
185+
case bustub::TypeId::VARCHAR: {
186+
schema.emplace_back(std::map<std::string, std::string>{{"name", col.GetName()}, {"type", "varchar"}});
187+
break;
188+
}
189+
default:
190+
throw bustub::Exception("unsupported column type");
191+
}
192+
}
193+
catalog.emplace_back(std::map<std::string, json>{{std::string("name"), json(table->name_)},
194+
{std::string("oid"), json(table->oid_)},
195+
{std::string("schema"), json(schema)}});
196+
}
197+
res.set_content(json(std::map<std::string, json>{{"catalog", catalog}}).dump(), "text/plain");
198+
});
199+
200+
std::cerr << "BusTub HTTP server listening" << std::endl;
201+
202+
svr.listen("127.0.0.1", 23333);
203+
204+
return 0;
205+
}

0 commit comments

Comments
 (0)