CMU_15445_P3_Part1
这部分主要是实现一些基本的 Plan_Node 的 Executor, 我们可以首先通过一个列子来看, 就是 Projection
Plan_Node 的例子.
Projection
类型的 PLAN_NODE 是作为有条件的 SELECT语句 或者嵌套的 SELECT 语句的根节点, 例如:
SELECT a, b FROM t1 WHERE c > 10;
SELECT a + b FROM (SELECT a, b FROM t1 WHERE c > 10) AS sub;
但是没有条件的 SELECT 语句通常不使用 Projection
PLAN_NODE 节点.
并且 Projection 类型的 PLAN_NODE 只有一个孩子节点返回读取到的 Tuples, Projection 的 PLAN_NODE 中还包含各种表达式, 这些表达式定义了如何从孩子节点返回的 tuple 获取 Projection 的 tuple.
auto ProjectionExecutor::Next(Tuple *tuple, RID *rid) -> bool {
Tuple child_tuple{};
/**
* 从孩子节点获取 tuple, 也就是孩子节点返回 tuple.
* 并且, 这里实现了一个递归的调用机制, 从上到下, 先调用孩子节点的 Next() 函数
*/
const auto status = child_executor_->Next(&child_tuple, rid);
if (!status) {
return false;
}
/**
* 实际这部分是调用孩子节点的 Next() 函数之后的回溯部分, 孩子节点返回 tuple 到父节点之后
* 父节点根据自身的逻辑修改与判断这个 tuple 是否符合条件, 继续返回至根节点
*/
std::vector<Value> values{};
// 每一列的 Column 对应着从 孩子节点的 tuple 中取一个值
values.reserve(GetOutputSchema().GetColumnCount());
// Projection 节点通常有多个表达式, 获取孩子节点的 tuple 之后, 在回溯节点使用表达式的逻辑得到该节点的 tuple 输出
for (const auto &expr : plan_->GetExpressions()) {
values.push_back(expr->Evaluate(&child_tuple, child_executor_->GetOutputSchema()));
}
// 组合成一个 tuple 返回
*tuple = Tuple{values, &GetOutputSchema()};
return true;
}
Iterator Model
的 Executor 执行的过程类似于一个递归回溯的过程, 但是并不完全是递归回溯, 上面的代码就展示了该调用的过程, 首先会递归的调用 Next() 函数, 然后调用完之后会使用该节点的处理逻辑, 返回该节点的 tuple. 这里还有一个问题, 常见的递归回溯会将递归的过程全部执行完, 再进行回溯, 而 Iterator Model
每次只返回一个 tuple, 下次调用该节点的时候, 继续返回 tuple, 这里的实现方式在后续会揭开.