cmu15445 2023spring Project #3 Query Execution

背景

建议看这位大佬的文章,讲解的挺清楚。
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经验。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CMU 15445 课程的 Project 0 是一个调试练习,旨在帮助学生熟悉调试工具和技术。在这个项目中,你将开始使用 GDB 和 Valgrind 这两个常用的调试工具,以及一些其他辅助工具。以下是一些问题和步骤,帮助你完成这个练习: 1. 你需要查看项目中提供的代码,并了解它的结构和功能,这样你才能更好地理解程序的逻辑和可能出现的 bug。 2. 接下来,你需要编译项目,并确保没有编译错误。如果出现错误,你需要修复它们,这可能需要检查一些语法错误或缺失的库。 3. 一旦成功编译项目,你就可以使用 GDB 进行调试了。GDB 是一个强大的调试器,可以帮助你找出程序中的错误。你可以使用 GDB 来单步执行代码、设置断点、查看变量的值等等。通过使用 GDB,你可以逐步查看代码运行的路径,并找出程序崩溃或产生错误的原因。 4. 在使用 GDB 进行调试时,你可以通过设置断点来暂停程序的执行,并查看变量的值和程序的状态。你可以使用“break”命令在程序中设置断点,并通过“run”命令启动程序。当程序到达这个断点时,它会停止执行,你可以使用“print”命令查看变量的值,或者“step”命令逐步执行代码。 5. 另一个常用的调试工具是 Valgrind。Valgrind 可以帮助你检测内存泄漏和错误的访问方式。你可以使用“valgrind”命令来运行程序,并查看 Valgrind 的输出。它会告诉你有关程序中任何潜在问题的信息,例如未初始化的变量、访问越界等。 6. 最后,当你发现 bug 并修复它们后,可以运行各种测试用例来验证程序的正确性。测试用例可以帮助你确定程序是否按预期工作,并且在修改代码后,它们可以帮助你确保你的修复没有引入新的错误。 通过完成 CMU 15445 项目 0 的调试练习,你将掌握一些重要的调试技巧和工具,这对于进一步开发和调试软件应用程序将非常有用。希望上述步骤和建议对你有所帮助,祝你顺利完成这个项目!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值