前言
第三个实验需要我们实现一个执行器(并非解释器),我认为这个实验和 project2 来说各有各的难点。project2 难在即使非常清楚了可拓展哈希表的流程,在多线程、各种锁的折腾下还是很容易出错,每一个小 TASK 都需要debug很长时间。而这个 project 难在已经实现的类、及其成员变量非常繁杂,相互之间的调用很不清晰,完全不知道各个变量的具体含义。所以在做这个 project 之前,梳理已给代码非常重要。
TASK #1 - EXECUTORS
整个 project 只有这一个 task,需要我们实现以下9个执行器:
- 读取:Sequential Scan(顺序扫描)
- 增删改查: Insert, Update, Delete
- 其他:Nested Loop Join, Hash Join, Aggregation, Limit, Distinct
思路
在前言中我们提到各种成员变量,包括
Catalog、Tuple、Schema、TableHeap、TableHeapIterator、Value、Column...等待
其每个变量都在具体数据库中有一个对应,我是通过 debug 慢慢摸索出其含义,后来发现 这篇文章 总结非常好,在做第一个执行器(Sequential Scan)的时候强烈推荐结合文章理清楚各个类的交互关系。
提示
说一下我踩的几个坑,应该是我对聚合算子本身不来熟悉,导致一些比较乌龙的错误。
一个是要注意 group by
是可以接多个列的,站在数据库层表示以 n 个完全相同的列进行分组,在代码中体现就是 AggregateKey
的成员变量 group_bys_
是一个 vector
。
二是我在看聚合的测试文件时,发现 ValueExpression
必须指定输出列是否为 is_group_by_term
。根据执行逻辑如果是则代表其为 group by
的字段,如果不是则代表其为聚合的字段。
比如这样的语句:select count(Id),City from Employee group by City
,前者则为false,后者则为 true。
后来我突发奇想,如果是这样的语句select count(Id),City,Address from Employee group by City
,其中 Address
既不是 group by
的字段,也不是聚合的字段,那该怎么支持呢?
count(ID) | City | Address |
---|---|---|
1 | Kirkland | 722 Moss Bay Blvd. |
4 | London | 14 Garrett Hill |
1 | Redmond | 4110 Old Redmond Rd. |
2 | Seattle | 507 - 20th Ave. E. Apt. 2A |
1 | Tacoma | 908 W. Capital Way |
后来想了想在聚合里这个字段也没有意义,如果一个组有多个数据,可能只会随机输出一个,真实情况下我们一般会考虑group_concat
:
select count(Id),City,group_concat(Address,' ;') from Employee group by City
count(ID) | City | group_concat(Address,’ ;’) |
---|---|---|
1 | Kirkland | 722 Moss Bay Blvd. |
4 | London | 14 Garrett Hill ;Coventry House Miner Rd. ;Edgeham Hollow Winchester Way ;7 Houndstooth Rd. |
1 | Redmond | 4110 Old Redmond Rd. |
2 | Seattle | 507 - 20th Ave. E. Apt. 2A ;4726 - 11th Ave. N.E. |
1 | Tacoma | 908 W. Capital Way |
那这样的话就又得支持新的算子了,虽然感觉上不难… 不过我暂时还没有实现 XD
最后
最重要的一点,请不要使用官方的打包命令,gradescope 上莫名其妙把一堆自带的 GTEST 给检测,其 check-lint G了一大片,后来群里大哥说加上 src/include/storage/page/tmp_tuple_page.h
可以解决,原因位置:
zip project3-submission.zip \
src/include/buffer/lru_replacer.h \
src/buffer/lru_replacer.cpp \
src/include/buffer/buffer_pool_manager_instance.h \
src/buffer/buffer_pool_manager_instance.cpp \
src/include/storage/page/hash_table_directory_page.h \
src/storage/page/hash_table_directory_page.cpp \
src/include/storage/page/hash_table_bucket_page.h \
src/storage/page/hash_table_bucket_page.cpp \
src/include/container/hash/extendible_hash_table.h \
src/container/hash/extendible_hash_table.cpp \
src/include/execution/execution_engine.h \
src/include/execution/executors/seq_scan_executor.h \
src/include/execution/executors/insert_executor.h \
src/include/execution/executors/update_executor.h \
src/include/execution/executors/delete_executor.h \
src/include/execution/executors/nested_loop_join_executor.h \
src/include/execution/executors/hash_join_executor.h \
src/include/execution/executors/aggregation_executor.h \
src/include/execution/executors/limit_executor.h \
src/include/execution/executors/distinct_executor.h \
src/execution/seq_scan_executor.cpp \
src/execution/insert_executor.cpp \
src/execution/update_executor.cpp \
src/execution/delete_executor.cpp \
src/execution/nested_loop_join_executor.cpp \
src/execution/hash_join_executor.cpp \
src/execution/aggregation_executor.cpp \
src/execution/limit_executor.cpp \
src/execution/distinct_executor.cpp \
src/include/storage/page/tmp_tuple_page.h