總覽
這個lab要實現executors
,executor負責query plan(就是operator形成的樹)上的operator並執行它們,對於每個executor,需要實現:
對於這個lab,沒有SQL,執行的查詢計划都是寫好的算子樹,並且用的是火山模型,每個算子的執行器(executor)必須實現Next函數,Next函數的執行粒度是一個tuple,也就是每次執行要么返回一個tuple,要么返回一個空指針來表示當前沒有tuple了。那么每個算子executor需要實現一個循環去調用子算子的Next函數
Task#1 SYSTEM CATALOG
catalog存的信息是用來顯示一個database有什么table以及table被存在哪里,其中還存有了table的index
table怎么存的?
復習一下數據庫的存儲模型,每個頁存n個tuple,對於一個table,可以根據key,建立B+樹index,去查詢tuple
可以看到在lab中的實現中,leaf node節點的value就是page_id和slot_num,
那么頁的布局就是slotted page
catalog
catalog需要實現在數據庫中添加tables和通過name或則內部標識符table_oid_t
取tables,主要是實現:
index的添加
Task#2 EXECUTORS
需要實現sequential scans, index scans, inserts, updates, deletes, nested loop joins, nested index joins, limits with offset and aggregations等算子的executor,每個executor都有兩個方法——Init用來獲取需要scan的table和Next用來迭代讀取table中的tuple,當前算子的Next方法就是執行循環去執行子算子的Next,
調用子算子是有慣例的順序。這里的ExecutionEngine
采取了工廠模式。
ExecutionEngine
中調用ExcutionFactory類去將一個AbstractPlanNode
轉化為對應的executor,ExecutorContext
保存了一個query的查詢狀態,每次執行一個PlanNode,需要先Init,在execute,
executor的抽象的執行流程
這里拿seq_scan_executor舉例,
- 該類繼承於
AbstractExecutor
AbstractExecutor
只有一個成員函數ExecutorContext *exec_ctx_
,AbstractExecutor
還兩個純虛函數,Init和Next,這是每個executor都需要實現的ExecutorContext
中保存了一個transaction執行一個query時的context,- 一個executor還需要planNode,
SeqScanPlanNode
繼承於AbstractPlanNode
,AbstractPlanNode
兩個成員變量分別用於限定輸出的schema與其子planNode
SeqScanPlanNode
有自己的成員變量,分別用於指定其需要掃描的表與掃描表的tuple需要滿足的謂詞
- 謂詞是
AbstractExpression
,決定了返回的結果的schema
修改execution_engine.h中的異常處理?
sequential scan
- Iterator的每次迭代會獲取一個沒有被刪除的tuple
不論是在當前Pgae,還找到新的一個Page的第一個沒被刪除的tuple
- Next函數如果得到了一個tuple,那么返回true,否則false
- 由於根據predicate計算一個tuple是否可行的Evaluate函數返回的是一個Value類型
所以需要GetAs
把Value轉換為bool類型 - 創建一個column需要使用AbstractExpression類的子類ColumnValueExpression,ColumnValueExpression的Evaluate方法是主要是tuple->GetValue(schema, col_idx_);,Evaluate的第二個schema參數使用來獲取對應列的類型的
而GetValue獲取是分別獲取類型與列中的真實數據
對於varchar類型的數據,由於不定長,所以在tuple的對應column中記錄的是一個四字節的地址,而對於inline的數據(inline應該就是指不是varchar的數據),就放在tuple的對應位置可以猜測存儲的varchar數據的頭4個字節是varcahr數據的大小
傳指針返回值
Index Scans
只考慮Keysize為8的Iterator
- protected是指基類與繼承類都可以直接訪問
- 這個predicate檢查所有的列比較合理?
INSERT
- 需要做兩件事,一是把tuple插入到table中,二是更新tale的所有索引
- 插入的tuple也分為兩種,一種是原生的tuple,列的值放在InsertPlanNode之中,另一種是從其他table中復制一個tuple插入
- 用InsertPlanNode中的這個方法判斷是否是原生插入
KeyFromTuple
可以從一個table的tuple構造一個index entry的key tuple- 一個index的keyAttrs是一個數組,表明了table的tuple中的列號
- 這里涉及了編譯器的一種優化RVO
UPDATE
- update的子planNode,update的planNode用給定的謂詞去遍歷需要更新的tuple
DELETE
- Delete需要刪除tuple並在index中刪除index entry,和Update一樣,Delete一定會有一個child executor
- 為什么不刪除,而是標記,用了實現MVCC嗎?
NESTED LOOP JOIN
此處的join采取一種simple join的join方式
- join算子,需要分別執行兩個子算子
- left算子得到的每一個tuple需要與right算子的每一個tuple都判斷謂詞,都如果滿足就輸出一個結果tuple,由於Next函數是一個協程,那么需要保存當前left算子得到的當前tuple,還需要保持一個狀態,表明當前join的left算子是否遍歷完了所有的tuple
- 一個
NestedLoopJoinExecutor
尤其對應的OutputSchema
,而
OutputSchema
中是一個Column數組,每個Cloumn對象有一個
AbstractExpression
成員變量那么根據
AbstractExpression
的EvaluateJoin
中的tuple_idx_成員變量可以知道該column來子join的左table還是右table - 不過RID有啥用?
INDEX NESTED LOOP JOIN
-
要求outer table的每一個tuple去與inner的每一個tuple做謂詞判斷,不用謂詞判斷,獲取plan中的指定,通過plan設定的索引的key_schema
-
點查詢為什么要傳一個vector?
AGGREGATION
在GROUP BY子句后面包含了一個HAVING子句。HAVING類似於WHERE(唯一的差別是WHERE過濾行,HAVING過濾組)HAVING支持所有WHERE操作符。
- 這個函數用來實現聚合,當一個聚合函數還沒被使用時,將其插入map中
並作出合理的初始化
這個函數用來做實際聚合操作
- plan指定了該算子會做哪些聚合
- 一個plan的有指定的OutPutSchema,而OutPutSchema有一個Cloumn數組,其中每個Column對象指定了用來創建該Column的
AbstractExpression
,那么每次獲取一個輸出tuple中每一列就是調用OutPutSchema的每一個col的Expression聚類需要把key與val都放到output的tuple中
LIMIT
問題
- 這個三個參數是干嘛用的?key_schema用來表示key的列的樣子?
- 一個表可以有很多個index
- 一個Table就有一個TableHeap,用來存儲Table的頁且采取doubly-linked list的存儲頁的方式
- 這里返回的是一個Iterator
它重載了
->
- 創建索引的時候需要把一個表中的所有的tuple的對於index放到索引數據解構中
- 一個TablePage刪除tuple的時候,不會真的刪除,用來實現MVCC嗎?
- 用make_unique會出錯?
- 測試的時候,找到一個lab2的錯誤,在
CoalesceOrRedistribute
中Coalesce的時候忘記給sibling_node解鎖了