Skip to content

Commit

Permalink
fall 2024 p4 update (#769)
Browse files Browse the repository at this point in the history
* fall 2024 p4 update

* lint

* disable the public test

* nit: copyright and format
  • Loading branch information
J-HowHuang authored Nov 14, 2024
1 parent d8f4778 commit 03ddb25
Show file tree
Hide file tree
Showing 4 changed files with 398 additions and 6 deletions.
75 changes: 72 additions & 3 deletions src/execution/execution_common.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
//===----------------------------------------------------------------------===//
//
// BusTub
//
// execution_common.cpp
//
// Identification: src/execution/execution_common.cpp
//
// Copyright (c) 2024-2024, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "execution/execution_common.h"

#include "catalog/catalog.h"
#include "common/config.h"
#include "common/macros.h"
#include "concurrency/transaction_manager.h"
#include "fmt/core.h"
#include "storage/table/table_heap.h"
#include "type/value.h"
#include "type/value_factory.h"

namespace bustub {

Expand All @@ -23,11 +33,70 @@ auto GenerateSortKey(const Tuple &tuple, const std::vector<OrderBy> &order_bys,
* You can ignore the remaining part of this file until P4.
*/

/**
* @brief Reconstruct a tuple by applying the provided undo logs from the base tuple. All logs in the undo_logs are
* applied regardless of the timestamp
*
* @param schema The schema of the base tuple and the returned tuple.
* @param base_tuple The base tuple to start the reconstruction from.
* @param base_meta The metadata of the base tuple.
* @param undo_logs The list of undo logs to apply during the reconstruction, the front is applied first.
* @return An optional tuple that represents the reconstructed tuple. If the tuple is deleted as the result, returns
* std::nullopt.
*/
auto ReconstructTuple(const Schema *schema, const Tuple &base_tuple, const TupleMeta &base_meta,
const std::vector<UndoLog> &undo_logs) -> std::optional<Tuple> {
UNIMPLEMENTED("not implemented");
}

/**
* @brief Collects the undo logs sufficient to reconstruct the tuple w.r.t. the txn.
*
* @param rid The RID of the tuple.
* @param base_meta The metadata of the base tuple.
* @param base_tuple The base tuple.
* @param undo_link The undo link to the latest undo log.
* @param txn The transaction.
* @param txn_mgr The transaction manager.
* @return An optional vector of undo logs to pass to ReconstructTuple(). std::nullopt if the tuple did not exist at the
* time.
*/
auto CollectUndoLogs(RID rid, const TupleMeta &base_meta, const Tuple &base_tuple, std::optional<UndoLink> undo_link,
Transaction *txn, TransactionManager *txn_mgr) -> std::optional<std::vector<UndoLog>> {
UNIMPLEMENTED("not implemented");
}

/**
* @brief Generates a new undo log as the transaction tries to modify this tuple at the first time.
*
* @param schema The schema of the table.
* @param base_tuple The base tuple before the update, the one retrieved from the table heap. nullptr if the tuple is
* deleted.
* @param target_tuple The target tuple after the update. nullptr if this is a deletion.
* @param ts The timestamp of the base tuple.
* @param prev_version The undo link to the latest undo log of this tuple.
* @return The generated undo log.
*/
auto GenerateNewUndoLog(const Schema *schema, const Tuple *base_tuple, const Tuple *target_tuple, timestamp_t ts,
UndoLink prev_version) -> UndoLog {
UNIMPLEMENTED("not implemented");
}

/**
* @brief Generate the updated undo log to replace the old one, whereas the tuple is already modified by this txn once.
*
* @param schema The schema of the table.
* @param base_tuple The base tuple before the update, the one retrieved from the table heap. nullptr if the tuple is
* deleted.
* @param target_tuple The target tuple after the update. nullptr if this is a deletion.
* @param log The original undo log.
* @return The updated undo log.
*/
auto GenerateUpdatedUndoLog(const Schema *schema, const Tuple *base_tuple, const Tuple *target_tuple,
const UndoLog &log) -> UndoLog {
UNIMPLEMENTED("not implemented");
}

void TxnMgrDbg(const std::string &info, TransactionManager *txn_mgr, const TableInfo *table_info,
TableHeap *table_heap) {
// always use stderr for printing logs...
Expand Down
22 changes: 20 additions & 2 deletions src/include/execution/execution_common.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
//===----------------------------------------------------------------------===//
//
// BusTub
//
// execution_common.h
//
// Identification: src/include/execution/execution_common.h
//
// Copyright (c) 2014-2024, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//
#pragma once

#include <string>
Expand Down Expand Up @@ -44,6 +55,15 @@ auto GenerateSortKey(const Tuple &tuple, const std::vector<OrderBy> &order_bys,
auto ReconstructTuple(const Schema *schema, const Tuple &base_tuple, const TupleMeta &base_meta,
const std::vector<UndoLog> &undo_logs) -> std::optional<Tuple>;

auto CollectUndoLogs(RID rid, const TupleMeta &base_meta, const Tuple &base_tuple, std::optional<UndoLink> undo_link,
Transaction *txn, TransactionManager *txn_mgr) -> std::optional<std::vector<UndoLog>>;

auto GenerateNewUndoLog(const Schema *schema, const Tuple *base_tuple, const Tuple *target_tuple, timestamp_t ts,
UndoLink prev_version) -> UndoLog;

auto GenerateUpdatedUndoLog(const Schema *schema, const Tuple *base_tuple, const Tuple *target_tuple,
const UndoLog &log) -> UndoLog;

void TxnMgrDbg(const std::string &info, TransactionManager *txn_mgr, const TableInfo *table_info,
TableHeap *table_heap);

Expand All @@ -52,11 +72,9 @@ void TxnMgrDbg(const std::string &info, TransactionManager *txn_mgr, const Table
// To give you a sense of what can be shared across executors / transaction manager, here are the
// list of helper function names that we defined in the reference solution. You should come up with
// your own when you go through the process.
// * CollectUndoLogs
// * WalkUndoLogs
// * Modify
// * IsWriteWriteConflict
// * GenerateDiffLog
// * GenerateNullTupleForSchema
// * GetUndoLogSchema
//
Expand Down
106 changes: 106 additions & 0 deletions test/txn/txn_executor_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,112 @@ TEST(TxnExecutorTest, DISABLED_InsertDeleteConflictTest) { // NOLINT
WithTxn(txn7, CommitTxn(*bustub, _var, _txn));
}

TEST(TxnExecutorTest, DISABLED_GenerateUndoLogTest) {
{
fmt::println(stderr, "--- GenerateUndoLogTest: Simple update ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("b double,c boolean");
auto base_tuple = Tuple{{Int(0), Double(0.0), Bool(true)}, schema.get()};
auto target_tuple = Tuple{{Int(0), Double(1.0), Bool(false)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), &base_tuple, &target_tuple, 0, {});

auto tuple = ReconstructTuple(schema.get(), target_tuple, {0, false}, {undo_log});
ASSERT_TRUE(tuple.has_value());
ASSERT_TRUE(IsTupleContentEqual(*tuple, base_tuple));
}
{
fmt::println(stderr, "--- GenerateUndoLogTest: Simple delete ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto base_tuple = Tuple{{Int(0), Double(0.0), Bool(true)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), &base_tuple, nullptr, 0, {});

auto tuple = ReconstructTuple(schema.get(), base_tuple, {0, true}, {undo_log});
ASSERT_TRUE(tuple.has_value());
ASSERT_TRUE(IsTupleContentEqual(*tuple, base_tuple));
}
{
// This case is only relevant after task 4.2, where an insert may happen on a tombstone.
// Before task 4.2, insert always generates a new tuple in table heap without any undo logs.
fmt::println(stderr, "--- GenerateUndoLogTest: Simple insert ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("");
auto target_tuple = Tuple{{Int(0), Double(1.0), Bool(false)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), nullptr, &target_tuple, 0, {});

auto tuple = ReconstructTuple(schema.get(), target_tuple, {0, false}, {undo_log});
ASSERT_FALSE(tuple.has_value());
}
{
fmt::println(stderr, "--- GenerateUndoLogTest: Update twice in a txn ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("b double,c boolean");
auto base_tuple = Tuple{{Int(0), Double(0.0), Bool(true)}, schema.get()};
auto intermidiate_tuple = Tuple{{Int(0), Double(0.0), Bool(false)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), &base_tuple, &intermidiate_tuple, 0, {});
auto target_tuple = Tuple{{Int(0), Double(1.0), Bool(false)}, schema.get()};
auto updated_undo_log = GenerateUpdatedUndoLog(schema.get(), &intermidiate_tuple, &target_tuple, undo_log);

auto tuple = ReconstructTuple(schema.get(), target_tuple, {0, false}, {updated_undo_log});
ASSERT_TRUE(tuple.has_value());
ASSERT_TRUE(IsTupleContentEqual(*tuple, base_tuple));
}
{
fmt::println(stderr, "--- GenerateUndoLogTest: Update then delete in a txn ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("b double,c boolean");
auto base_tuple = Tuple{{Int(0), Double(0.0), Bool(true)}, schema.get()};
auto target_tuple = Tuple{{Int(0), Double(1.0), Bool(false)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), &base_tuple, &target_tuple, 0, {});
auto updated_undo_log = GenerateUpdatedUndoLog(schema.get(), &target_tuple, nullptr, undo_log);

auto tuple = ReconstructTuple(schema.get(), target_tuple, {0, true}, {updated_undo_log});
ASSERT_TRUE(tuple.has_value());
ASSERT_TRUE(IsTupleContentEqual(*tuple, base_tuple));
}
{
// This case is only relevant after task 4.2, where an insert may happen on a tombstone.
// Before task 4.2, insert always generates a new tuple in table heap without any undo logs.
fmt::println(stderr, "--- GenerateUndoLogTest: Insert then update in a txn ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("");
auto intermediate_tuple = Tuple{{Int(0), Double(0.0), Bool(false)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), nullptr, &intermediate_tuple, 0, {});
auto target_tuple = Tuple{{Int(0), Double(1.0), Bool(false)}, schema.get()};
auto updated_undo_log = GenerateUpdatedUndoLog(schema.get(), &intermediate_tuple, &target_tuple, undo_log);

auto tuple = ReconstructTuple(schema.get(), target_tuple, {0, false}, {updated_undo_log});
ASSERT_FALSE(tuple.has_value());
}
{
// This case is only relevant after task 4.2, where an insert may happen on a tombstone.
// Before task 4.2, insert always generates a new tuple in table heap without any undo logs.
fmt::println(stderr, "--- GenerateUndoLogTest: Insert then delete in a txn ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("");
auto intermediate_tuple = Tuple{{Int(0), Double(0.0), Bool(false)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), nullptr, &intermediate_tuple, 0, {});
auto updated_undo_log = GenerateUpdatedUndoLog(schema.get(), &intermediate_tuple, nullptr, undo_log);

auto tuple = ReconstructTuple(schema.get(), intermediate_tuple, {0, true}, {updated_undo_log});
ASSERT_FALSE(tuple.has_value());
}
{
// This case is only relevant after task 4.2, where an insert may happen on a tombstone.
// Before task 4.2, insert always generates a new tuple in table heap without any undo logs.
fmt::println(stderr, "--- GenerateUndoLogTest: Delete then insert in a txn ---");
auto schema = ParseCreateStatement("a integer,b double,c boolean");
auto partial_schema = ParseCreateStatement("b double,c boolean");
auto base_tuple = Tuple{{Int(0), Double(0.0), Bool(true)}, schema.get()};
auto undo_log = GenerateNewUndoLog(schema.get(), &base_tuple, nullptr, 0, {});
auto target_tuple = Tuple{{Int(0), Double(1.0), Bool(false)}, schema.get()};
auto updated_undo_log = GenerateUpdatedUndoLog(schema.get(), &base_tuple, &target_tuple, undo_log);

auto tuple = ReconstructTuple(schema.get(), target_tuple, {0, false}, {updated_undo_log});
ASSERT_TRUE(tuple.has_value());
ASSERT_TRUE(IsTupleContentEqual(*tuple, base_tuple));
}
}

TEST(TxnExecutorTest, DISABLED_UpdateTest1) { // NOLINT
fmt::println(stderr, "--- UpdateTest1: no undo log ---");
auto bustub = std::make_unique<BusTubInstance>();
Expand Down
Loading

0 comments on commit 03ddb25

Please sign in to comment.