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