CMU15445 FALL2022 Project #3 - Query Execution

CMU15445 FALL2022 Project #3 - Query Execution

在这里插入图片描述

Task #1 - Access Method Executors

SeqScan

  • 顺序扫描

  • /** 要执行的顺序扫描计划节点 */
    const SeqScanPlanNode *plan_;
    /** 用于扫描表的迭代器,初始化为空指针 */
    TableIterator table_iter_ = {nullptr, RID(), nullptr};
    /** 指向存储实际表数据的表堆的指针 */
    TableHeap *table_heap_;
    /** 指向包含模式和元数据的表信息的指针 */
    TableInfo *table_info_;
    
  • Next

    • 通过table_iter_取相应内容后++即可

Insert

  • /** 要执行的插入计划节点 */
    const InsertPlanNode *plan_;
    /** 子执行器,对于插入操作来说通常是 Values 执行器 */
    std::unique_ptr<AbstractExecutor> child_executor_;
    /** 标识是否已经完成插入操作 */
    bool has_inserted_ = false;
    
  • Init

    • child_executor_->Init()
  • Next

    • 获取待插入的表信息及其索引列表
    • 循环调用child_executor_->Next获取待插入的tuplerid
      • InsertTuple插入tuplerid指定内容
      • 根据索引的模式从数据元组中构造索引元组,并从索引中删除
    • 最后返回插入操作的影响行数

Delete

  • /** 要执行的删除计划节点 */
    const DeletePlanNode *plan_;
    /** 从中获取要删除的元组的RID的子执行器 */
    std::unique_ptr<AbstractExecutor> child_executor_;
    /** 标志,指示是否已删除元组 */
    bool has_deleted_ = false;
    
  • Init

    • child_executor_->Init()
  • Next

    • 获取待删除的表信息及其索引列表
    • 循环调用child_executor_->Next获取待删除的tuplerid
      • MarkDelete删除rid指定内容
      • 根据索引的模式从数据元组中构造索引元组,并从索引中删除
    • 最后返回插入操作的影响行数

IndexScan

  • /** 要执行的index扫描节点 */
    const IndexScanPlanNode *plan_;
    /** 待扫描表的 B+ 树索引 */
    BPlusTreeIndexForOneIntegerColumn *tree_;
    /** 待扫描表的 B+ 树索引迭代器 */
    BPlusTreeIndexIteratorForOneIntegerColumn table_iter_;
    /** 待扫描表的表堆 */
    TableHeap *table_heap_;
    
  • tree_ = dynamic_cast<BPlusTreeIndexForOneIntegerColumn *>(
              exec_ctx_->GetCatalog()->GetIndex(plan_->GetIndexOid())->index_.get());
    table_iter_ = tree_->GetBeginIterator();
    IndexInfo *index_info = exec_ctx_->GetCatalog()->GetIndex(plan_->GetIndexOid());
    table_heap_ = exec_ctx_->GetCatalog()->GetTable(index_info->table_name_)->table_.get();
    
  • Next

    • 通过table_iter_取相应内容后++即可
Task #2 - Aggregation & Join Executors

Aggregation

/** 聚合计划节点 */
const AggregationPlanNode *plan_;
/** 子执行器,用于生成计算聚合的元组 */
std::unique_ptr<AbstractExecutor> child_;
/** 简单的聚合哈希表 */
SimpleAggregationHashTable aht_;
/** 简单的聚合哈希表迭代器 */
SimpleAggregationHashTable::Iterator aht_iterator_;
  • Init
    • 调用子执行器的 Init 方法
    • 循环调用子执行器的 Next 方法获取元组,并将其插入到聚合哈希表中
    • 如果聚合哈希表为空且输出模式只有一列,则插入初始组合
    • 将哈希表迭代器设置为哈希表的起始位置
  • Next
    • 如果哈希表迭代器到达末尾,返回 false 表示没有更多元组
    • 否则,从迭代器中获取键(分组依据)和值(聚合结果),构造输出元组并递增迭代器
    • 返回 true 表示成功获取下一个元组

NestedLoopJoin

/** NestedLoopJoin计划节点 */
const NestedLoopJoinPlanNode *plan_;
/** 左表子执行器,对于循环连接操作来说通常是左表上的 Scan 执行器 */
std::unique_ptr<AbstractExecutor> left_executor_;
/** 右表子执行器,对于循环连接操作来说通常是右表上的 Scan 执行器 */
std::unique_ptr<AbstractExecutor> right_executor_;
/** 连接结果集 */
std::queue<Tuple> results_;
  • Init
    • 调用左执行器和右执行器的 Init 方法
    • 从左执行器和右执行器中获取所有元组并分别存储在 left_tuplesright_tuples 向量中
    • 通过嵌套循环依次连接left_tuplesright_tuples中的所有元组
      • 使用连接条件评估每对元组是否匹配
      • 如果匹配,将连接结果追加到结果集(results_)中
      • 对于左连接,如果左元组没有匹配的右元组,右元组中的字段用空值填充,并将连接结果追加到结果集中
  • Next
    • 从结果集队列中获取下一个连接后的元组
    • 如果结果集为空,返回 false 表示没有更多元组
    • 否则,返回 true 表示成功获取下一个元组

NestedIndexJoin

/** nested index join计划节点 */
const NestedIndexJoinPlanNode *plan_;
/** 左表子执行器,对于索引连接操作来说通常是左表上的 Scan 执行器 */
std::unique_ptr<AbstractExecutor> left_executor_;
/** 连接结果集 */
std::queue<Tuple> results_;
  • Init
    • 调用左执行器的 Init 方法
    • 获取右表的索引信息和表信息,包括索引对象、表堆和表模式
    • 从左执行器中获取所有元组
      • 对于每个左元组,使用连接字段的值在右表的索引中查找匹配的 RID
      • 对于每个匹配的右表 RID,获取对应的右元组,将左元组和右元组的值组合并添加到结果集中
      • 如果左元组没有匹配的右元组且连接类型为左连接,将右元组的字段填充为空值,并将组合结果添加到结果集中
  • Next
      • 从结果集队列中获取下一个连接后的元组
      • 如果结果集为空,返回 false 表示没有更多元组
      • 否则,返回 true 表示成功获取下一个元组
Task #3 - Sort + Limit Executors and Top-N Optimization

Sort

/** 排序计划节点 */
const SortPlanNode *plan_;
/** 子执行器,用于生成需要排序的元组 */
std::unique_ptr<AbstractExecutor> child_;
/** 存储子执行器生成的元组的向量 */
std::vector<Tuple> child_tuples_;
/** 子元组向量的常量迭代器 */
std::vector<Tuple>::const_iterator child_iter_;
  • Init
    • 调用子执行器的 Init 方法
    • 从子执行器中获取所有元组,并存储在 child_tuples_ 向量中
    • 使用 std::sort函数对 child_tuples_进行排序,排序依据为计划节点中的排序键和排序类型
      • 对每对元组,依次比较每个排序键的值
      • 根据排序类型(升序或降序)决定排序顺序
      • 如果所有排序键的值都相等,则认为这对元组相等
    • 初始化迭代器 child_iter_ 指向排序后的元组向量的起始位置
  • Next
    • 从排序后的元组向量中依次获取元组
    • 如果迭代器达到向量末尾,返回 false 表示没有更多元组
    • 否则,返回 true 表示成功获取下一个元组,并更新迭代器

Limit

/** 要执行的限制计划节点 */
const LimitPlanNode *plan_;
/** 从中获取元组的子执行器 */
std::unique_ptr<AbstractExecutor> child_;
/** 限制值 */
std::size_t limit_;
  • Init
    • 调用子执行器的 Init 方法,初始化子执行器
    • 从限制计划节点中获取限制值,并存储在成员变量 limit_
  • Next
    • 检查当前限制值 limit_ 是否大于零
    • 如果限制值大于零,则尝试从子执行器获取下一个元组
      • 调用子执行器的 Next 方法,如果成功获取到元组,则将限制值 limit_ 减一,并返回 true 表示成功获取下一个元组
    • 如果限制值不大于零或子执行器没有更多元组,则返回 false 表示没有更多元组

Top-N Optimization Rule

/** 要执行的 Top-N 计划节点 */
const TopNPlanNode *plan_;
/** 子执行器,用于获取元组 */
std::unique_ptr<AbstractExecutor> child_;
/** Top-N 查询中的 N 值 */
std::size_t n_;
/** 存储排序后的子元组 */
std::vector<Tuple> child_tuples_;
/** 子元组向量的迭代器 */
std::vector<Tuple>::const_iterator child_iter_;
  • Init
    • 调用子执行器的 Init 方法,初始化子执行器。
    • 获取 Top-N 计划节点中的 N 值,并存储在成员变量 n_ 中。
    • 定义用于比较元组的函数 comparator,根据计划节点中的排序键和排序类型进行比较。
      • 遍历计划节点中的排序键,比较元组的值,根据排序类型(升序或降序)决定排序顺序。
      • 如果所有排序键的值都相等,则认为元组相等。
    • 使用定义的比较函数构建优先队列 pq,注意使用 greater,因为默认 priority_queue 是最大堆。
    • 从子执行器获取所有元组,将其插入到优先队列中:
      • 如果优先队列的大小小于 N,则直接插入元组。
      • 如果优先队列的大小已达到 N,且新元组比堆顶元组更优,则替换堆顶元组。
    • 将优先队列中的元素转移到 child_tuples_ 向量中,并反转该向量以保证元组按正确顺序排列。
  • Next
    • 从排序后的元组向量中依次获取元组。
    • 如果 N 值小于等于零或迭代器已到达向量末尾,返回 false 表示没有更多元组。
    • 否则,返回 true 表示成功获取下一个元组,并更新迭代器和 N 值。

sort_limit_as_topn

  • 递归优化子节点:首先,函数会遍历plan的子节点,对每个子节点调用OptimizeSortLimitAsTopN函数进行递归优化。这是一个自底向上的递归优化过程,会一直递归到叶子节点。
  • 克隆优化后的计划:递归优化完成后,函数会使用CloneWithChildren方法克隆当前节点,并将优化后的子节点替换原有的子节点。这样就完成了对当前节点的优化。
  • 检查Limit节点:接下来,函数会检查当前优化后的计划是否是一个Limit节点。如果是,就获取Limit的值,并确保Limit节点有且仅有一个子节点。
  • 检查Sort节点:如果Limit节点的子节点是一个Sort节点,那么就进一步获取Sort节点的排序方式(OrderBy)。
  • 替换为TopN节点:最后,如果Limit节点的子节点是Sort节点,函数会将Limit节点和Sort节点替换为一个TopN节点,并保留与Sort节点相同的子节点、排序方式和Limit值。
  • 返回优化后的计划:最终,函数返回优化后的计划。

最后放个线上测试结果
在这里插入图片描述

  • 44
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值