cmu15445 2023spring Project #3 Query Execution

项目3涉及实现一系列SQL执行算子,如SeqScan、Insert、Update、IndexScan等。文章详细描述了在实现过程中遇到的Bug和解决方法,包括迭代器的使用、数据转换问题、更新策略以及B+Tree索引的处理。此外,还讨论了Aggregation的Bug、NestedLoopJoin的初始化和Join类型的优化,特别是HashJoin的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

建议看这位大佬的文章,讲解的挺清楚。
project3就是实现一系列算子,本身编码并不难,难的是看懂各个接口的使用,以及许多细节处理。笔者在实验的过程中遇到了一些bug,本文会主要说明这些bug及其处理。

Task 1

SeqScan

直接用TableIterator,注意区分前置++和后置++,这个实验只定义了前置++。
由于TableIterator只能用移动构造,所以可以直接在SeqScanExecutor构造函数初始化列表中使用TableIterator移动构造函数,或者使用unique_ptr(推荐)。

Insert

大部分接口都提供了,直接用。
这里讲一个坑,insert的返回值tuple是插入个数,将Int转换为Tuple找了半天,最后还是参考projection_executor.cpp的写法,代码如下

  std::vector<Value> values;
  values.emplace_back(INTEGER, count_rows);
  *tuple = Tuple{values, &GetOutputSchema()};

另外,即使只插入0个也要返回true,为了不陷入死循环,又加了个bool类型的数据成员来判断是第一次执行还是第二次执行。

Update

也是返回更新数量,处理同上。另外,不能一条一条地更新(删除与插入),否则会陷入死循环,需要先将更新元组存储再执行更新。

IndexScan

可以直接转成B+Tree索引

tree_ = dynamic_cast<BPlusTreeIndexForTwoIntegerColumn *>(index_info_->index_.get())

对迭代器进行operator*()可以得到<key, rid>,再根据rid再page heap取tuple。

遇到了project2的一个bug,之前B+Tree删除时可能会也空节点,导致这次project出现访问end迭代器的情况。

Task #2 - Aggregation & Join Executors

Aggregation

遇到一个小bug,在第一次执行max操作时,我先将result->aggregates_[i]置为INT32_MIN,然后再执行max操作,但好像执行过程中将INT32_MIN当作最大值了…
最后改成INT32_MIN + 1了。

result->aggregates_[i] = ValueFactory::GetIntegerValue(INT32_MIN + 1);

一个关键点是输出结果是否含有key,如果没有key,则即使输出结果为空也要输出一行,但是如果有key,则输出为空。

NestedLoopJoin

  • inner join: 每次遍历完right_executor_,需要执行right_executor_->Init(),对于之前实现的算子,要特别注意Init(),别忘记初始化元数据。

  • left join: 如果right_executor_没有与left_tuple对应的,则应该与null结合。关键代码如下:

std::vector<Value> values;
for (uint32_t i = 0; i < left_schema.GetColumns().size(); ++i) {
  values.emplace_back(current_left_tuple_.GetValue(&left_schema, i));
}
for (uint32_t i = 0; i < right_schema.GetColumns().size(); ++i) {
  values.emplace_back(ValueFactory::GetNullValueByType(right_schema.GetColumn(i).GetType()));
}
*tuple = Tuple(values, &GetOutputSchema());

HashJoin & Optimizing NestedLoopJoin to HashJoin

哈希表的key一开始选为value,但是没有定义hash,会报错,可以参考聚合算子的聚合key写法,写一个HashKey。

Optimizing一开始不知道怎么下手,可以参考/src/optimizer/merge_projection.cpp。

我贴下关键代码:

  if (type == PlanType::NestedLoopJoin) { 
    ...
    if (dynamic_cast<ColumnValueExpression *>(key_expressions[0].get()) != nullptr) {  // t1.a = t2.a
      for (auto const &expr : key_expressions) {
        if (dynamic_cast<ColumnValueExpression *>(expr.get())->GetTupleIdx() == 0) {
          left_exprs.emplace_back(expr);
        } else {
          right_exprs.emplace_back(expr);
        }
      }
    } else {  // t1.a = t2.a and t1.b = t2.b and ...
    ...

    return std::make_shared<HashJoinPlanNode>(nlj_plan.output_schema_, left_child_plan, right_child_plan, left_exprs,
                                                right_exprs, nlj_plan.GetJoinType());
  }

哈希join只支持=,所以不用在乎nested的predicate。以及注意递归调用。

另外,expr属于左join还是右join需要根据tuple_index的值判断。如果有多个等式,key_expressions还会嵌套。

Task #3

Sort and limit

非常简单,但是记得init时元数据初始化,比如sort中,vector需要清空,limit中index需要置零。

贴一个排序参考

case OrderByType::ASC: {
  auto value_a = expr->Evaluate(&a, schema);
  auto value_b = expr->Evaluate(&b, schema);

  if (value_a.CompareLessThan(value_b) == CmpBool::CmpTrue) {
    return true;
  }
  if (value_a.CompareGreaterThan(value_b) == CmpBool::CmpTrue) {
    return false;
  }
}

Top-N

实现一个堆,这也不难,priority_queue走起。将sort中的比较函数实现成一个类。

将sort+limit优化成top-n别忘记递归优化。

优化

暂时没空。

评测与总结

截止提交时间(2023.05-18),gradescope上有25人完成了project2。

在这里插入图片描述
这次实验略微加深了对sql执行的理解,增加了vscode debug经验。

### CMU 15445 课程环境配置指南 对于希望参与卡内基梅隆大学 (CMU) 的数据库系统实现入门课(即 CMU 15445)的学生来说,正确设置开发环境至关重要。虽然官方文档提供了详细的指导说明[^1],这里提供一个简化版本来帮助学生快速上手。 #### 安装依赖项 为了确保项目顺利运行,在开始之前需安装必要的软件包: ```bash sudo apt-y \ git cmake g++ libtbb-dev python3-pip valgrind clang-format-7 ``` 此命令会更新本地包列表并安装一系列必需工具,包括 Git 版本控制系统、CMake 构建管理器以及 C++ 编译器等[^2]。 #### 获取源码仓库 通过克隆 GitHub 上托管的教学材料库获取最新版教材和其他资源: ```bash git clone https://github.com/CMU-db/15445-fall2021-starter-code.git db_project cd db_project ``` 这一步骤创建了一个名为 `db_project` 的新目录用于存放整个项目的文件结构。 #### 配置构建脚本 进入刚刚建立的工作区之后,按照惯例先初始化子模块再执行常规编译流程: ```bash git submodule init && git submodule update --recursive mkdir build && cd build cmake .. make -j$(nproc) ``` 上述指令序列完成了从准备到最终生成可执行程序的过程;其中 `-j$(nproc)` 参数允许利用多核处理器加速编译速度。 #### 测试安装成果 最后验证一切正常运作的方法就是尝试启动测试套件看看能否成功完成一轮完整的迭代: ```bash ./testrunner.sh ``` 如果所有单元测试均能无误通过,则表明当前机器已经具备开展后续学习活动所需的一切条件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值