Skip to content

Commit c139e9f

Browse files
test(p3): add nlj init check (#526)
1 parent 20aa4a5 commit c139e9f

File tree

13 files changed

+285
-12
lines changed

13 files changed

+285
-12
lines changed

src/common/bustub_instance.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "common/util/string_util.h"
2121
#include "concurrency/lock_manager.h"
2222
#include "concurrency/transaction.h"
23+
#include "execution/check_options.h"
2324
#include "execution/execution_engine.h"
2425
#include "execution/executor_context.h"
2526
#include "execution/executors/mock_scan_executor.h"
@@ -172,10 +173,11 @@ see the execution plan of your query.
172173
WriteOneCell(help, writer);
173174
}
174175

175-
auto BustubInstance::ExecuteSql(const std::string &sql, ResultWriter &writer) -> bool {
176+
auto BustubInstance::ExecuteSql(const std::string &sql, ResultWriter &writer,
177+
std::shared_ptr<CheckOptions> check_options) -> bool {
176178
auto txn = txn_manager_->Begin();
177179
try {
178-
auto result = ExecuteSqlTxn(sql, writer, txn);
180+
auto result = ExecuteSqlTxn(sql, writer, txn, std::move(check_options));
179181
txn_manager_->Commit(txn);
180182
delete txn;
181183
return result;
@@ -186,7 +188,8 @@ auto BustubInstance::ExecuteSql(const std::string &sql, ResultWriter &writer) ->
186188
}
187189
}
188190

189-
auto BustubInstance::ExecuteSqlTxn(const std::string &sql, ResultWriter &writer, Transaction *txn) -> bool {
191+
auto BustubInstance::ExecuteSqlTxn(const std::string &sql, ResultWriter &writer, Transaction *txn,
192+
std::shared_ptr<CheckOptions> check_options) -> bool {
190193
if (!sql.empty() && sql[0] == '\\') {
191194
// Internal meta-commands, like in `psql`.
192195
if (sql == "\\dt") {
@@ -257,6 +260,9 @@ auto BustubInstance::ExecuteSqlTxn(const std::string &sql, ResultWriter &writer,
257260

258261
// Execute the query.
259262
auto exec_ctx = MakeExecutorContext(txn);
263+
if (check_options != nullptr) {
264+
exec_ctx->InitCheckOptions(std::move(check_options));
265+
}
260266
std::vector<Tuple> result_set{};
261267
is_successful &= execution_engine_->Execute(optimized_plan, &result_set, txn, exec_ctx.get());
262268

src/execution/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_library(
88
fmt_impl.cpp
99
hash_join_executor.cpp
1010
index_scan_executor.cpp
11+
init_check_executor.cpp
1112
insert_executor.cpp
1213
limit_executor.cpp
1314
mock_scan_executor.cpp

src/execution/executor_factory.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "execution/executors/filter_executor.h"
2222
#include "execution/executors/hash_join_executor.h"
2323
#include "execution/executors/index_scan_executor.h"
24+
#include "execution/executors/init_check_executor.h"
2425
#include "execution/executors/insert_executor.h"
2526
#include "execution/executors/limit_executor.h"
2627
#include "execution/executors/mock_scan_executor.h"
@@ -33,6 +34,7 @@
3334
#include "execution/executors/update_executor.h"
3435
#include "execution/executors/values_executor.h"
3536
#include "execution/plans/filter_plan.h"
37+
#include "execution/plans/init_check_plan.h"
3638
#include "execution/plans/mock_scan_plan.h"
3739
#include "execution/plans/projection_plan.h"
3840
#include "execution/plans/sort_plan.h"
@@ -95,6 +97,17 @@ auto ExecutorFactory::CreateExecutor(ExecutorContext *exec_ctx, const AbstractPl
9597
auto nested_loop_join_plan = dynamic_cast<const NestedLoopJoinPlanNode *>(plan.get());
9698
auto left = ExecutorFactory::CreateExecutor(exec_ctx, nested_loop_join_plan->GetLeftPlan());
9799
auto right = ExecutorFactory::CreateExecutor(exec_ctx, nested_loop_join_plan->GetRightPlan());
100+
auto check_options_set = exec_ctx->GetCheckOptions()->check_options_set_;
101+
if (check_options_set.find(CheckOption::ENABLE_NLJ_CHECK) != check_options_set.end()) {
102+
auto left_init_check_plan = dynamic_cast<const InitCheckPlanNode *>(nested_loop_join_plan->GetLeftPlan().get());
103+
auto right_init_check_plan =
104+
dynamic_cast<const InitCheckPlanNode *>(nested_loop_join_plan->GetRightPlan().get());
105+
auto left_check = std::make_unique<InitCheckExecutor>(exec_ctx, left_init_check_plan, std::move(left));
106+
auto right_check = std::make_unique<InitCheckExecutor>(exec_ctx, right_init_check_plan, std::move(right));
107+
exec_ctx->AddCheckExecutor(left_check.get(), right_check.get());
108+
return std::make_unique<NestedLoopJoinExecutor>(exec_ctx, nested_loop_join_plan, std::move(left_check),
109+
std::move(right_check));
110+
}
98111
return std::make_unique<NestedLoopJoinExecutor>(exec_ctx, nested_loop_join_plan, std::move(left),
99112
std::move(right));
100113
}

src/execution/init_check_executor.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// BusTub
4+
//
5+
// init_check_executor.cpp
6+
//
7+
// Identification: src/execution/init_check_executor.cpp
8+
//
9+
// Copyright (c) 2015-2021, Carnegie Mellon University Database Group
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "execution/executors/init_check_executor.h"
14+
15+
namespace bustub {
16+
17+
InitCheckExecutor::InitCheckExecutor(ExecutorContext *exec_ctx, const InitCheckPlanNode *plan,
18+
std::unique_ptr<AbstractExecutor> &&child_executor)
19+
: AbstractExecutor{exec_ctx}, child_executor_{std::move(child_executor)} {}
20+
21+
void InitCheckExecutor::Init() {
22+
if (!child_executor_) {
23+
return;
24+
}
25+
n_init_++;
26+
// Initialize the child executor
27+
child_executor_->Init();
28+
}
29+
30+
auto InitCheckExecutor::Next(Tuple *tuple, RID *rid) -> bool {
31+
if (!child_executor_) {
32+
return EXECUTOR_EXHAUSTED;
33+
}
34+
35+
// Emit the next tuple
36+
n_next_++;
37+
return child_executor_->Next(tuple, rid);
38+
}
39+
40+
} // namespace bustub

src/include/common/bustub_instance.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "catalog/catalog.h"
2626
#include "common/config.h"
2727
#include "common/util/string_util.h"
28+
#include "execution/check_options.h"
2829
#include "libfort/lib/fort.hpp"
2930
#include "type/value.h"
3031

@@ -226,12 +227,14 @@ class BustubInstance {
226227
/**
227228
* Execute a SQL query in the BusTub instance.
228229
*/
229-
auto ExecuteSql(const std::string &sql, ResultWriter &writer) -> bool;
230+
auto ExecuteSql(const std::string &sql, ResultWriter &writer, std::shared_ptr<CheckOptions> check_options = nullptr)
231+
-> bool;
230232

231233
/**
232234
* Execute a SQL query in the BusTub instance with provided txn.
233235
*/
234-
auto ExecuteSqlTxn(const std::string &sql, ResultWriter &writer, Transaction *txn) -> bool;
236+
auto ExecuteSqlTxn(const std::string &sql, ResultWriter &writer, Transaction *txn,
237+
std::shared_ptr<CheckOptions> check_options = nullptr) -> bool;
235238

236239
/**
237240
* FOR TEST ONLY. Generate test tables in this BusTub instance.

src/include/execution/check_options.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2022 RisingLight Project Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <memory>
16+
#include <unordered_set>
17+
18+
#pragma once
19+
20+
namespace bustub {
21+
22+
enum class CheckOption : uint8_t {
23+
ENABLE_NLJ_CHECK = 0,
24+
ENABLE_TOPN_CHECK = 1,
25+
};
26+
27+
/**
28+
* The CheckOptions class contains the set of check options used for testing
29+
* executor logic.
30+
*/
31+
class CheckOptions {
32+
public:
33+
std::unordered_set<CheckOption> check_options_set_;
34+
};
35+
36+
}; // namespace bustub

src/include/execution/execution_engine.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "concurrency/transaction_manager.h"
2121
#include "execution/executor_context.h"
2222
#include "execution/executor_factory.h"
23+
#include "execution/executors/init_check_executor.h"
2324
#include "execution/plans/abstract_plan.h"
2425
#include "storage/table/tuple.h"
2526

@@ -63,6 +64,7 @@ class ExecutionEngine {
6364
try {
6465
executor->Init();
6566
PollExecutor(executor.get(), plan, result_set);
67+
PerformChecks(exec_ctx);
6668
} catch (const ExecutionException &ex) {
6769
#ifndef NDEBUG
6870
LOG_ERROR("Error Encountered in Executor Execution: %s", ex.what());
@@ -76,6 +78,15 @@ class ExecutionEngine {
7678
return executor_succeeded;
7779
}
7880

81+
void PerformChecks(ExecutorContext *exec_ctx) {
82+
for (const auto &[left_executor, right_executor] : exec_ctx->GetNLJCheckExecutorSet()) {
83+
auto casted_left_executor = dynamic_cast<const InitCheckExecutor *>(left_executor);
84+
auto casted_right_executor = dynamic_cast<const InitCheckExecutor *>(right_executor);
85+
BUSTUB_ASSERT(casted_left_executor->GetNextCount() == casted_right_executor->GetInitCount(),
86+
"nlj check failed, are you initialising the right executor correctly?");
87+
}
88+
}
89+
7990
private:
8091
/**
8192
* Poll the executor until exhausted, or exception escapes.

src/include/execution/executor_context.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@
1212

1313
#pragma once
1414

15+
#include <deque>
16+
#include <memory>
1517
#include <unordered_set>
1618
#include <utility>
1719
#include <vector>
1820

1921
#include "catalog/catalog.h"
2022
#include "concurrency/transaction.h"
23+
#include "execution/check_options.h"
24+
#include "execution/executors/abstract_executor.h"
2125
#include "storage/page/tmp_tuple_page.h"
2226

2327
namespace bustub {
28+
class AbstractExecutor;
2429
/**
2530
* ExecutorContext stores all the context necessary to run an executor.
2631
*/
@@ -36,7 +41,11 @@ class ExecutorContext {
3641
*/
3742
ExecutorContext(Transaction *transaction, Catalog *catalog, BufferPoolManager *bpm, TransactionManager *txn_mgr,
3843
LockManager *lock_mgr)
39-
: transaction_(transaction), catalog_{catalog}, bpm_{bpm}, txn_mgr_(txn_mgr), lock_mgr_(lock_mgr) {}
44+
: transaction_(transaction), catalog_{catalog}, bpm_{bpm}, txn_mgr_(txn_mgr), lock_mgr_(lock_mgr) {
45+
nlj_check_exec_set_ = std::deque<std::pair<AbstractExecutor *, AbstractExecutor *>>(
46+
std::deque<std::pair<AbstractExecutor *, AbstractExecutor *>>{});
47+
check_options_ = std::make_shared<CheckOptions>();
48+
}
4049

4150
~ExecutorContext() = default;
4251

@@ -60,6 +69,23 @@ class ExecutorContext {
6069
/** @return the transaction manager */
6170
auto GetTransactionManager() -> TransactionManager * { return txn_mgr_; }
6271

72+
/** @return the set of nlj check executors */
73+
auto GetNLJCheckExecutorSet() -> std::deque<std::pair<AbstractExecutor *, AbstractExecutor *>> & {
74+
return nlj_check_exec_set_;
75+
}
76+
77+
/** @return the check options */
78+
auto GetCheckOptions() -> std::shared_ptr<CheckOptions> { return check_options_; }
79+
80+
void AddCheckExecutor(AbstractExecutor *left_exec, AbstractExecutor *right_exec) {
81+
nlj_check_exec_set_.emplace_back(left_exec, right_exec);
82+
}
83+
84+
void InitCheckOptions(std::shared_ptr<CheckOptions> &&check_options) {
85+
BUSTUB_ASSERT(check_options, "nullptr");
86+
check_options_ = std::move(check_options);
87+
}
88+
6389
private:
6490
/** The transaction context associated with this executor context */
6591
Transaction *transaction_;
@@ -71,6 +97,10 @@ class ExecutorContext {
7197
TransactionManager *txn_mgr_;
7298
/** The lock manager associated with this executor context */
7399
LockManager *lock_mgr_;
100+
/** The set of NLJ check executors associated with this executor context */
101+
std::deque<std::pair<AbstractExecutor *, AbstractExecutor *>> nlj_check_exec_set_;
102+
/** The set of check options associated with this executor context */
103+
std::shared_ptr<CheckOptions> check_options_;
74104
};
75105

76106
} // namespace bustub

src/include/execution/executors/abstract_executor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "storage/table/tuple.h"
1717

1818
namespace bustub {
19+
class ExecutorContext;
1920
/**
2021
* The AbstractExecutor implements the Volcano tuple-at-a-time iterator model.
2122
* This is the base class from which all executors in the BustTub execution
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// BusTub
4+
//
5+
// init_check_executor.h
6+
//
7+
// Identification: src/include/execution/executors/init_check_executor.h
8+
//
9+
// Copyright (c) 2015-2021, Carnegie Mellon University Database Group
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#pragma once
14+
15+
#include <memory>
16+
#include <utility>
17+
18+
#include "execution/executor_context.h"
19+
#include "execution/executors/abstract_executor.h"
20+
#include "execution/plans/init_check_plan.h"
21+
22+
namespace bustub {
23+
24+
/**
25+
* InitCheckExecutor counts the number of times the child operator calls init.
26+
*/
27+
class InitCheckExecutor : public AbstractExecutor {
28+
public:
29+
/**
30+
* Construct a new InitCheckExecutor instance.
31+
* @param exec_ctx The executor context
32+
* @param plan The init check plan to be executed
33+
* @param child_executor The child executor from which init calls are counted
34+
*/
35+
InitCheckExecutor(ExecutorContext *exec_ctx, const InitCheckPlanNode *plan,
36+
std::unique_ptr<AbstractExecutor> &&child_executor);
37+
38+
/** Initialize the InitCheck */
39+
void Init() override;
40+
41+
/**
42+
* Yield the next tuple from the child executor.
43+
* @param[out] tuple The next tuple produced by the child executor
44+
* @param[out] rid The next tuple RID produced by the child executor
45+
* @return `true` if a tuple was produced, `false` if there are no more tuples
46+
*/
47+
auto Next(Tuple *tuple, RID *rid) -> bool override;
48+
49+
/** @return The output schema for the child executor */
50+
auto GetOutputSchema() const -> const Schema & override { return plan_->OutputSchema(); };
51+
52+
/** @return The number of inits */
53+
auto GetInitCount() const -> std::size_t { return n_init_; };
54+
55+
/** @return The number of nexts */
56+
auto GetNextCount() const -> std::size_t { return n_next_; };
57+
58+
private:
59+
/** InitCheckExecutor returns `true` when it should be polled again */
60+
constexpr static const bool EXECUTOR_ACTIVE{true};
61+
62+
/** InitCheckExecutor returns `false` when child executor is exhausted */
63+
constexpr static const bool EXECUTOR_EXHAUSTED{false};
64+
65+
/** The init check plan node to be executed */
66+
const InitCheckPlanNode *plan_;
67+
68+
/** The child executor from which tuples are obtained */
69+
std::unique_ptr<AbstractExecutor> child_executor_;
70+
71+
/** The number of times init was called */
72+
std::size_t n_init_{0};
73+
std::size_t n_next_{0};
74+
};
75+
76+
} // namespace bustub

0 commit comments

Comments
 (0)