├── .gitignore ├── CMakeLists.txt ├── README.md ├── VERSION.txt ├── src ├── CMakeLists.txt ├── buffer │ ├── buffer_pool_manager.cpp │ └── lru_replacer.cpp ├── catalog │ ├── column.cpp │ └── schema.cpp ├── common │ └── config.cpp ├── concurrency │ ├── lock_manager.cpp │ └── transaction_manager.cpp ├── disk │ └── disk_manager.cpp ├── hash │ └── extendible_hash.cpp ├── include │ ├── buffer │ │ ├── buffer_pool_manager.h │ │ ├── lru_replacer.h │ │ └── replacer.h │ ├── catalog │ │ ├── column.h │ │ └── schema.h │ ├── common │ │ ├── config.h │ │ ├── exception.h │ │ ├── logger.h │ │ ├── rid.h │ │ ├── rwmutex.h │ │ └── string_utility.h │ ├── concurrency │ │ ├── lock_manager.h │ │ ├── transaction.h │ │ └── transaction_manager.h │ ├── disk │ │ └── disk_manager.h │ ├── hash │ │ ├── extendible_hash.h │ │ └── hash_table.h │ ├── index │ │ ├── b_plus_tree.h │ │ ├── b_plus_tree_index.h │ │ ├── generic_key.h │ │ ├── index.h │ │ └── index_iterator.h │ ├── logging │ │ ├── log_manager.h │ │ ├── log_record.h │ │ └── log_recovery.h │ ├── page │ │ ├── b_plus_tree_internal_page.h │ │ ├── b_plus_tree_leaf_page.h │ │ ├── b_plus_tree_page.h │ │ ├── header_page.h │ │ ├── page.h │ │ └── table_page.h │ ├── sqlite │ │ ├── sqlite3.h │ │ └── sqlite3ext.h │ ├── table │ │ ├── table_heap.h │ │ ├── table_iterator.h │ │ └── tuple.h │ ├── type │ │ ├── bigint_type.h │ │ ├── boolean_type.h │ │ ├── decimal_type.h │ │ ├── integer_parent_type.h │ │ ├── integer_type.h │ │ ├── limits.h │ │ ├── numeric_type.h │ │ ├── smallint_type.h │ │ ├── tinyint_type.h │ │ ├── type.h │ │ ├── type_id.h │ │ ├── type_util.h │ │ ├── value.h │ │ └── varlen_type.h │ └── vtable │ │ └── virtual_table.h ├── index │ ├── b_plus_tree.cpp │ ├── b_plus_tree_index.cpp │ └── index_iterator.cpp ├── logging │ ├── log_manager.cpp │ └── log_recovery.cpp ├── page │ ├── b_plus_tree_internal_page.cpp │ ├── b_plus_tree_leaf_page.cpp │ ├── b_plus_tree_page.cpp │ ├── header_page.cpp │ └── table_page.cpp ├── sqlite │ ├── shell.c │ └── sqlite3.c ├── table │ ├── table_heap.cpp │ ├── table_iterator.cpp │ └── tuple.cpp ├── type │ ├── bigint_type.cpp │ ├── boolean_type.cpp │ ├── decimal_type.cpp │ ├── integer_parent_type.cpp │ ├── integer_type.cpp │ ├── smallint_type.cpp │ ├── tinyint_type.cpp │ ├── type.cpp │ ├── value.cpp │ └── varlen_type.cpp └── vtable │ └── virtual_table.cpp ├── test ├── CMakeLists.txt ├── buffer │ ├── buffer_pool_manager_test.cpp │ └── lru_replacer_test.cpp ├── common │ └── rwmutex_test.cpp ├── concurrency │ └── lock_manager_test.cpp ├── hash │ └── extendible_hash_test.cpp ├── include │ ├── logging │ │ └── common.h │ └── vtable │ │ └── testing_vtable_util.h ├── index │ ├── b_plus_tree_concurrent_test.cpp │ ├── b_plus_tree_print_test.cpp │ └── b_plus_tree_test.cpp ├── logging │ └── log_manager_test.cpp ├── table │ ├── header_page_test.cpp │ └── tuple_test.cpp ├── type │ └── type_test.cpp └── vtable │ └── virtual_table_test.cpp └── third_party ├── gmock ├── gmock-gtest-all.cc ├── gmock │ └── gmock.h ├── gmock_main.cc └── gtest │ └── gtest.h └── valgrind └── valgrind.supp /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cmake-build-debug/ 3 | build/ 4 | .vscode/ 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | if(POLICY CMP0042) 3 | cmake_policy(SET CMP0042 NEW) 4 | endif() 5 | 6 | # ---[ SQLite_VTable project 7 | project(SQLite_VTable CXX C) 8 | 9 | # ---[ CTest 10 | include(CTest) 11 | 12 | # ---[ Dependencies 13 | find_package(Threads REQUIRED) 14 | 15 | # ---[ C++1y Flags 16 | include(CheckCXXCompilerFlag) 17 | check_cxx_compiler_flag("-std=c++1y" COMPILER_SUPPORTS_CXX1y) 18 | 19 | if(COMPILER_SUPPORTS_CXX1y) 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y") 21 | else() 22 | message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++1y support. Please use a different C++ compiler.") 23 | endif() 24 | 25 | # Create a new pre-processor macro __VTableFILE__ that has a truncated 26 | # path to reduce the size of the debug log messages. 27 | # Source: http://stackoverflow.com/a/16658858 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__VTableFILE__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'") 29 | 30 | # ---[ Flags 31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wextra -Werror -march=native") 32 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-unused-private-field") #TODO: remove 33 | 34 | # -- [ Debug Flags 35 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb -fno-omit-frame-pointer -fno-optimize-sibling-calls") 36 | 37 | # --[ Output directory 38 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 39 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 40 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 41 | 42 | # ---[ Includes 43 | set(SQLITE_VTABLE_SRC_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/src/include) 44 | set(SQLITE_VTABLE_TEST_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/test/include) 45 | set(SQLITE_VTABLE_THIRD_PARTY_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/third_party) 46 | include_directories(${SQLITE_VTABLE_SRC_INCLUDE_DIR} ${SQLITE_VTABLE_TEST_INCLUDE_DIR} ${SQLITE_VTABLE_THIRD_PARTY_INCLUDE_DIR}) 47 | include_directories(BEFORE src) # This is needed for gtest. 48 | 49 | # ---[ Subdirectories 50 | add_subdirectory(src) 51 | add_subdirectory(test) 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | If you want to find the initial project code, please refer to my [initial commit](https://github.com/junjieliu2910/cmu-15-445/tree/78f36050504ac75dd5f474523e5984f77a90f8c9). 2 | 3 | PS: My initial commit is submitted after completing lab1. If you want to start from scratch, just delete all the function bodies in corresponding cpp files. 4 | 5 | 6 | The project folder of CMU15-445 7 | 8 | - [x] Project 1 9 | 10 | - [x] Project 2 11 | 12 | - [x] Project 3 13 | 14 | - [x] Project 4 15 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 15-445/645 Project Source Code 2 | ------------------------------------------------------------ 3 | Created: Nov 21 2017 @ 00:28:55 4 | Last Commit: be0f6dd92b4c3eaa52a6456bd8847773ab65b3ed 5 | 6 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | # SRC CMAKELISTS 3 | ################################################################################## 4 | 5 | # https://raw.githubusercontent.com/azadkuh/sqlite-amalgamation/master/CMakeLists.txt 6 | string(REGEX MATCH "Clang" IS_CLANG "${CMAKE_CXX_COMPILER_ID}") 7 | string(REGEX MATCH "GNU" IS_GNUXX "${CMAKE_CXX_COMPILER_ID}") 8 | string(REGEX MATCH "Linux" IS_LINUX "${CMAKE_SYSTEM_NAME}") 9 | string(REGEX MATCH "Darwin" IS_MACOS "${CMAKE_SYSTEM_NAME}") 10 | 11 | 12 | # sqlite3 compile options, modules, ... 13 | option(BUILD_ENABLE_DBSTAT_VTAB "enables dbstat virtual table" OFF) 14 | option(BUILD_ENABLE_FTS3 "enables full text searches version 3" OFF) 15 | option(BUILD_ENABLE_FTS5 "enables full text searches version 5" OFF) 16 | option(BUILD_ENABLE_ICU "enables international components fir unicode" OFF) 17 | option(BUILD_ENABLE_JSON1 "enables JSON SQL functins" OFF) 18 | option(BUILD_ENABLE_RBU "enables resumable bulk update extension" OFF) 19 | option(BUILD_ENABLE_RTREE "enables R*TRee index extension" OFF) 20 | 21 | # other build options 22 | option(BUILD_SHARED_LIBS "build sqlite3 as a unix shared (so/dylib) library" ON) 23 | option(BUILD_SHELL "build sqlite3 shell application" ON) 24 | if(MSVC) 25 | option(BUILD_MT_RELEASE "static msvcrt build" ON) 26 | endif() 27 | 28 | # compiler settings 29 | if(IS_CLANG OR IS_GNUXX) 30 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3 -g0 -Wall -Wextra -pedantic -Wcast-align -Wunused -Wno-unused-parameter") 31 | elseif(MSVC) 32 | set(CMAKE_C_FLAGS_RELEASE "-nologo -Zc:wchar_t -FS -O2 -MD -Zc:strictStrings -W3") 33 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -MDd") 34 | if(BUILD_MT_RELEASE) 35 | string(REPLACE "-MD" "-MT" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") 36 | string(REPLACE "-MDd" "-MTd" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") 37 | endif() 38 | set(CMAKE_SHARED_LINKER_FLAGS_RELEASE 39 | "${CMAKE_SHARED_LINKER_FLAGS_RELASE} \ 40 | /Gy /GF /OPT:REF /OPT:ICF") 41 | if(BUILD_SHARED_LIBS) 42 | message(SEND_ERROR "@error: shared lib is not supported under Visual Studio.\n \ 43 | please compile as a static lib.") 44 | endif() 45 | endif() 46 | 47 | if(BUILD_ENABLE_DBSTAT_VTAB) 48 | add_definitions(-DSQLITE_ENABLE_DBSTAT_VTAB) 49 | endif() 50 | if(BUILD_ENABLE_FTS3) 51 | add_definitions(-DSQLITE_ENABLE_FTS3) 52 | endif() 53 | if(BUILD_ENABLE_FTS5) 54 | add_definitions(-DSQLITE_ENABLE_FTS5) 55 | endif() 56 | if(BUILD_ENABLE_ICU) 57 | add_definitions(-DSQLITE_ENABLE_ICU) 58 | endif() 59 | if(BUILD_ENABLE_JSON1) 60 | add_definitions(-DSQLITE_ENABLE_JSON1) 61 | endif() 62 | if(BUILD_ENABLE_RBU) 63 | add_definitions(-DSQLITE_ENABLE_RBU) 64 | endif() 65 | if(BUILD_ENABLE_RTREE) 66 | add_definitions(-DSQLITE_ENABLE_RTREE) 67 | endif() 68 | # Multi-thread. 69 | # In this mode, SQLite can be safely used by multiple threads provided that no single 70 | # database connection is used simultaneously in two or more threads. 71 | add_definitions(-DSQLITE_THREADSAFE=2) 72 | 73 | # sqlite3 as library 74 | add_library(sqlite3 sqlite/sqlite3.c include/sqlite/sqlite3ext.h include/sqlite/sqlite3.h) 75 | if(IS_LINUX) 76 | if(BUILD_SHARED_LIBS) 77 | target_link_libraries(sqlite3 pthread dl) 78 | endif() 79 | elseif(IS_MACOS AND BUILD_SHARED_LIBS) 80 | set(CMAKE_SKIP_RPATH 0) # make dynamic linking work for Mac 81 | endif() 82 | 83 | # shell app 84 | if(BUILD_SHELL) 85 | add_executable(sqlite3_shell sqlite/shell.c) 86 | target_link_libraries(sqlite3_shell sqlite3) 87 | set_target_properties(sqlite3_shell PROPERTIES OUTPUT_NAME sqlite3) 88 | 89 | if(IS_LINUX) 90 | if(NOT BUILD_SHARED_LIBS) 91 | target_link_libraries(sqlite3_shell pthread dl) 92 | endif() 93 | endif() 94 | endif() 95 | 96 | # --- [ sqlite_vtable 97 | file(GLOB_RECURSE srcs ${PROJECT_SOURCE_DIR}/src/*/*.cpp) 98 | file(GLOB sqlite_srcs ${PROJECT_SOURCE_DIR}/src/sqlite/*.c) 99 | list(REMOVE_ITEM srcs ${sqlite_srcs}) 100 | add_library(vtable SHARED ${srcs}) 101 | -------------------------------------------------------------------------------- /src/buffer/lru_replacer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * LRU implementation 3 | */ 4 | #include "buffer/lru_replacer.h" 5 | #include "page/page.h" 6 | 7 | namespace cmudb { 8 | 9 | template 10 | LRUReplacer::LRUReplacer() {} 11 | 12 | template 13 | LRUReplacer::~LRUReplacer() {} 14 | 15 | /* 16 | * Insert value into LRU 17 | */ 18 | template 19 | void LRUReplacer::Insert(const T &value) { 20 | std::lock_guard guard(mtx); 21 | if (hash.find(value) != hash.end()) { 22 | auto iter = hash[value]; 23 | lst.erase(iter); 24 | } 25 | 26 | lst.insert(lst.begin(), value); 27 | hash[value] = lst.begin(); 28 | } 29 | 30 | /* If LRU is non-empty, pop the head member from LRU to argument "value", and 31 | * return true. If LRU is empty, return false 32 | */ 33 | template 34 | bool LRUReplacer::Victim(T &value) { 35 | std::lock_guard guard(mtx); 36 | if (lst.empty()) { 37 | return false; 38 | } 39 | value = *lst.rbegin(); 40 | lst.pop_back(); 41 | hash.erase(value); 42 | return true; 43 | } 44 | 45 | /* 46 | * Remove value from LRU. If removal is successful, return true, otherwise 47 | * return false 48 | */ 49 | template 50 | bool LRUReplacer::Erase(const T &value) { 51 | std::lock_guard guard(mtx); 52 | if (hash.find(value) == hash.end()) { 53 | return false; 54 | } 55 | auto iter = hash[value]; 56 | lst.erase(iter); 57 | hash.erase(value); 58 | return true; 59 | } 60 | 61 | template 62 | size_t LRUReplacer::Size() { 63 | std::lock_guard guard(mtx); 64 | return hash.size(); 65 | } 66 | 67 | template 68 | class LRUReplacer; 69 | // test only 70 | template 71 | class LRUReplacer; 72 | 73 | } // namespace cmudb -------------------------------------------------------------------------------- /src/catalog/column.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * column.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "catalog/column.h" 8 | 9 | namespace cmudb { 10 | 11 | void Column::SetLength(int32_t column_length) { 12 | // Set the column length based on whether it is inlined 13 | if (is_inlined) { 14 | fixed_length = column_length; 15 | variable_length = 0; 16 | } else { 17 | fixed_length = sizeof(int32_t); 18 | variable_length = column_length; 19 | } 20 | } 21 | 22 | void Column::SetInlined() { 23 | switch (column_type) { 24 | case TypeId::VARCHAR: 25 | is_inlined = false; 26 | break; 27 | 28 | default: 29 | is_inlined = true; 30 | break; 31 | } 32 | } 33 | 34 | std::string Column::ToString() const { 35 | std::ostringstream os; 36 | 37 | os << "Column[" << column_name << ", " << Type::TypeIdToString(column_type) 38 | << ", " 39 | << "Offset:" << column_offset << ", "; 40 | 41 | if (is_inlined) { 42 | os << "FixedLength:" << fixed_length; 43 | } else { 44 | os << "VarLength:" << variable_length; 45 | } 46 | os << "]"; 47 | return (os.str()); 48 | } 49 | 50 | } // namespace cmudb -------------------------------------------------------------------------------- /src/catalog/schema.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * schema.cpp 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "catalog/schema.h" 10 | 11 | namespace cmudb { 12 | 13 | // Construct schema from vector of Column 14 | Schema::Schema(const std::vector &columns) : tuple_is_inlined(true) { 15 | int32_t column_offset = 0; 16 | for (size_t index = 0; index < columns.size(); index++) { 17 | Column column = columns[index]; 18 | // handle uninlined column 19 | if (column.IsInlined() == false) { 20 | tuple_is_inlined = false; 21 | uninlined_columns.push_back(index); 22 | } 23 | // set column offset 24 | column.column_offset = column_offset; 25 | column_offset += column.GetFixedLength(); 26 | 27 | // add column 28 | this->columns.push_back(std::move(column)); 29 | } 30 | // set tuple length 31 | length = column_offset; 32 | } 33 | 34 | /* 35 | * CopySchema() - Copies the schema into a new schema object with index_list 36 | * as indices to copy 37 | * The returned schema is created by new operator, and the caller is responsible 38 | * for destroying it. 39 | */ 40 | Schema *Schema::CopySchema(const Schema *schema, const std::vector &ids) { 41 | std::vector column_list; 42 | // Reserve some space to avoid multiple malloc() calls 43 | column_list.reserve(ids.size()); 44 | // For each column index, push the column 45 | for (int id : ids) { 46 | // Make sure the index does not refer to invalid element 47 | assert(id < schema->GetColumnCount()); 48 | column_list.push_back(schema->columns[id]); 49 | } 50 | 51 | Schema *ret_schema = new Schema(column_list); 52 | return ret_schema; 53 | } 54 | 55 | std::string Schema::ToString() const { 56 | std::ostringstream os; 57 | 58 | os << "Schema[" 59 | << "NumColumns:" << GetColumnCount() << ", " 60 | << "IsInlined:" << tuple_is_inlined << ", " 61 | << "Length:" << length << "]"; 62 | 63 | bool first = true; 64 | os << " :: ("; 65 | for (int i = 0; i < GetColumnCount(); i++) { 66 | if (first) { 67 | first = false; 68 | } else { 69 | os << ", "; 70 | } 71 | os << columns[i].ToString(); 72 | } 73 | os << ")"; 74 | 75 | return os.str(); 76 | } 77 | 78 | // Compare two schemas 79 | bool Schema::operator==(const Schema &other) const { 80 | if (other.GetColumnCount() != GetColumnCount() || 81 | other.IsInlined() != IsInlined()) { 82 | return false; 83 | } 84 | 85 | for (int column_itr = 0; column_itr < other.GetColumnCount(); column_itr++) { 86 | const Column &column_info = other.GetColumn(column_itr); 87 | const Column &other_column_info = GetColumn(column_itr); 88 | 89 | if (column_info != other_column_info) 90 | return false; 91 | } 92 | 93 | return true; 94 | } 95 | 96 | bool Schema::operator!=(const Schema &other) const { return !(*this == other); } 97 | 98 | } // namespace cmudb 99 | -------------------------------------------------------------------------------- /src/common/config.cpp: -------------------------------------------------------------------------------- 1 | #include "common/config.h" 2 | 3 | namespace cmudb { 4 | std::atomic ENABLE_LOGGING(false); // for virtual table 5 | std::chrono::duration LOG_TIMEOUT = 6 | std::chrono::seconds(1); 7 | } 8 | -------------------------------------------------------------------------------- /src/concurrency/lock_manager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * lock_manager.cpp 3 | */ 4 | 5 | #include "concurrency/lock_manager.h" 6 | 7 | namespace cmudb { 8 | 9 | //Chech whether current transaction is valid to lock 10 | bool LockManager::IsValidToLock(Transaction* txn){ 11 | if(txn->GetState() == TransactionState::ABORTED) return false; 12 | if(txn->GetState() == TransactionState::COMMITTED) return false; 13 | if(txn->GetState() == TransactionState::SHRINKING){ 14 | txn->SetState(TransactionState::ABORTED); 15 | return false; 16 | } 17 | return true; 18 | } 19 | 20 | /* 21 | Question for this part: 22 | 1. If there are several txns that are waiting, and all are older than current one. 23 | When the lock release, should I grant the lock to the first required txn or the 24 | oldest txn in the waiting list ? 25 | 26 | 2. Is upgraded lock has highest priority ? 27 | 28 | In this implementation, I grant to the oldest txn in the waiting list. 29 | Since otherwise I have to abort all the other incompatiable waiting txn. 30 | */ 31 | bool LockManager::LockShared(Transaction *txn, const RID &rid) { 32 | 33 | std::unique_lock locker(mutex_); 34 | 35 | if(!IsValidToLock(txn)) return false; 36 | // No lock is granted 37 | auto found_list = lock_table_.find(rid); 38 | if(found_list == lock_table_.end()){ 39 | lock_table_.insert({rid, std::make_shared(txn->GetTransactionId(), LockMode::SHARED, true)}); 40 | locker.unlock(); 41 | txn->GetSharedLockSet()->insert(rid); 42 | return true; 43 | } 44 | 45 | //Chech whether current granted lock is shared 46 | auto locklist = lock_table_[rid]; 47 | txn_id_t tid = txn->GetTransactionId(); 48 | if(locklist->CanAddShardLock()){ 49 | // First lock is shared 50 | locklist->Push_front(tid, LockMode::SHARED, true); 51 | locker.unlock(); 52 | txn->GetSharedLockSet()->insert(rid); 53 | return true; 54 | }else{ 55 | // First lock is exclusive, wait die 56 | if(tid > locklist->GetOldest()){ 57 | // younger than held txn, abort 58 | txn->SetState(TransactionState::ABORTED); 59 | return false; 60 | }else{ 61 | locklist->Add(tid, LockMode::SHARED, false); 62 | cond.wait(locker, [&](){ 63 | return locklist->Begin()->tid_ == tid; 64 | }); 65 | locklist->Hold(tid); 66 | locker.unlock(); 67 | txn->GetSharedLockSet()->insert(rid); 68 | return true; 69 | } 70 | } 71 | } 72 | 73 | bool LockManager::LockExclusive(Transaction *txn, const RID &rid) { 74 | std::unique_lock locker(mutex_); 75 | if(!IsValidToLock(txn)) return false; 76 | 77 | // Rid not locked yet 78 | auto found_list = lock_table_.find(rid); 79 | if(found_list == lock_table_.end()){ 80 | lock_table_.insert({rid, std::make_shared(txn->GetTransactionId(), LockMode::EXCLUSIVE, true)}); 81 | locker.unlock(); 82 | txn->GetExclusiveLockSet()->insert(rid); 83 | return true; 84 | } 85 | 86 | // Wait-die 87 | auto lock_list = lock_table_[rid]; 88 | txn_id_t tid = txn->GetTransactionId(); 89 | if(tid > lock_list->GetOldest()){ 90 | txn->SetState(TransactionState::ABORTED); 91 | return false; 92 | }else{ 93 | lock_list->Add(tid, LockMode::EXCLUSIVE, false); 94 | cond.wait(locker, [&](){ 95 | return lock_list->Begin()->tid_ == tid; 96 | }); 97 | lock_list->Hold(tid); 98 | locker.unlock(); 99 | txn->GetExclusiveLockSet()->insert(rid); 100 | return true; 101 | } 102 | } 103 | 104 | bool LockManager::LockUpgrade(Transaction *txn, const RID &rid) { 105 | std::unique_lock locker(mutex_); 106 | if(!IsValidToLock(txn)) return false; 107 | 108 | auto found_list = lock_table_.find(rid); 109 | if(found_list == lock_table_.end()){return false;} 110 | 111 | auto lock_list = lock_table_[rid]; 112 | txn_id_t tid = txn->GetTransactionId(); 113 | /* 114 | Not sure whether upgrade should follow wait-die 115 | */ 116 | if(tid > lock_list->GetOldest()){ 117 | txn->SetState(TransactionState::ABORTED); 118 | return false; 119 | }else{ 120 | lock_list->Remove(tid); 121 | lock_list->Add(tid, LockMode::EXCLUSIVE, false); 122 | return true; 123 | } 124 | } 125 | 126 | /* 127 | Strict 2PL 128 | Unlock until committed or aborted 129 | */ 130 | bool LockManager::Unlock(Transaction *txn, const RID &rid) { 131 | 132 | std::unique_lock locker(mutex_); 133 | if(strict_2PL_){ 134 | if(txn->GetState()!=TransactionState::ABORTED && 135 | txn->GetState()!=TransactionState::COMMITTED){ 136 | // Can not unlock in growing or shrinking state 137 | txn->SetState(TransactionState::ABORTED); 138 | } 139 | return false; 140 | }else{ 141 | if(txn->GetState() == TransactionState::GROWING){ 142 | txn->SetState(TransactionState::SHRINKING); 143 | } 144 | } 145 | 146 | /* 147 | Exclusive lock must be the first one 148 | */ 149 | txn_id_t tid = txn->GetTransactionId(); 150 | auto lock_list = lock_table_.find(rid)->second; 151 | auto item = lock_list->Find(tid); 152 | item.held_ = false; 153 | if(item.mode_ == LockMode::SHARED){ 154 | txn->GetSharedLockSet()->erase(rid); 155 | }else{ 156 | txn->GetExclusiveLockSet()->erase(rid); 157 | } 158 | if(item.mode_ == LockMode::EXCLUSIVE || lock_list->IsFirst(tid)){ 159 | cond.notify_all(); // Notify all waiting thread 160 | } 161 | lock_list->Remove(tid); 162 | return true; 163 | } 164 | 165 | } // namespace cmudb 166 | -------------------------------------------------------------------------------- /src/concurrency/transaction_manager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * transaction_manager.cpp 3 | * 4 | */ 5 | #include "concurrency/transaction_manager.h" 6 | #include "table/table_heap.h" 7 | 8 | #include 9 | namespace cmudb { 10 | 11 | Transaction *TransactionManager::Begin() { 12 | Transaction *txn = new Transaction(next_txn_id_++); 13 | 14 | if (ENABLE_LOGGING) { 15 | // TODO: write log and update transaction's prev_lsn here 16 | LogRecord record(txn->GetTransactionId(), txn->GetPrevLSN(), LogRecordType::BEGIN); 17 | txn->SetPrevLSN(log_manager_->AppendLogRecord(record)); 18 | } 19 | 20 | return txn; 21 | } 22 | 23 | void TransactionManager::Commit(Transaction *txn) { 24 | txn->SetState(TransactionState::COMMITTED); 25 | // truly delete before commit 26 | auto write_set = txn->GetWriteSet(); 27 | while (!write_set->empty()) { 28 | auto &item = write_set->back(); 29 | auto table = item.table_; 30 | if (item.wtype_ == WType::DELETE) { 31 | // this also release the lock when holding the page latch 32 | table->ApplyDelete(item.rid_, txn); 33 | } 34 | write_set->pop_back(); 35 | } 36 | write_set->clear(); 37 | 38 | if (ENABLE_LOGGING) { 39 | // TODO: write log and update transaction's prev_lsn here 40 | LogRecord record(txn->GetTransactionId(), txn->GetPrevLSN(), LogRecordType::COMMIT); 41 | auto commit_lsn = log_manager_->AppendLogRecord(record); 42 | // make sure the commit record is flushed to disk 43 | while(log_manager_->GetPersistentLSN() < commit_lsn){ 44 | // Still try to find better solution 45 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 46 | } 47 | } 48 | 49 | // release all the lock 50 | std::unordered_set lock_set; 51 | for (auto item : *txn->GetSharedLockSet()) 52 | lock_set.emplace(item); 53 | for (auto item : *txn->GetExclusiveLockSet()) 54 | lock_set.emplace(item); 55 | // release all the lock 56 | for (auto locked_rid : lock_set) { 57 | lock_manager_->Unlock(txn, locked_rid); 58 | } 59 | } 60 | 61 | void TransactionManager::Abort(Transaction *txn) { 62 | txn->SetState(TransactionState::ABORTED); 63 | // rollback before releasing lock 64 | auto write_set = txn->GetWriteSet(); 65 | while (!write_set->empty()) { 66 | auto &item = write_set->back(); 67 | auto table = item.table_; 68 | if (item.wtype_ == WType::DELETE) { 69 | LOG_DEBUG("rollback delete"); 70 | table->RollbackDelete(item.rid_, txn); 71 | } else if (item.wtype_ == WType::INSERT) { 72 | LOG_DEBUG("rollback insert"); 73 | table->ApplyDelete(item.rid_, txn); 74 | } else if (item.wtype_ == WType::UPDATE) { 75 | LOG_DEBUG("rollback update"); 76 | table->UpdateTuple(item.tuple_, item.rid_, txn); 77 | } 78 | write_set->pop_back(); 79 | } 80 | write_set->clear(); 81 | 82 | if (ENABLE_LOGGING) { 83 | // TODO: write log and update transaction's prev_lsn here 84 | LogRecord record(txn->GetTransactionId(), txn->GetPrevLSN(), LogRecordType::ABORT); 85 | auto commit_lsn = log_manager_->AppendLogRecord(record); 86 | // make sure the commit record is flushed to disk 87 | while(log_manager_->GetPersistentLSN() < commit_lsn){ 88 | // Still try to find better solution 89 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 90 | } 91 | } 92 | 93 | // release all the lock 94 | std::unordered_set lock_set; 95 | for (auto item : *txn->GetSharedLockSet()) 96 | lock_set.emplace(item); 97 | for (auto item : *txn->GetExclusiveLockSet()) 98 | lock_set.emplace(item); 99 | // release all the lock 100 | for (auto locked_rid : lock_set) { 101 | lock_manager_->Unlock(txn, locked_rid); 102 | } 103 | } 104 | } // namespace cmudb 105 | -------------------------------------------------------------------------------- /src/disk/disk_manager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * disk_manager.cpp 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common/logger.h" 11 | #include "disk/disk_manager.h" 12 | 13 | namespace cmudb { 14 | 15 | static char *buffer_used = nullptr; 16 | 17 | /** 18 | * Constructor: open/create a single database file & log file 19 | * @input db_file: database file name 20 | */ 21 | DiskManager::DiskManager(const std::string &db_file) 22 | : file_name_(db_file), next_page_id_(0), num_flushes_(0), flush_log_(false), 23 | flush_log_f_(nullptr) { 24 | std::string::size_type n = file_name_.find("."); 25 | if (n == std::string::npos) { 26 | LOG_DEBUG("wrong file format"); 27 | return; 28 | } 29 | log_name_ = file_name_.substr(0, n) + ".log"; 30 | 31 | log_io_.open(log_name_, 32 | std::ios::binary | std::ios::in | std::ios::app | std::ios::out); 33 | // directory or file does not exist 34 | if (!log_io_.is_open()) { 35 | log_io_.clear(); 36 | // create a new file 37 | log_io_.open(log_name_, std::ios::binary | std::ios::trunc | std::ios::app | 38 | std::ios::out); 39 | log_io_.close(); 40 | // reopen with original mode 41 | log_io_.open(log_name_, std::ios::binary | std::ios::in | std::ios::app | 42 | std::ios::out); 43 | } 44 | 45 | db_io_.open(db_file, 46 | std::ios::binary | std::ios::in | std::ios::out | std::ios::out); 47 | // directory or file does not exist 48 | if (!db_io_.is_open()) { 49 | db_io_.clear(); 50 | // create a new file 51 | db_io_.open(db_file, std::ios::binary | std::ios::trunc | std::ios::out); 52 | db_io_.close(); 53 | // reopen with original mode 54 | db_io_.open(db_file, std::ios::binary | std::ios::in | std::ios::out); 55 | } 56 | } 57 | 58 | DiskManager::~DiskManager() { 59 | db_io_.close(); 60 | log_io_.close(); 61 | } 62 | 63 | /** 64 | * Write the contents of the specified page into disk file 65 | */ 66 | void DiskManager::WritePage(page_id_t page_id, const char *page_data) { 67 | size_t offset = page_id * PAGE_SIZE; 68 | // set write cursor to offset 69 | db_io_.seekp(offset); 70 | db_io_.write(page_data, PAGE_SIZE); 71 | // check for I/O error 72 | if (db_io_.bad()) { 73 | LOG_DEBUG("I/O error while writing"); 74 | return; 75 | } 76 | // needs to flush to keep disk file in sync 77 | db_io_.flush(); 78 | } 79 | 80 | /** 81 | * Read the contents of the specified page into the given memory area 82 | */ 83 | void DiskManager::ReadPage(page_id_t page_id, char *page_data) { 84 | int offset = page_id * PAGE_SIZE; 85 | // check if read beyond file length 86 | if (offset > GetFileSize(file_name_)) { 87 | LOG_DEBUG("I/O error while reading"); 88 | // std::cerr << "I/O error while reading" << std::endl; 89 | } else { 90 | // set read cursor to offset 91 | db_io_.seekp(offset); 92 | db_io_.read(page_data, PAGE_SIZE); 93 | // if file ends before reading PAGE_SIZE 94 | int read_count = db_io_.gcount(); 95 | if (read_count < PAGE_SIZE) { 96 | LOG_DEBUG("Read less than a page"); 97 | // std::cerr << "Read less than a page" << std::endl; 98 | memset(page_data + read_count, 0, PAGE_SIZE - read_count); 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Write the contents of the log into disk file 105 | * Only return when sync is done, and only perform sequence write 106 | */ 107 | void DiskManager::WriteLog(char *log_data, int size) { 108 | // enforce swap log buffer 109 | assert(log_data != buffer_used); 110 | buffer_used = log_data; 111 | 112 | if (size == 0) // no effect on num_flushes_ if log buffer is empty 113 | return; 114 | 115 | flush_log_ = true; 116 | 117 | if (flush_log_f_ != nullptr) 118 | // used for checking non-blocking flushing 119 | assert(flush_log_f_->wait_for(std::chrono::seconds(10)) == 120 | std::future_status::ready); 121 | 122 | num_flushes_ += 1; 123 | // sequence write 124 | log_io_.write(log_data, size); 125 | 126 | // check for I/O error 127 | if (log_io_.bad()) { 128 | LOG_DEBUG("I/O error while writing log"); 129 | return; 130 | } 131 | // needs to flush to keep disk file in sync 132 | log_io_.flush(); 133 | flush_log_ = false; 134 | } 135 | 136 | /** 137 | * Read the contents of the log into the given memory area 138 | * Always read from the beginning and perform sequence read 139 | * @return: false means already reach the end 140 | */ 141 | bool DiskManager::ReadLog(char *log_data, int size, int offset) { 142 | if (offset >= GetFileSize(log_name_)) { 143 | // LOG_DEBUG("end of log file"); 144 | // LOG_DEBUG("file size is %d", GetFileSize(log_name_)); 145 | return false; 146 | } 147 | log_io_.seekp(offset); 148 | log_io_.read(log_data, size); 149 | // if log file ends before reading "size" 150 | int read_count = log_io_.gcount(); 151 | if (read_count < size) { 152 | log_io_.clear(); 153 | memset(log_data + read_count, 0, size - read_count); 154 | } 155 | 156 | return true; 157 | } 158 | 159 | /** 160 | * Allocate new page (operations like create index/table) 161 | * For now just keep an increasing counter 162 | */ 163 | page_id_t DiskManager::AllocatePage() { return next_page_id_++; } 164 | 165 | /** 166 | * Deallocate page (operations like drop index/table) 167 | * Need bitmap in header page for tracking pages 168 | */ 169 | void DiskManager::DeallocatePage(__attribute__((unused)) page_id_t page_id) { 170 | return; 171 | } 172 | 173 | /** 174 | * Returns number of flushes made so far 175 | */ 176 | int DiskManager::GetNumFlushes() const { return num_flushes_; } 177 | 178 | /** 179 | * Returns true if the log is currently being flushed 180 | */ 181 | bool DiskManager::GetFlushState() const { return flush_log_; } 182 | 183 | /** 184 | * Private helper function to get disk file size 185 | */ 186 | int DiskManager::GetFileSize(const std::string &file_name) { 187 | struct stat stat_buf; 188 | int rc = stat(file_name.c_str(), &stat_buf); 189 | return rc == 0 ? stat_buf.st_size : -1; 190 | } 191 | 192 | } // namespace cmudb 193 | -------------------------------------------------------------------------------- /src/hash/extendible_hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hash/extendible_hash.h" 5 | #include "page/page.h" 6 | 7 | namespace cmudb { 8 | 9 | /* 10 | * constructor 11 | * array_size: fixed array size for each bucket 12 | */ 13 | template 14 | ExtendibleHash::ExtendibleHash(size_t size): globalDepth(0), bucketSizeLimit(size) { 15 | bucketDirectory.push_back(std::make_shared(0)); 16 | } 17 | 18 | /* 19 | * helper function to calculate the hashing address of input key 20 | */ 21 | template 22 | size_t ExtendibleHash::HashKey(const K &key) { 23 | return std::hash{}(key); 24 | } 25 | 26 | /* 27 | * helper function to return global depth of hash table 28 | * NOTE: you must implement this function in order to pass test 29 | */ 30 | template 31 | int ExtendibleHash::GetGlobalDepth() const { 32 | return globalDepth; 33 | } 34 | 35 | /* 36 | * helper function to return local depth of one specific bucket 37 | * NOTE: you must implement this function in order to pass test 38 | */ 39 | template 40 | int ExtendibleHash::GetLocalDepth(int bucket_id) const { 41 | return bucketDirectory[bucket_id]->localDepth; 42 | } 43 | 44 | /* 45 | * helper function to return current number of bucket in hash table 46 | */ 47 | template 48 | int ExtendibleHash::GetNumBuckets() const { 49 | return static_cast(bucketDirectory.size()); 50 | } 51 | 52 | /* 53 | * lookup function to find value associate with input key 54 | */ 55 | template 56 | bool ExtendibleHash::Find(const K &key, V &value) { 57 | std::lock_guard guard(mtx); 58 | std::shared_ptr bucket = getBucket(key); 59 | if (bucket == nullptr || bucket->contents.find(key) == bucket->contents.end()) { 60 | return false; 61 | } 62 | value = bucket->contents[key]; 63 | return true; 64 | } 65 | 66 | /* 67 | * delete entry in hash table 68 | * Shrink & Combination is not required for this project 69 | */ 70 | template 71 | bool ExtendibleHash::Remove(const K &key) { 72 | std::lock_guard guard(mtx); 73 | 74 | std::shared_ptr bucket = getBucket(key); 75 | if (bucket->contents.find(key) == bucket->contents.end()) { 76 | return false; 77 | } 78 | 79 | bucket->contents.erase(key); 80 | return true; 81 | } 82 | 83 | /* 84 | * insert entry in hash table 85 | * Split & Redistribute bucket when there is overflow and if necessary increase 86 | * global depth 87 | */ 88 | template 89 | void ExtendibleHash::Insert(const K &key, const V &value) { 90 | std::lock_guard guard(mtx); 91 | 92 | std::shared_ptr target = getBucket(key); 93 | int index = getBucketIndex(HashKey(key)); 94 | while (target->contents.size() == bucketSizeLimit) { 95 | if (target->localDepth == globalDepth) { 96 | size_t length = bucketDirectory.size(); 97 | for(size_t i = 0; i < length; i++){ 98 | bucketDirectory.push_back(bucketDirectory[i]); 99 | } 100 | globalDepth++; 101 | } 102 | int mask = (1 << target->localDepth ); 103 | 104 | auto a = std::make_shared(target->localDepth + 1); 105 | auto b = std::make_shared(target->localDepth + 1); 106 | for(auto item : target->contents){ 107 | size_t newKey = HashKey(item.first); 108 | if(newKey & mask){ 109 | b->contents.insert(item); 110 | }else{ 111 | a->contents.insert(item); 112 | } 113 | } 114 | for(size_t i = 0; i < bucketDirectory.size(); i++){ 115 | if(bucketDirectory[i] == target){ 116 | if(i & mask){ 117 | bucketDirectory[i] = b; 118 | }else{ 119 | bucketDirectory[i] = a; 120 | } 121 | } 122 | } 123 | target = getBucket(key); 124 | index = getBucketIndex(HashKey(key)); 125 | } 126 | 127 | bucketDirectory[index]->contents[key] = value; 128 | } 129 | 130 | template 131 | int ExtendibleHash::getBucketIndex(size_t hashKey) const { 132 | return static_cast(hashKey & ((1 << globalDepth) - 1)); 133 | } 134 | 135 | template 136 | std::shared_ptr::Bucket> ExtendibleHash::getBucket(const K &key) { 137 | auto ret = bucketDirectory[getBucketIndex(HashKey(key))]; 138 | return ret; 139 | }; 140 | 141 | template 142 | class ExtendibleHash; 143 | 144 | template 145 | class ExtendibleHash::iterator>; 146 | 147 | // test purpose 148 | template 149 | class ExtendibleHash; 150 | 151 | template 152 | class ExtendibleHash::iterator>; 153 | 154 | template 155 | class ExtendibleHash; 156 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/buffer/buffer_pool_manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * buffer_pool_manager.h 3 | * 4 | * Functionality: The simplified Buffer Manager interface allows a client to 5 | * new/delete pages on disk, to read a disk page into the buffer pool and pin 6 | * it, also to unpin a page in the buffer pool. 7 | */ 8 | 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include "buffer/lru_replacer.h" 16 | #include "disk/disk_manager.h" 17 | #include "hash/extendible_hash.h" 18 | #include "logging/log_manager.h" 19 | #include "page/page.h" 20 | 21 | namespace cmudb { 22 | class BufferPoolManager { 23 | public: 24 | BufferPoolManager(size_t pool_size, DiskManager *disk_manager, 25 | LogManager *log_manager = nullptr); 26 | 27 | ~BufferPoolManager(); 28 | 29 | Page *FetchPage(page_id_t page_id); 30 | 31 | bool UnpinPage(page_id_t page_id, bool is_dirty); 32 | 33 | bool FlushPage(page_id_t page_id); 34 | 35 | Page *NewPage(page_id_t &page_id); 36 | 37 | bool DeletePage(page_id_t page_id); 38 | 39 | private: 40 | size_t pool_size_; // number of pages in buffer pool 41 | Page *pages_; // array of pages 42 | DiskManager *disk_manager_; 43 | LogManager *log_manager_; 44 | //HashTable *page_table_; // to keep track of pages 45 | std::unordered_map *page_table_; 46 | Replacer *replacer_; // to find an unpinned page for replacement 47 | std::list *free_list_; // to find a free page for replacement 48 | std::mutex latch_; // to protect shared data structure 49 | 50 | void pin_page(Page* p){ 51 | p->pin_count_++; 52 | } 53 | }; 54 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/buffer/lru_replacer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * lru_replacer.h 3 | * 4 | * Functionality: The buffer pool manager must maintain a LRU list to collect 5 | * all the pages that are unpinned and ready to be swapped. The simplest way to 6 | * implement LRU is a FIFO queue, but remember to dequeue or enqueue pages when 7 | * a page changes from unpinned to pinned, or vice-versa. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "buffer/replacer.h" 13 | #include "hash/extendible_hash.h" 14 | #include 15 | #include 16 | #include 17 | 18 | namespace cmudb { 19 | 20 | template 21 | class LRUReplacer : public Replacer { 22 | public: 23 | // do not change public interface 24 | LRUReplacer(); 25 | 26 | ~LRUReplacer(); 27 | 28 | void Insert(const T &value); 29 | 30 | bool Victim(T &value); 31 | 32 | bool Erase(const T &value); 33 | 34 | size_t Size(); 35 | 36 | private: 37 | // add your member variables here 38 | std::unordered_map::iterator> hash; 39 | std::list lst; 40 | std::mutex mtx; 41 | }; 42 | 43 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/buffer/replacer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * replacer.h 3 | * 4 | * Abstract class for replacer, your LRU should implement those methods 5 | */ 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace cmudb { 11 | 12 | template class Replacer { 13 | public: 14 | Replacer() {} 15 | virtual ~Replacer() {} 16 | virtual void Insert(const T &value) = 0; 17 | virtual bool Victim(T &value) = 0; 18 | virtual bool Erase(const T &value) = 0; 19 | virtual size_t Size() = 0; 20 | }; 21 | 22 | } // namespace cmudb 23 | -------------------------------------------------------------------------------- /src/include/catalog/column.h: -------------------------------------------------------------------------------- 1 | /** 2 | * column.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "common/exception.h" 10 | #include "type/type.h" 11 | 12 | namespace cmudb { 13 | 14 | class Column { 15 | friend class Schema; 16 | 17 | public: 18 | Column() : column_type(TypeId::INVALID), fixed_length(-1) { 19 | // Nothing to see... 20 | } 21 | 22 | Column(TypeId value_type, int32_t column_length, std::string column_name) 23 | : column_type(value_type), fixed_length(-1), column_name(column_name) { 24 | // only VARCHAR type is not inlined store 25 | SetInlined(); 26 | // We should not have an inline value of length 0 27 | if (is_inlined && column_length <= 0) 28 | throw Exception(EXCEPTION_TYPE_CONSTRAINT, 29 | "inline type must pass in column_length"); 30 | SetLength(column_length); 31 | } 32 | 33 | //===--------------------------------------------------------------------===// 34 | // ACCESSORS 35 | //===--------------------------------------------------------------------===// 36 | 37 | // Set inlined 38 | void SetInlined(); 39 | 40 | // Set the appropriate column length 41 | void SetLength(int32_t column_length); 42 | 43 | // set column offset in schema 44 | int32_t GetOffset() const { return column_offset; } 45 | 46 | std::string GetName() const { return column_name; } 47 | 48 | int32_t GetLength() const { 49 | if (is_inlined) 50 | return fixed_length; 51 | else 52 | return variable_length; 53 | } 54 | 55 | int32_t GetFixedLength() const { return fixed_length; } 56 | 57 | int32_t GetVariableLength() const { return variable_length; } 58 | 59 | inline TypeId GetType() const { return column_type; } 60 | 61 | inline bool IsInlined() const { return is_inlined; } 62 | 63 | // Compare two column objects 64 | bool operator==(const Column &other) const { 65 | if (other.column_type != column_type || other.is_inlined != is_inlined) { 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | bool operator!=(const Column &other) const { return !(*this == other); } 72 | 73 | // Get a string representation for debugging 74 | std::string ToString() const; 75 | 76 | //===--------------------------------------------------------------------===// 77 | // MEMBERS 78 | //===--------------------------------------------------------------------===// 79 | 80 | private: 81 | // value type of column 82 | TypeId column_type; // = TypeId::INVALID; 83 | 84 | // if the column is not inlined, this is set to pointer size 85 | // else, it is set to length of the fixed length column 86 | int32_t fixed_length; 87 | 88 | // if the column is inlined, this is set to 0 89 | // else, it is set to length of the variable length column 90 | int32_t variable_length = -1; 91 | 92 | // name of the column 93 | std::string column_name; 94 | 95 | // is the column inlined ? 96 | bool is_inlined = false; 97 | 98 | // offset of column in tuple 99 | int32_t column_offset = -1; 100 | }; 101 | 102 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/catalog/schema.h: -------------------------------------------------------------------------------- 1 | /** 2 | * schema.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "catalog/column.h" 11 | #include "type/type.h" 12 | 13 | namespace cmudb { 14 | 15 | class Schema { 16 | public: 17 | //===--------------------------------------------------------------------===// 18 | // Static factory methods to construct schema objects 19 | //===--------------------------------------------------------------------===// 20 | // Construct schema from vector of Column 21 | Schema(const std::vector &columns); 22 | 23 | // Copy Schema, use to construct indexed tuple 24 | static Schema *CopySchema(const Schema *schema, const std::vector &ids); 25 | 26 | // Compare two schemas 27 | bool operator==(const Schema &other) const; 28 | 29 | bool operator!=(const Schema &other) const; 30 | 31 | //===--------------------------------------------------------------------===// 32 | // Schema accessors 33 | //===--------------------------------------------------------------------===// 34 | 35 | inline int32_t GetOffset(const int column_id) const { 36 | return columns[column_id].GetOffset(); 37 | } 38 | 39 | inline TypeId GetType(const int column_id) const { 40 | return columns[column_id].GetType(); 41 | } 42 | 43 | // Return appropriate length based on whether column is inlined 44 | inline int32_t GetAppropriateLength(const int column_id) const { 45 | auto is_inlined = columns[column_id].IsInlined(); 46 | int32_t column_length; 47 | 48 | if (is_inlined) { 49 | column_length = GetLength(column_id); 50 | } else { 51 | column_length = GetVariableLength(column_id); 52 | } 53 | 54 | return column_length; 55 | } 56 | 57 | // Returns fixed length 58 | inline int32_t GetLength(const int column_id) const { 59 | return columns[column_id].GetFixedLength(); 60 | } 61 | 62 | inline int32_t GetVariableLength(const int column_id) const { 63 | return columns[column_id].GetVariableLength(); 64 | } 65 | 66 | inline bool IsInlined(const int column_id) const { 67 | return columns[column_id].IsInlined(); 68 | } 69 | 70 | inline const Column GetColumn(const int column_id) const { 71 | return columns[column_id]; 72 | } 73 | 74 | // column id start with 0 75 | inline int GetColumnID(std::string col_name) const { 76 | int i; 77 | for (i = 0; i < GetColumnCount(); ++i) { 78 | if (columns[i].GetName() == col_name) { 79 | return i; 80 | } 81 | } 82 | return -1; 83 | } 84 | 85 | inline const std::vector &GetUnlinedColumns() const { 86 | return uninlined_columns; 87 | } 88 | 89 | inline const std::vector &GetColumns() const { return columns; } 90 | 91 | // Return the number of columns in the schema for the tuple. 92 | inline int GetColumnCount() const { return static_cast(columns.size()); } 93 | 94 | inline int GetUnlinedColumnCount() const { 95 | return static_cast(uninlined_columns.size()); 96 | } 97 | 98 | // Return the number of bytes used by one tuple. 99 | inline int32_t GetLength() const { return length; } 100 | 101 | // Returns a flag indicating whether all columns are inlined 102 | inline bool IsInlined() const { return tuple_is_inlined; } 103 | 104 | // Get a string representation for debugging 105 | std::string ToString() const; 106 | 107 | private: 108 | // size of fixed length columns 109 | int32_t length; 110 | 111 | // all inlined and uninlined columns in the tuple 112 | std::vector columns; 113 | 114 | // are all columns inlined 115 | bool tuple_is_inlined; 116 | 117 | // keeps track of unlined columns, using logical position(start with 0) 118 | std::vector uninlined_columns; 119 | 120 | // keeps track of indexed columns in original table 121 | // std::vector indexed_columns_; 122 | }; 123 | 124 | } // namespace cmudb 125 | -------------------------------------------------------------------------------- /src/include/common/config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * config.h 3 | * 4 | * Database system configuration 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace cmudb { 14 | 15 | extern std::chrono::duration LOG_TIMEOUT; 16 | 17 | extern std::atomic ENABLE_LOGGING; 18 | 19 | #define INVALID_PAGE_ID -1 // representing an invalid page id 20 | #define INVALID_TXN_ID -1 // representing an invalid txn id 21 | #define INVALID_LSN -1 // representing an invalid lsn 22 | #define HEADER_PAGE_ID 0 // the header page id 23 | #define PAGE_SIZE 512 // size of a data page in byte 24 | #define LOG_BUFFER_SIZE \ 25 | ((BUFFER_POOL_SIZE + 1) * PAGE_SIZE) // size of a log buffer in byte 26 | #define BUCKET_SIZE 50 // size of extendible hash bucket 27 | #define BUFFER_POOL_SIZE 10 // size of buffer pool 28 | 29 | typedef int32_t page_id_t; // page id type 30 | typedef int32_t txn_id_t; // transaction id type 31 | typedef int32_t lsn_t; // log sequence number type 32 | 33 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/common/logger.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2008 by H-Store Project * 3 | * Brown University * 4 | * Massachusetts Institute of Technology * 5 | * Yale University * 6 | * * 7 | * This software may be modified and distributed under the terms * 8 | * of the MIT license. See the LICENSE file for details. * 9 | * * 10 | ***************************************************************************/ 11 | 12 | #ifndef HSTOREDEBUGLOG_H 13 | #define HSTOREDEBUGLOG_H 14 | 15 | /** 16 | * Debug logging functions for EE. Unlike the performance counters, 17 | * these are just fprintf() turned on/off by LOG_LEVEL compile option. 18 | * The main concern here is not to add any overhead on runtime performance 19 | * when the logging is turned off. Use LOG_XXX_ENABLED macros defined here to 20 | * eliminate all instructions in the final binary. 21 | * @author Hideaki 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | namespace cmudb { 28 | 29 | // Note that __VTableFILE__ is a special pre-processor macro that we 30 | // generate for shorter path names using CMake. 31 | 32 | // Log levels. 33 | #define LOG_LEVEL_OFF 1000 34 | #define LOG_LEVEL_ERROR 500 35 | #define LOG_LEVEL_WARN 400 36 | #define LOG_LEVEL_INFO 300 37 | #define LOG_LEVEL_DEBUG 200 38 | #define LOG_LEVEL_TRACE 100 39 | #define LOG_LEVEL_ALL 0 40 | 41 | #define LOG_LOG_TIME_FORMAT "%Y-%m-%d %H:%M:%S" 42 | #define LOG_OUTPUT_STREAM stdout 43 | 44 | // Compile Option 45 | #ifndef LOG_LEVEL 46 | // TODO : any way to use pragma message in GCC? 47 | //#pragma message("Warning: LOG_LEVEL compile option was not explicitly 48 | // given.") 49 | #ifndef NDEBUG 50 | //#pragma message("LOG_LEVEL_DEBUG is used instead as DEBUG option is on.") 51 | #define LOG_LEVEL LOG_LEVEL_DEBUG 52 | #else 53 | //#pragma message("LOG_LEVEL_WARN is used instead as DEBUG option is off.") 54 | #define LOG_LEVEL LOG_LEVEL_INFO 55 | #endif 56 | //#pragma message("Give LOG_LEVEL compile option to overwrite the default 57 | // level.") 58 | #endif 59 | 60 | // For compilers which do not support __FUNCTION__ 61 | #if !defined(__FUNCTION__) && !defined(__GNUC__) 62 | #define __FUNCTION__ "" 63 | #endif 64 | 65 | void outputLogHeader_(const char *file, int line, const char *func, int level); 66 | 67 | // Two convenient macros for debugging 68 | // 1. Logging macros. 69 | // 2. LOG_XXX_ENABLED macros. Use these to "eliminate" all the debug blocks from 70 | // release binary. 71 | #ifdef LOG_ERROR_ENABLED 72 | #undef LOG_ERROR_ENABLED 73 | #endif 74 | #if LOG_LEVEL <= LOG_LEVEL_ERROR 75 | #define LOG_ERROR_ENABLED 76 | //#pragma message("LOG_ERROR was enabled.") 77 | #define LOG_ERROR(...) \ 78 | outputLogHeader_(__VTableFILE__, __LINE__, __FUNCTION__, LOG_LEVEL_ERROR); \ 79 | ::fprintf(LOG_OUTPUT_STREAM, __VA_ARGS__); \ 80 | fprintf(LOG_OUTPUT_STREAM, "\n"); \ 81 | ::fflush(stdout) 82 | #else 83 | #define LOG_ERROR(...) ((void)0) 84 | #endif 85 | 86 | #ifdef LOG_WARN_ENABLED 87 | #undef LOG_WARN_ENABLED 88 | #endif 89 | #if LOG_LEVEL <= LOG_LEVEL_WARN 90 | #define LOG_WARN_ENABLED 91 | //#pragma message("LOG_WARN was enabled.") 92 | #define LOG_WARN(...) \ 93 | outputLogHeader_(__VTableFILE__, __LINE__, __FUNCTION__, LOG_LEVEL_WARN); \ 94 | ::fprintf(LOG_OUTPUT_STREAM, __VA_ARGS__); \ 95 | fprintf(LOG_OUTPUT_STREAM, "\n"); \ 96 | ::fflush(stdout) 97 | #else 98 | #define LOG_WARN(...) ((void)0) 99 | #endif 100 | 101 | #ifdef LOG_INFO_ENABLED 102 | #undef LOG_INFO_ENABLED 103 | #endif 104 | #if LOG_LEVEL <= LOG_LEVEL_INFO 105 | #define LOG_INFO_ENABLED 106 | //#pragma message("LOG_INFO was enabled.") 107 | #define LOG_INFO(...) \ 108 | outputLogHeader_(__VTableFILE__, __LINE__, __FUNCTION__, LOG_LEVEL_INFO); \ 109 | ::fprintf(LOG_OUTPUT_STREAM, __VA_ARGS__); \ 110 | fprintf(LOG_OUTPUT_STREAM, "\n"); \ 111 | ::fflush(stdout) 112 | #else 113 | #define LOG_INFO(...) ((void)0) 114 | #endif 115 | 116 | #ifdef LOG_DEBUG_ENABLED 117 | #undef LOG_DEBUG_ENABLED 118 | #endif 119 | #if LOG_LEVEL <= LOG_LEVEL_DEBUG 120 | #define LOG_DEBUG_ENABLED 121 | //#pragma message("LOG_DEBUG was enabled.") 122 | #define LOG_DEBUG(...) \ 123 | outputLogHeader_(__VTableFILE__, __LINE__, __FUNCTION__, LOG_LEVEL_DEBUG); \ 124 | ::fprintf(LOG_OUTPUT_STREAM, __VA_ARGS__); \ 125 | fprintf(LOG_OUTPUT_STREAM, "\n"); \ 126 | ::fflush(stdout) 127 | #else 128 | #define LOG_DEBUG(...) ((void)0) 129 | #endif 130 | 131 | #ifdef LOG_TRACE_ENABLED 132 | #undef LOG_TRACE_ENABLED 133 | #endif 134 | #if LOG_LEVEL <= LOG_LEVEL_TRACE 135 | #define LOG_TRACE_ENABLED 136 | //#pragma message("LOG_TRACE was enabled.") 137 | #define LOG_TRACE(...) \ 138 | outputLogHeader_(__VTableFILE__, __LINE__, __FUNCTION__, LOG_LEVEL_TRACE); \ 139 | ::fprintf(LOG_OUTPUT_STREAM, __VA_ARGS__); \ 140 | fprintf(LOG_OUTPUT_STREAM, "\n"); \ 141 | ::fflush(stdout) 142 | #else 143 | #define LOG_TRACE(...) ((void)0) 144 | #endif 145 | 146 | // Output log message header in this format: [type] [file:line:function] time - 147 | // ex: [ERROR] [somefile.cpp:123:doSome()] 2008/07/06 10:00:00 - 148 | inline void outputLogHeader_(const char *file, int line, const char *func, 149 | int level) { 150 | time_t t = ::time(NULL); 151 | tm *curTime = localtime(&t); 152 | char time_str[32]; // FIXME 153 | ::strftime(time_str, 32, LOG_LOG_TIME_FORMAT, curTime); 154 | const char *type; 155 | switch (level) { 156 | case LOG_LEVEL_ERROR: 157 | type = "ERROR"; 158 | break; 159 | case LOG_LEVEL_WARN: 160 | type = "WARN "; 161 | break; 162 | case LOG_LEVEL_INFO: 163 | type = "INFO "; 164 | break; 165 | case LOG_LEVEL_DEBUG: 166 | type = "DEBUG"; 167 | break; 168 | case LOG_LEVEL_TRACE: 169 | type = "TRACE"; 170 | break; 171 | default: 172 | type = "UNKWN"; 173 | } 174 | // PAVLO: DO NOT CHANGE THIS 175 | ::fprintf(LOG_OUTPUT_STREAM, "%s [%s:%d:%s] %s - ", time_str, file, line, 176 | func, type); 177 | } 178 | 179 | } // namespace cmudb 180 | 181 | #endif 182 | -------------------------------------------------------------------------------- /src/include/common/rid.h: -------------------------------------------------------------------------------- 1 | /** 2 | * rid.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "common/config.h" 11 | 12 | namespace cmudb { 13 | 14 | class RID { 15 | public: 16 | RID() : page_id_(INVALID_PAGE_ID), slot_num_(-1){}; // invalid rid 17 | RID(page_id_t page_id, int slot_num) 18 | : page_id_(page_id), slot_num_(slot_num){}; 19 | 20 | RID(int64_t rid) : page_id_(rid >> 32), slot_num_(rid){}; 21 | 22 | inline int64_t Get() const { return ((int64_t)page_id_) << 32 | slot_num_; } 23 | 24 | inline page_id_t GetPageId() const { return page_id_; } 25 | 26 | inline int GetSlotNum() const { return slot_num_; } 27 | 28 | inline void Set(page_id_t page_id, int slot_num) { 29 | page_id_ = page_id; 30 | slot_num_ = slot_num; 31 | } 32 | 33 | inline std::string ToString() const { 34 | std::stringstream os; 35 | os << "page_id: " << page_id_; 36 | os << " slot_num: " << slot_num_ << "\n"; 37 | 38 | return os.str(); 39 | } 40 | 41 | friend std::ostream &operator<<(std::ostream &os, const RID &rid) { 42 | os << rid.ToString(); 43 | return os; 44 | } 45 | 46 | bool operator==(const RID &other) const { 47 | if ((page_id_ == other.page_id_) && (slot_num_ == other.slot_num_)) 48 | return true; 49 | else 50 | return false; 51 | } 52 | 53 | private: 54 | page_id_t page_id_; 55 | int slot_num_; // logical offset from 0, 1... 56 | }; 57 | 58 | } // namespace cmudb 59 | 60 | namespace std { 61 | template <> struct hash { 62 | size_t operator()(const cmudb::RID &obj) const { 63 | return hash()(obj.Get()); 64 | } 65 | }; 66 | } // namespace std 67 | -------------------------------------------------------------------------------- /src/include/common/rwmutex.h: -------------------------------------------------------------------------------- 1 | /** 2 | * rwmutex.h 3 | * 4 | * Reader-Writer lock 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace cmudb { 14 | class RWMutex { 15 | 16 | typedef std::mutex mutex_t; 17 | typedef std::condition_variable cond_t; 18 | static const uint32_t max_readers_ = UINT_MAX; 19 | 20 | public: 21 | RWMutex() : reader_count_(0), writer_entered_(false) {} 22 | 23 | ~RWMutex() { std::lock_guard guard(mutex_); } 24 | 25 | RWMutex(const RWMutex &) = delete; 26 | RWMutex &operator=(const RWMutex &) = delete; 27 | 28 | void WLock() { 29 | std::unique_lock lock(mutex_); 30 | while (writer_entered_) 31 | reader_.wait(lock); 32 | writer_entered_ = true; 33 | while (reader_count_ > 0) 34 | writer_.wait(lock); 35 | } 36 | 37 | void WUnlock() { 38 | std::lock_guard guard(mutex_); 39 | writer_entered_ = false; 40 | reader_.notify_all(); 41 | } 42 | 43 | void RLock() { 44 | std::unique_lock lock(mutex_); 45 | while (writer_entered_ || reader_count_ == max_readers_) 46 | reader_.wait(lock); 47 | reader_count_++; 48 | } 49 | 50 | void RUnlock() { 51 | std::lock_guard guard(mutex_); 52 | reader_count_--; 53 | if (writer_entered_) { 54 | if (reader_count_ == 0) 55 | writer_.notify_one(); 56 | } else { 57 | if (reader_count_ == max_readers_ - 1) 58 | reader_.notify_one(); 59 | } 60 | } 61 | 62 | private: 63 | mutex_t mutex_; 64 | cond_t writer_; 65 | cond_t reader_; 66 | uint32_t reader_count_; 67 | bool writer_entered_; 68 | }; 69 | } // namespace cmudb 70 | -------------------------------------------------------------------------------- /src/include/common/string_utility.h: -------------------------------------------------------------------------------- 1 | /** 2 | * string_utility.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cmudb { 13 | class StringUtility { 14 | public: 15 | // trim from start (in place) 16 | static inline void LTrim(std::string &s) { 17 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), 18 | [](int ch) { return !std::isspace(ch); })); 19 | } 20 | 21 | // trim from end (in place) 22 | static inline void RTrim(std::string &s) { 23 | s.erase(std::find_if(s.rbegin(), s.rend(), 24 | [](int ch) { return !std::isspace(ch); }) 25 | .base(), 26 | s.end()); 27 | } 28 | 29 | // trim from both sides (in place) 30 | static inline void Trim(std::string &s) { 31 | LTrim(s); 32 | RTrim(s); 33 | } 34 | 35 | static std::vector Split(const std::string &s, char delim) { 36 | std::stringstream ss; 37 | std::vector elems; 38 | std::string item; 39 | 40 | ss.str(s); 41 | while (std::getline(ss, item, delim)) { 42 | Trim(item); 43 | elems.push_back(item); 44 | } 45 | 46 | return elems; 47 | } 48 | }; 49 | } // namespace cmudb 50 | -------------------------------------------------------------------------------- /src/include/concurrency/lock_manager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * lock_manager.h 3 | * 4 | * Tuple level lock manager, use wait-die to prevent deadlocks 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "common/rid.h" 17 | #include "common/logger.h" 18 | #include "concurrency/transaction.h" 19 | 20 | namespace cmudb { 21 | 22 | enum class LockMode { SHARED = 0, EXCLUSIVE }; // lock mode for list item 23 | 24 | struct LockItem { 25 | 26 | explicit LockItem(txn_id_t id, LockMode mode, bool status): 27 | tid_(id), mode_(mode), held_(status){} 28 | 29 | txn_id_t tid_; 30 | LockMode mode_; 31 | bool held_; // For strict 2PL 32 | }; 33 | 34 | class LockList { 35 | public: 36 | LockList(txn_id_t id, LockMode mode, bool status){ 37 | list_.push_back(LockItem(id, mode, status)); 38 | oldest_ = id; 39 | } 40 | 41 | inline bool IsEmpty(){return list_.empty();} 42 | inline txn_id_t GetOldest(){return oldest_;} 43 | inline std::list::iterator Begin(){return list_.begin();} 44 | 45 | void Add(txn_id_t id, LockMode mode, bool status){ 46 | bool flag = true; 47 | for(auto i = list_.begin(); i != list_.end(); ++i){ 48 | if(i->held_) continue; 49 | if(i->tid_ > id){ 50 | //Find first larger than 51 | list_.insert(i, LockItem(id, mode, status)); 52 | flag = false; 53 | break; 54 | } 55 | } 56 | // Not insert in middle, then push back 57 | if(flag) 58 | list_.push_back(LockItem(id, mode, status)); 59 | } 60 | 61 | void Push_front(txn_id_t id, LockMode mode, bool status){ 62 | list_.push_front(LockItem(id, mode, status)); 63 | if(id < oldest_) oldest_ = id; // Update oldest txn 64 | } 65 | 66 | LockItem Find(txn_id_t id){ 67 | for(auto i = list_.begin(); i != list_.end(); ++i){ 68 | if(i->tid_ == id){ 69 | return *i; 70 | } 71 | } 72 | return LockItem(-1, LockMode::SHARED, false); 73 | } 74 | 75 | bool IsFirst(txn_id_t id){return list_.begin()->tid_ == id;} 76 | 77 | void Remove(txn_id_t id){ 78 | // need to update the oldest id 79 | for(auto i = list_.begin(); i != list_.end(); ++i){ 80 | if(i->tid_ == id){ 81 | list_.erase(i); 82 | break; 83 | } 84 | } 85 | if(id == oldest_){ 86 | // Update oldest_ 87 | oldest_ = 10000000; 88 | if(!list_.empty()){ 89 | for(auto i = list_.begin(); i != list_.end(); ++i){ 90 | if(!i->held_) break; 91 | if(oldest_ > i->tid_) oldest_ = i->tid_; 92 | } 93 | } 94 | } 95 | } 96 | 97 | void Hold(txn_id_t id){ 98 | for(auto i = list_.begin() ; i!=list_.end(); ++i){ 99 | if(i->tid_ == id && !i->held_){ 100 | i->held_ = true; 101 | return; 102 | } 103 | } 104 | } 105 | 106 | // void Upgrade(txn_id_t id){ 107 | // assert(!IsEmpty()); 108 | // for(auto i ) 109 | // } 110 | 111 | // void MoveToFront(txn_id_t id){ 112 | // // move an element to the begin of the list 113 | // assert(!IsEmpty()) 114 | // if(list_[0].tid_ == id) return; 115 | // for(auto i = list_.begin(); i != list.end(); ++i){ 116 | // if(*i.tid_ == id){ 117 | // list_.Push_front(LockItem(id, *i.mode_, true)); 118 | // } 119 | // } 120 | // } 121 | 122 | bool CanAddShardLock(){ 123 | if(list_.empty()) return true; 124 | return list_.begin()->mode_ == LockMode::SHARED; 125 | } 126 | 127 | private: 128 | std::list list_; 129 | txn_id_t oldest_; // Wait-die, keep the oldest tid of multiple shared locks 130 | }; 131 | 132 | class LockManager { 133 | 134 | public: 135 | LockManager(bool strict_2PL) : strict_2PL_(strict_2PL){ 136 | //lock_table_ = new std::unordered_map>; 137 | // This is not efficient, better to intialize when first lock 138 | }; 139 | 140 | /*** below are APIs need to implement ***/ 141 | // lock: 142 | // return false if transaction is aborted 143 | // it should be blocked on waiting and should return true when granted 144 | // note the behavior of trying to lock locked rids by same txn is undefined 145 | // it is transaction's job to keep track of its current locks 146 | bool LockShared(Transaction *txn, const RID &rid); 147 | bool LockExclusive(Transaction *txn, const RID &rid); 148 | bool LockUpgrade(Transaction *txn, const RID &rid); 149 | 150 | // unlock: 151 | // release the lock hold by the txn 152 | bool Unlock(Transaction *txn, const RID &rid); 153 | /*** END OF APIs ***/ 154 | bool IsValidToLock(Transaction *txn); 155 | 156 | private: 157 | bool strict_2PL_; 158 | std::unordered_map> lock_table_; 159 | std::mutex mutex_; 160 | std::condition_variable cond; 161 | }; 162 | 163 | } // namespace cmudb 164 | -------------------------------------------------------------------------------- /src/include/concurrency/transaction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * transaction.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "common/config.h" 14 | #include "common/logger.h" 15 | #include "page/page.h" 16 | #include "table/tuple.h" 17 | 18 | namespace cmudb { 19 | 20 | /** 21 | * Transaction states: 22 | * 23 | * _________________________ 24 | * | v 25 | * GROWING -> SHRINKING -> COMMITTED ABORTED 26 | * |__________|________________________^ 27 | * 28 | **/ 29 | enum class TransactionState { GROWING, SHRINKING, COMMITTED, ABORTED }; 30 | 31 | enum class WType { INSERT = 0, DELETE, UPDATE }; 32 | 33 | class TableHeap; 34 | 35 | // write set record 36 | class WriteRecord { 37 | public: 38 | WriteRecord(RID rid, WType wtype, const Tuple &tuple, TableHeap *table) 39 | : rid_(rid), wtype_(wtype), tuple_(tuple), table_(table) {} 40 | 41 | RID rid_; 42 | WType wtype_; 43 | // tuple is only for update operation 44 | Tuple tuple_; 45 | // which table 46 | TableHeap *table_; 47 | }; 48 | 49 | class Transaction { 50 | public: 51 | Transaction(Transaction const &) = delete; 52 | Transaction(txn_id_t txn_id) 53 | : state_(TransactionState::GROWING), 54 | thread_id_(std::this_thread::get_id()), 55 | txn_id_(txn_id), prev_lsn_(INVALID_LSN), shared_lock_set_{new std::unordered_set}, 56 | exclusive_lock_set_{new std::unordered_set} { 57 | // initialize sets 58 | write_set_.reset(new std::deque); 59 | page_set_.reset(new std::deque); 60 | deleted_page_set_.reset(new std::unordered_set); 61 | } 62 | 63 | ~Transaction() {} 64 | 65 | //===--------------------------------------------------------------------===// 66 | // Mutators and Accessors 67 | //===--------------------------------------------------------------------===// 68 | inline std::thread::id GetThreadId() const { return thread_id_; } 69 | 70 | inline txn_id_t GetTransactionId() const { return txn_id_; } 71 | 72 | inline std::shared_ptr> GetWriteSet() { 73 | return write_set_; 74 | } 75 | 76 | inline std::shared_ptr> GetPageSet() { return page_set_; } 77 | 78 | inline void AddIntoPageSet(Page *page) { page_set_->push_back(page); } 79 | //inline void PushFrontPageSet(Page *page){page_set_->push_front(page); } 80 | 81 | inline std::shared_ptr> GetDeletedPageSet() { 82 | return deleted_page_set_; 83 | } 84 | 85 | inline void AddIntoDeletedPageSet(page_id_t page_id) { 86 | deleted_page_set_->insert(page_id); 87 | } 88 | 89 | inline std::shared_ptr> GetSharedLockSet() { 90 | return shared_lock_set_; 91 | } 92 | 93 | inline std::shared_ptr> GetExclusiveLockSet() { 94 | return exclusive_lock_set_; 95 | } 96 | 97 | inline TransactionState GetState() { return state_; } 98 | 99 | inline void SetState(TransactionState state) { state_ = state; } 100 | 101 | inline lsn_t GetPrevLSN() { return prev_lsn_; } 102 | 103 | inline void SetPrevLSN(lsn_t prev_lsn) { prev_lsn_ = prev_lsn; } 104 | 105 | 106 | private: 107 | TransactionState state_; 108 | // thread id, single-threaded transactions 109 | std::thread::id thread_id_; 110 | // transaction id 111 | txn_id_t txn_id_; 112 | // Below are used by transaction, undo set 113 | std::shared_ptr> write_set_; 114 | // prev lsn 115 | lsn_t prev_lsn_; 116 | 117 | // Below are used by concurrent index 118 | // this deque contains page pointer that was latche during index operation 119 | std::shared_ptr> page_set_; 120 | // this set contains page_id that was deleted during index operation 121 | std::shared_ptr> deleted_page_set_; 122 | 123 | // Below are used by lock manager 124 | // this set contains rid of shared-locked tuples by this transaction 125 | std::shared_ptr> shared_lock_set_; 126 | // this set contains rid of exclusive-locked tuples by this transaction 127 | std::shared_ptr> exclusive_lock_set_; 128 | }; 129 | } // namespace cmudb 130 | -------------------------------------------------------------------------------- /src/include/concurrency/transaction_manager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * transaction_manager.h 3 | * 4 | */ 5 | 6 | #pragma once 7 | #include 8 | #include 9 | 10 | #include "common/config.h" 11 | #include "concurrency/lock_manager.h" 12 | #include "logging/log_manager.h" 13 | 14 | namespace cmudb { 15 | class TransactionManager { 16 | public: 17 | TransactionManager(LockManager *lock_manager, 18 | LogManager *log_manager = nullptr) 19 | : next_txn_id_(0), lock_manager_(lock_manager), 20 | log_manager_(log_manager) {} 21 | Transaction *Begin(); 22 | void Commit(Transaction *txn); 23 | void Abort(Transaction *txn); 24 | 25 | private: 26 | std::atomic next_txn_id_; 27 | LockManager *lock_manager_; 28 | LogManager *log_manager_; 29 | }; 30 | 31 | } // namespace cmudb 32 | -------------------------------------------------------------------------------- /src/include/disk/disk_manager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * disk_manager.h 3 | * 4 | * Disk manager takes care of the allocation and deallocation of pages within a 5 | * database. It also performs read and write of pages to and from disk, and 6 | * provides a logical file layer within the context of a database management 7 | * system. 8 | */ 9 | 10 | #pragma once 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "common/config.h" 17 | 18 | namespace cmudb { 19 | 20 | class DiskManager { 21 | public: 22 | DiskManager(const std::string &db_file); 23 | ~DiskManager(); 24 | 25 | void WritePage(page_id_t page_id, const char *page_data); 26 | void ReadPage(page_id_t page_id, char *page_data); 27 | 28 | void WriteLog(char *log_data, int size); 29 | bool ReadLog(char *log_data, int size, int offset); 30 | 31 | page_id_t AllocatePage(); 32 | void DeallocatePage(page_id_t page_id); 33 | 34 | int GetNumFlushes() const; 35 | bool GetFlushState() const; 36 | inline void SetFlushLogFuture(std::future *f) { flush_log_f_ = f; } 37 | inline bool HasFlushLogFuture() { return flush_log_f_ != nullptr; } 38 | 39 | private: 40 | int GetFileSize(const std::string &name); 41 | // stream to write log file 42 | std::fstream log_io_; 43 | std::string log_name_; 44 | // stream to write db file 45 | std::fstream db_io_; 46 | std::string file_name_; 47 | std::atomic next_page_id_; 48 | int num_flushes_; 49 | bool flush_log_; 50 | std::future *flush_log_f_; 51 | }; 52 | 53 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/hash/extendible_hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * extendible_hash.h : implementation of in-memory hash table using extendible 3 | * hashing 4 | * 5 | * Functionality: The buffer pool manager must maintain a page table to be able 6 | * to quickly map a PageId to its corresponding memory location; or alternately 7 | * report that the PageId does not match any currently-buffered page. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "hash/hash_table.h" 20 | 21 | namespace cmudb { 22 | 23 | template 24 | class ExtendibleHash : public HashTable { 25 | public: 26 | // constructor 27 | ExtendibleHash(size_t size); 28 | 29 | // helper function to generate hash addressing 30 | size_t HashKey(const K &key); 31 | 32 | // helper function to get global & local depth 33 | int GetGlobalDepth() const; 34 | 35 | int GetLocalDepth(int bucket_id) const; 36 | 37 | int GetNumBuckets() const; 38 | 39 | // lookup and modifier 40 | bool Find(const K &key, V &value) override; 41 | 42 | bool Remove(const K &key) override; 43 | 44 | void Insert(const K &key, const V &value) override; 45 | 46 | private: 47 | // add your own member variables here 48 | class Bucket { 49 | public: 50 | int localDepth; 51 | std::map contents; 52 | 53 | Bucket(int depth) : localDepth(depth) {} 54 | }; 55 | 56 | std::vector> bucketDirectory; 57 | int globalDepth; 58 | const size_t bucketSizeLimit; 59 | 60 | int getBucketIndex(size_t hashKey) const; 61 | 62 | std::shared_ptr getBucket(const K &key); 63 | 64 | std::mutex mtx; 65 | }; 66 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/hash/hash_table.h: -------------------------------------------------------------------------------- 1 | /** 2 | * hash_table.h 3 | * 4 | * Abstract class for hash table implementation 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace cmudb { 10 | 11 | template class HashTable { 12 | public: 13 | HashTable() {} 14 | virtual ~HashTable() {} 15 | // lookup and modifier 16 | virtual bool Find(const K &key, V &value) = 0; 17 | virtual bool Remove(const K &key) = 0; 18 | virtual void Insert(const K &key, const V &value) = 0; 19 | }; 20 | 21 | } // namespace cmudb 22 | -------------------------------------------------------------------------------- /src/include/index/b_plus_tree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree.h 3 | * 4 | * Implementation of simple b+ tree data structure where internal pages direct 5 | * the search and leaf pages contain actual data. 6 | * (1) We only support unique key 7 | * (2) support insert & remove 8 | * (3) The structure should shrink and grow dynamically 9 | * (4) Implement index iterator for range scan 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | #include "concurrency/transaction.h" 17 | #include "index/index_iterator.h" 18 | #include "page/b_plus_tree_internal_page.h" 19 | #include "page/b_plus_tree_leaf_page.h" 20 | 21 | 22 | namespace cmudb { 23 | enum class Operation {SEARCH = 0, INSERT, DELETE}; 24 | 25 | #define BPLUSTREE_TYPE BPlusTree 26 | // Main class providing the API for the Interactive B+ Tree. 27 | INDEX_TEMPLATE_ARGUMENTS 28 | class BPlusTree { 29 | public: 30 | explicit BPlusTree(const std::string &name, 31 | BufferPoolManager *buffer_pool_manager, 32 | const KeyComparator &comparator, 33 | page_id_t root_page_id = INVALID_PAGE_ID); 34 | 35 | // Returns true if this B+ tree has no keys and values. 36 | bool IsEmpty() const; 37 | 38 | // Insert a key-value pair into this B+ tree. 39 | bool Insert(const KeyType &key, const ValueType &value, 40 | Transaction *transaction = nullptr); 41 | 42 | // Remove a key and its value from this B+ tree. 43 | void Remove(const KeyType &key, Transaction *transaction = nullptr); 44 | 45 | // return the value associated with a given key 46 | bool GetValue(const KeyType &key, std::vector &result, 47 | Transaction *transaction = nullptr); 48 | 49 | // index iterator 50 | INDEXITERATOR_TYPE Begin(); 51 | INDEXITERATOR_TYPE Begin(const KeyType &key); 52 | 53 | // Print this B+ tree to stdout using a simple command-line 54 | std::string ToString(bool verbose = false); 55 | 56 | // read data from file and insert one by one 57 | void InsertFromFile(const std::string &file_name, 58 | Transaction *transaction = nullptr); 59 | 60 | // read data from file and remove one by one 61 | void RemoveFromFile(const std::string &file_name, 62 | Transaction *transaction = nullptr); 63 | 64 | 65 | private: 66 | void LockPage(Page* page, Transaction* txn, Operation op); 67 | void UnlockPage(Page* page, Transaction* txn, Operation op); 68 | void UnlockParentPage(Page* page, Transaction* txn, Operation op); 69 | void UnlockAllPage(Transaction* txn, Operation op); 70 | 71 | 72 | Page *FindLeafPage(const KeyType &key, 73 | bool leftMost = false, 74 | Transaction *txn = nullptr, 75 | Operation op = Operation::SEARCH); 76 | 77 | void StartNewTree(const KeyType &key, const ValueType &value, Transaction* txn); 78 | 79 | bool InsertIntoLeaf(const KeyType &key, const ValueType &value, 80 | Transaction *transaction = nullptr); 81 | 82 | void InsertIntoParent(BPlusTreePage *old_node, const KeyType &key, 83 | BPlusTreePage *new_node, 84 | Transaction *transaction = nullptr); 85 | 86 | template N *Split(N *node); 87 | 88 | template 89 | bool CoalesceOrRedistribute(N *node, Transaction *transaction = nullptr); 90 | 91 | template 92 | bool Coalesce( 93 | N *&neighbor_node, N *&node, 94 | BPlusTreeInternalPage *&parent, 95 | int index, Transaction *transaction = nullptr); 96 | 97 | template void Redistribute(N *neighbor_node, N *node, int index); 98 | 99 | bool AdjustRoot(BPlusTreePage *node); 100 | 101 | void UpdateRootPageId(int insert_record = false); 102 | 103 | inline void LockRoot(){root_mutex_.lock();} 104 | inline void UnlockRoot(){root_mutex_.unlock();} 105 | 106 | // member variable 107 | std::string index_name_; 108 | page_id_t root_page_id_; 109 | BufferPoolManager *buffer_pool_manager_; 110 | KeyComparator comparator_; 111 | int split_count_; // Count split 112 | 113 | std::mutex root_mutex_; //mutex for root page id 114 | 115 | std::once_flag flag_; //Start new tree 116 | }; 117 | 118 | } // namespace cmudb 119 | -------------------------------------------------------------------------------- /src/include/index/b_plus_tree_index.h: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_index.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "index/b_plus_tree.h" 12 | #include "index/index.h" 13 | 14 | namespace cmudb { 15 | 16 | #define BPLUSTREE_INDEX_TYPE BPlusTreeIndex 17 | 18 | INDEX_TEMPLATE_ARGUMENTS 19 | class BPlusTreeIndex : public Index { 20 | 21 | public: 22 | BPlusTreeIndex(IndexMetadata *metadata, 23 | BufferPoolManager *buffer_pool_manager, 24 | page_id_t root_page_id = INVALID_PAGE_ID); 25 | 26 | ~BPlusTreeIndex() {} 27 | 28 | void InsertEntry(const Tuple &key, RID rid, 29 | Transaction *transaction = nullptr) override; 30 | 31 | void DeleteEntry(const Tuple &key, 32 | Transaction *transaction = nullptr) override; 33 | 34 | void ScanKey(const Tuple &key, std::vector &result, 35 | Transaction *transaction = nullptr) override; 36 | 37 | protected: 38 | // comparator for key 39 | KeyComparator comparator_; 40 | // container 41 | BPlusTree container_; 42 | }; 43 | 44 | } // namespace cmudb 45 | -------------------------------------------------------------------------------- /src/include/index/generic_key.h: -------------------------------------------------------------------------------- 1 | /** 2 | * generic_key.h 3 | * 4 | * Key used for indexing with opaque data 5 | * 6 | * This key type uses an fixed length array to hold data for indexing 7 | * purposes, the actual size of which is specified and instantiated 8 | * with a template argument. 9 | */ 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "table/tuple.h" 15 | #include "type/value.h" 16 | 17 | namespace cmudb { 18 | template class GenericKey { 19 | public: 20 | inline void SetFromKey(const Tuple &tuple) { 21 | // intialize to 0 22 | memset(data, 0, KeySize); 23 | memcpy(data, tuple.GetData(), tuple.GetLength()); 24 | } 25 | 26 | // NOTE: for test purpose only 27 | inline void SetFromInteger(int64_t key) { 28 | memset(data, 0, KeySize); 29 | memcpy(data, &key, sizeof(int64_t)); 30 | } 31 | 32 | inline Value ToValue(Schema *schema, int column_id) const { 33 | const char *data_ptr; 34 | const TypeId column_type = schema->GetType(column_id); 35 | const bool is_inlined = schema->IsInlined(column_id); 36 | if (is_inlined) { 37 | data_ptr = (data + schema->GetOffset(column_id)); 38 | } else { 39 | int32_t offset = *reinterpret_cast( 40 | const_cast(data + schema->GetOffset(column_id))); 41 | data_ptr = (data + offset); 42 | } 43 | return Value::DeserializeFrom(data_ptr, column_type); 44 | } 45 | 46 | // NOTE: for test purpose only 47 | // interpret the first 8 bytes as int64_t from data vector 48 | inline int64_t ToString() const { 49 | return *reinterpret_cast(const_cast(data)); 50 | } 51 | 52 | // NOTE: for test purpose only 53 | // interpret the first 8 bytes as int64_t from data vector 54 | friend std::ostream &operator<<(std::ostream &os, const GenericKey &key) { 55 | os << key.ToString(); 56 | return os; 57 | } 58 | 59 | // actual location of data, extends past the end. 60 | char data[KeySize]; 61 | }; 62 | 63 | /** 64 | * Function object returns true if lhs < rhs, used for trees 65 | */ 66 | template class GenericComparator { 67 | public: 68 | inline int operator()(const GenericKey &lhs, 69 | const GenericKey &rhs) const { 70 | int column_count = key_schema_->GetColumnCount(); 71 | 72 | for (int i = 0; i < column_count; i++) { 73 | Value lhs_value = (lhs.ToValue(key_schema_, i)); 74 | Value rhs_value = (rhs.ToValue(key_schema_, i)); 75 | 76 | if (lhs_value.CompareLessThan(rhs_value) == CMP_TRUE) 77 | return -1; 78 | 79 | if (lhs_value.CompareGreaterThan(rhs_value) == CMP_TRUE) 80 | return 1; 81 | } 82 | // equals 83 | return 0; 84 | } 85 | 86 | GenericComparator(const GenericComparator &other) { 87 | this->key_schema_ = other.key_schema_; 88 | } 89 | 90 | // constructor 91 | GenericComparator(Schema *key_schema) : key_schema_(key_schema) {} 92 | 93 | private: 94 | Schema *key_schema_; 95 | }; 96 | 97 | } // namespace cmudb 98 | -------------------------------------------------------------------------------- /src/include/index/index.h: -------------------------------------------------------------------------------- 1 | /** 2 | * index.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "catalog/schema.h" 12 | #include "table/tuple.h" 13 | #include "type/value.h" 14 | 15 | namespace cmudb { 16 | 17 | /** 18 | * class IndexMetadata - Holds metadata of an index object 19 | * 20 | * The metadata object maintains the tuple schema and key attribute of an 21 | * index, since the external callers does not know the actual structure of 22 | * the index key, so it is the index's responsibility to maintain such a 23 | * mapping relation and does the conversion between tuple key and index key 24 | */ 25 | class Transaction; 26 | class IndexMetadata { 27 | IndexMetadata() = delete; 28 | 29 | public: 30 | IndexMetadata(std::string index_name, std::string table_name, 31 | const Schema *tuple_schema, const std::vector &key_attrs) 32 | : name_(index_name), table_name_(table_name), key_attrs_(key_attrs) { 33 | key_schema_ = Schema::CopySchema(tuple_schema, key_attrs_); 34 | } 35 | 36 | ~IndexMetadata() { delete key_schema_; }; 37 | 38 | inline const std::string &GetName() const { return name_; } 39 | 40 | inline const std::string &GetTableName() { return table_name_; } 41 | 42 | // Returns a schema object pointer that represents the indexed key 43 | inline Schema *GetKeySchema() const { return key_schema_; } 44 | 45 | // Return the number of columns inside index key (not in tuple key) 46 | // Note that this must be defined inside the cpp source file 47 | // because it uses the member of catalog::Schema which is not known here 48 | int GetIndexColumnCount() const { return (int)key_attrs_.size(); } 49 | 50 | // Returns the mapping relation between indexed columns and base table 51 | // columns 52 | inline const std::vector &GetKeyAttrs() const { return key_attrs_; } 53 | 54 | // Get a string representation for debugging 55 | const std::string ToString() const { 56 | std::stringstream os; 57 | 58 | os << "IndexMetadata[" 59 | << "Name = " << name_ << ", " 60 | << "Type = B+Tree, " 61 | << "Table name = " << table_name_ << "] :: "; 62 | os << key_schema_->ToString(); 63 | 64 | return os.str(); 65 | } 66 | 67 | private: 68 | std::string name_; 69 | std::string table_name_; 70 | // The mapping relation between key schema and tuple schema 71 | const std::vector key_attrs_; 72 | // schema of the indexed key 73 | Schema *key_schema_; 74 | }; 75 | 76 | ///////////////////////////////////////////////////////////////////// 77 | // Index class definition 78 | ///////////////////////////////////////////////////////////////////// 79 | 80 | /** 81 | * class Index - Base class for derived indices of different types 82 | * 83 | * The index structure majorly maintains information on the schema of the 84 | * schema of the underlying table and the mapping relation between index key 85 | * and tuple key, and provides an abstracted way for the external world to 86 | * interact with the underlying index implementation without exposing 87 | * the actual implementation's interface. 88 | * 89 | * Index object also handles predicate scan, in addition to simple insert, 90 | * delete, predicate insert, point query, and full index scan. Predicate scan 91 | * only supports conjunction, and may or may not be optimized depending on 92 | * the type of expressions inside the predicate. 93 | */ 94 | class Index { 95 | public: 96 | Index(IndexMetadata *metadata) : metadata_(metadata) {} 97 | 98 | virtual ~Index() { delete metadata_; } 99 | 100 | // Return the metadata object associated with the index 101 | IndexMetadata *GetMetadata() const { return metadata_; } 102 | 103 | int GetIndexColumnCount() const { return metadata_->GetIndexColumnCount(); } 104 | 105 | const std::string &GetName() const { return metadata_->GetName(); } 106 | 107 | Schema *GetKeySchema() const { return metadata_->GetKeySchema(); } 108 | 109 | const std::vector &GetKeyAttrs() const { 110 | return metadata_->GetKeyAttrs(); 111 | } 112 | 113 | // Get a string representation for debugging 114 | const std::string ToString() const { 115 | std::stringstream os; 116 | 117 | os << "INDEX: (" << GetName() << ")"; 118 | os << metadata_->ToString(); 119 | return os.str(); 120 | } 121 | 122 | /////////////////////////////////////////////////////////////////// 123 | // Point Modification 124 | /////////////////////////////////////////////////////////////////// 125 | // designed for secondary indexes. 126 | virtual void InsertEntry(const Tuple &key, RID rid, 127 | Transaction *transaction = nullptr) = 0; 128 | 129 | // delete the index entry linked to given tuple 130 | virtual void DeleteEntry(const Tuple &key, 131 | Transaction *transaction = nullptr) = 0; 132 | 133 | virtual void ScanKey(const Tuple &key, std::vector &result, 134 | Transaction *transaction = nullptr) = 0; 135 | 136 | private: 137 | //===--------------------------------------------------------------------===// 138 | // Data members 139 | //===--------------------------------------------------------------------===// 140 | IndexMetadata *metadata_; 141 | }; 142 | 143 | } // namespace cmudb 144 | -------------------------------------------------------------------------------- /src/include/index/index_iterator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * index_iterator.h 3 | * For range scan of b+ tree 4 | */ 5 | #pragma once 6 | #include "page/b_plus_tree_leaf_page.h" 7 | #include "buffer/buffer_pool_manager.h" 8 | 9 | namespace cmudb { 10 | 11 | #define INDEXITERATOR_TYPE \ 12 | IndexIterator 13 | 14 | INDEX_TEMPLATE_ARGUMENTS 15 | class IndexIterator { 16 | public: 17 | // you may define your own constructor based on your member variables 18 | IndexIterator(page_id_t page_id, int index, BufferPoolManager* manager); 19 | ~IndexIterator(); 20 | 21 | bool isEnd(); 22 | 23 | const MappingType &operator*(); 24 | 25 | IndexIterator &operator++(); 26 | 27 | private: 28 | // add your own private member variables here 29 | page_id_t page_id_; 30 | int index_; 31 | BufferPoolManager *buffer_pool_manager_; 32 | B_PLUS_TREE_LEAF_PAGE_TYPE * leaf_; 33 | }; 34 | 35 | } // namespace cmudb 36 | -------------------------------------------------------------------------------- /src/include/logging/log_manager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * log_manager.h 3 | * log manager maintain a separate thread that is awaken when the log buffer is 4 | * full or time out(every X second) to write log buffer's content into disk log 5 | * file. 6 | */ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "disk/disk_manager.h" 15 | #include "logging/log_record.h" 16 | #include "common/logger.h" 17 | 18 | namespace cmudb { 19 | 20 | class LogManager { 21 | public: 22 | LogManager(DiskManager *disk_manager) 23 | : allow_to_flush_(false), offset_(0), 24 | persistent_lsn_changed_(false), 25 | next_lsn_(0), persistent_lsn_(INVALID_LSN), 26 | disk_manager_(disk_manager){ 27 | // TODO: you may intialize your own defined memeber variables here 28 | log_buffer_ = new char[LOG_BUFFER_SIZE]; 29 | flush_buffer_ = new char[LOG_BUFFER_SIZE]; 30 | } 31 | 32 | ~LogManager() { 33 | delete[] log_buffer_; 34 | delete[] flush_buffer_; 35 | log_buffer_ = nullptr; 36 | flush_buffer_ = nullptr; 37 | } 38 | // spawn a separate thread to wake up periodically to flush 39 | void RunFlushThread(); 40 | void StopFlushThread(); 41 | 42 | // append a log record into log buffer 43 | lsn_t AppendLogRecord(LogRecord &log_record); 44 | 45 | // get/set helper functions 46 | inline lsn_t GetPersistentLSN() { return persistent_lsn_; } 47 | inline void SetPersistentLSN(lsn_t lsn) { 48 | persistent_lsn_ = lsn; 49 | persistent_lsn_changed_ = true; 50 | } 51 | inline char *GetLogBuffer() { return log_buffer_; } 52 | 53 | inline void SwapBuffer(){ 54 | char* tmp = flush_buffer_; 55 | flush_buffer_ = log_buffer_; 56 | log_buffer_ = tmp; 57 | offset_ = 0; 58 | } 59 | 60 | void ForceFlush(); 61 | 62 | 63 | private: 64 | // TODO: you may add your own member variables 65 | // also remember to change constructor accordingly 66 | bool allow_to_flush_; 67 | int offset_; 68 | std::atomic persistent_lsn_changed_; 69 | 70 | // atomic counter, record the next log sequence number 71 | std::atomic next_lsn_; 72 | // log records before & include persistent_lsn_ have been written to disk 73 | std::atomic persistent_lsn_; 74 | // log buffer related 75 | char *log_buffer_; 76 | char *flush_buffer_; 77 | // latch to protect shared member variables 78 | std::mutex latch_; 79 | // flush thread 80 | std::thread *flush_thread_; 81 | // for notifying flush thread 82 | std::condition_variable cv_; 83 | // disk manager 84 | DiskManager *disk_manager_; 85 | }; 86 | 87 | } // namespace cmudb 88 | -------------------------------------------------------------------------------- /src/include/logging/log_record.h: -------------------------------------------------------------------------------- 1 | /** 2 | * log_record.h 3 | * For every write opeartion on table page, you should write ahead a 4 | * corresponding log record. 5 | * For EACH log record, HEADER is like (5 fields in common, 20 bytes in totoal) 6 | *------------------------------------------------------------- 7 | * | size | LSN | transID | prevLSN | LogType | 8 | *------------------------------------------------------------- 9 | * For insert type log record 10 | *------------------------------------------------------------- 11 | * | HEADER | tuple_rid | tuple_size | tuple_data(char[] array) | 12 | *------------------------------------------------------------- 13 | * For delete type(including markdelete, rollbackdelete, applydelete) 14 | *------------------------------------------------------------- 15 | * | HEADER | tuple_rid | tuple_size | tuple_data(char[] array) | 16 | *------------------------------------------------------------- 17 | * For update type log record 18 | *------------------------------------------------------------------------------ 19 | * | HEADER | tuple_rid | tuple_size | old_tuple_data | tuple_size | 20 | * | new_tuple_data | 21 | *------------------------------------------------------------------------------ 22 | * For new page type log record 23 | *------------------------------------------------------------- 24 | * | HEADER | prev_page_id | 25 | *------------------------------------------------------------- 26 | */ 27 | #pragma once 28 | #include 29 | 30 | #include "common/config.h" 31 | #include "table/tuple.h" 32 | 33 | namespace cmudb { 34 | // log record type 35 | enum class LogRecordType { 36 | INVALID = 0, 37 | INSERT, 38 | MARKDELETE, 39 | APPLYDELETE, 40 | ROLLBACKDELETE, 41 | UPDATE, 42 | BEGIN, 43 | COMMIT, 44 | ABORT, 45 | // when create a new page in heap table 46 | NEWPAGE, 47 | }; 48 | 49 | class LogRecord { 50 | friend class LogManager; 51 | friend class LogRecovery; 52 | 53 | public: 54 | LogRecord() 55 | : size_(0), lsn_(INVALID_LSN), txn_id_(INVALID_TXN_ID), 56 | prev_lsn_(INVALID_LSN), log_record_type_(LogRecordType::INVALID) {} 57 | 58 | // constructor for Transaction type(BEGIN/COMMIT/ABORT) 59 | LogRecord(txn_id_t txn_id, lsn_t prev_lsn, LogRecordType log_record_type) 60 | : size_(HEADER_SIZE), lsn_(INVALID_LSN), txn_id_(txn_id), 61 | prev_lsn_(prev_lsn), log_record_type_(log_record_type) {} 62 | 63 | // constructor for INSERT/DELETE type 64 | LogRecord(txn_id_t txn_id, lsn_t prev_lsn, LogRecordType log_record_type, 65 | const RID &rid, const Tuple &tuple) 66 | : lsn_(INVALID_LSN), txn_id_(txn_id), prev_lsn_(prev_lsn), 67 | log_record_type_(log_record_type) { 68 | if (log_record_type == LogRecordType::INSERT) { 69 | insert_rid_ = rid; 70 | insert_tuple_ = tuple; 71 | } else { 72 | assert(log_record_type == LogRecordType::APPLYDELETE || 73 | log_record_type == LogRecordType::MARKDELETE || 74 | log_record_type == LogRecordType::ROLLBACKDELETE); 75 | delete_rid_ = rid; 76 | delete_tuple_ = tuple; 77 | } 78 | // calculate log record size 79 | size_ = HEADER_SIZE + sizeof(RID) + sizeof(int32_t) + tuple.GetLength(); 80 | } 81 | 82 | // constructor for UPDATE type 83 | LogRecord(txn_id_t txn_id, lsn_t prev_lsn, LogRecordType log_record_type, 84 | const RID &update_rid, const Tuple &old_tuple, 85 | const Tuple &new_tuple) 86 | : lsn_(INVALID_LSN), txn_id_(txn_id), prev_lsn_(prev_lsn), 87 | log_record_type_(log_record_type), update_rid_(update_rid), 88 | old_tuple_(old_tuple), new_tuple_(new_tuple) { 89 | // calculate log record size 90 | size_ = HEADER_SIZE + sizeof(RID) + old_tuple.GetLength() + 91 | new_tuple.GetLength() + 2 * sizeof(int32_t); 92 | } 93 | 94 | // constructor for NEWPAGE type 95 | LogRecord(txn_id_t txn_id, lsn_t prev_lsn, LogRecordType log_record_type, 96 | page_id_t page_id) 97 | : size_(HEADER_SIZE), lsn_(INVALID_LSN), txn_id_(txn_id), 98 | prev_lsn_(prev_lsn), log_record_type_(log_record_type), 99 | prev_page_id_(page_id) { 100 | // calculate log record size 101 | size_ = HEADER_SIZE + sizeof(page_id_t); 102 | } 103 | 104 | ~LogRecord() {} 105 | 106 | inline RID &GetDeleteRID() { return delete_rid_; } 107 | 108 | inline Tuple &GetInserteTuple() { return insert_tuple_; } 109 | 110 | inline RID &GetInsertRID() { return insert_rid_; } 111 | 112 | 113 | inline page_id_t GetNewPageRecord() { return prev_page_id_; } 114 | 115 | inline int32_t GetSize() { return size_; } 116 | 117 | inline lsn_t GetLSN() { return lsn_; } 118 | 119 | inline txn_id_t GetTxnId() { return txn_id_; } 120 | 121 | inline lsn_t GetPrevLSN() { return prev_lsn_; } 122 | 123 | inline LogRecordType &GetLogRecordType() { return log_record_type_; } 124 | 125 | // For debug purpose 126 | inline std::string ToString() const { 127 | std::ostringstream os; 128 | os << "Log[" 129 | << "size:" << size_ << ", " 130 | << "LSN:" << lsn_ << ", " 131 | << "transID:" << txn_id_ << ", " 132 | << "prevLSN:" << prev_lsn_ << ", " 133 | << "LogType:" << (int)log_record_type_ << "]"; 134 | 135 | return os.str(); 136 | } 137 | 138 | private: 139 | // the length of log record(for serialization, in bytes) 140 | int32_t size_ = 0; 141 | // must have fields 142 | lsn_t lsn_ = INVALID_LSN; 143 | txn_id_t txn_id_ = INVALID_TXN_ID; 144 | lsn_t prev_lsn_ = INVALID_LSN; 145 | LogRecordType log_record_type_ = LogRecordType::INVALID; 146 | 147 | // case1: for delete opeartion, delete_tuple_ for UNDO opeartion 148 | RID delete_rid_; 149 | Tuple delete_tuple_; 150 | 151 | // case2: for insert opeartion 152 | RID insert_rid_; 153 | Tuple insert_tuple_; 154 | 155 | // case3: for update opeartion 156 | RID update_rid_; 157 | Tuple old_tuple_; 158 | Tuple new_tuple_; 159 | 160 | // case4: for new page opeartion 161 | page_id_t prev_page_id_ = INVALID_PAGE_ID; 162 | const static int HEADER_SIZE = 20; 163 | }; // namespace cmudb 164 | 165 | } // namespace cmudb 166 | -------------------------------------------------------------------------------- /src/include/logging/log_recovery.h: -------------------------------------------------------------------------------- 1 | /** 2 | * recovery_manager.h 3 | * Read log file from disk, redo and undo 4 | */ 5 | 6 | #pragma once 7 | #include 8 | #include 9 | #include 10 | 11 | #include "buffer/buffer_pool_manager.h" 12 | #include "concurrency/lock_manager.h" 13 | #include "logging/log_record.h" 14 | 15 | namespace cmudb { 16 | 17 | class LogRecovery { 18 | public: 19 | LogRecovery(DiskManager *disk_manager, 20 | BufferPoolManager *buffer_pool_manager) 21 | : disk_manager_(disk_manager), buffer_pool_manager_(buffer_pool_manager), 22 | offset_(0) { 23 | // global transaction through recovery phase 24 | log_buffer_ = new char[LOG_BUFFER_SIZE]; 25 | } 26 | 27 | ~LogRecovery() { 28 | delete[] log_buffer_; 29 | log_buffer_ = nullptr; 30 | } 31 | 32 | void Redo(); 33 | void Undo(); 34 | bool DeserializeLogRecord(const char *data, LogRecord &log_record); 35 | 36 | private: 37 | // TODO: you can add whatever member variable here 38 | 39 | 40 | // Don't forget to initialize newly added variable in constructor 41 | DiskManager *disk_manager_; 42 | BufferPoolManager *buffer_pool_manager_; 43 | // maintain active transactions and its corresponds latest lsn 44 | std::unordered_map active_txn_; 45 | // mapping log sequence number to log file offset, for undo purpose 46 | std::unordered_map lsn_mapping_; 47 | // log buffer related 48 | int offset_; 49 | char *log_buffer_; 50 | }; 51 | 52 | } // namespace cmudb 53 | -------------------------------------------------------------------------------- /src/include/page/b_plus_tree_internal_page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_internal_page.h 3 | * 4 | * Store n indexed keys and n+1 child pointers (page_id) within internal page. 5 | * Pointer PAGE_ID(i) points to a subtree in which all keys K satisfy: 6 | * K(i) <= K < K(i+1). 7 | * NOTE: since the number of keys does not equal to number of child pointers, 8 | * the first key always remains invalid. That is to say, any search/lookup 9 | * should ignore the first key. 10 | * 11 | * Internal page format (keys are stored in increasing order): 12 | * -------------------------------------------------------------------------- 13 | * | HEADER | KEY(1)+PAGE_ID(1) | KEY(2)+PAGE_ID(2) | ... | KEY(n)+PAGE_ID(n) | 14 | * -------------------------------------------------------------------------- 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | #include "page/b_plus_tree_page.h" 22 | 23 | namespace cmudb { 24 | 25 | #define B_PLUS_TREE_INTERNAL_PAGE_TYPE \ 26 | BPlusTreeInternalPage 27 | 28 | INDEX_TEMPLATE_ARGUMENTS 29 | class BPlusTreeInternalPage : public BPlusTreePage { 30 | public: 31 | // must call initialize method after "create" a new node 32 | void Init(page_id_t page_id, page_id_t parent_id = INVALID_PAGE_ID); 33 | 34 | KeyType KeyAt(int index) const; 35 | void SetKeyAt(int index, const KeyType &key); 36 | int ValueIndex(const ValueType &value) const; 37 | ValueType ValueAt(int index) const; 38 | 39 | ValueType Lookup(const KeyType &key, const KeyComparator &comparator) const; 40 | void PopulateNewRoot(const ValueType &old_value, const KeyType &new_key, 41 | const ValueType &new_value); 42 | int InsertNodeAfter(const ValueType &old_value, const KeyType &new_key, 43 | const ValueType &new_value); 44 | void Remove(int index); 45 | ValueType RemoveAndReturnOnlyChild(); 46 | 47 | void MoveHalfTo(BPlusTreeInternalPage *recipient, 48 | BufferPoolManager *buffer_pool_manager); 49 | void MoveAllTo(BPlusTreeInternalPage *recipient, int index_in_parent, 50 | BufferPoolManager *buffer_pool_manager); 51 | void MoveFirstToEndOf(BPlusTreeInternalPage *recipient, 52 | BufferPoolManager *buffer_pool_manager); 53 | void MoveLastToFrontOf(BPlusTreeInternalPage *recipient, 54 | int parent_index, 55 | BufferPoolManager *buffer_pool_manager); 56 | // DEUBG and PRINT 57 | std::string ToString(bool verbose) const; 58 | void QueueUpChildren(std::queue *queue, 59 | BufferPoolManager *buffer_pool_manager); 60 | 61 | private: 62 | void CopyHalfFrom(MappingType *items, int size, 63 | BufferPoolManager *buffer_pool_manager); 64 | void CopyAllFrom(MappingType *items, int size, 65 | BufferPoolManager *buffer_pool_manager); 66 | void CopyLastFrom(const MappingType &pair, 67 | BufferPoolManager *buffer_pool_manager); 68 | void CopyFirstFrom(const MappingType &pair, int parent_index, 69 | BufferPoolManager *buffer_pool_manager); 70 | MappingType array[0]; 71 | }; 72 | } // namespace cmudb 73 | -------------------------------------------------------------------------------- /src/include/page/b_plus_tree_leaf_page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_leaf_page.h 3 | * 4 | * Store indexed key and record id(record id = page id combined with slot id, 5 | * see include/common/rid.h for detailed implementation) together within leaf 6 | * page. Only support unique key. 7 | 8 | * Leaf page format (keys are stored in order): 9 | * ---------------------------------------------------------------------- 10 | * | HEADER | KEY(1) + RID(1) | KEY(2) + RID(2) | ... | KEY(n) + RID(n) 11 | * ---------------------------------------------------------------------- 12 | * 13 | * Header format (size in byte, 24 bytes in total): 14 | * --------------------------------------------------------------------- 15 | * | PageType (4) | CurrentSize (4) | MaxSize (4) | ParentPageId (4) | 16 | * --------------------------------------------------------------------- 17 | * ------------------------------ 18 | * | PageId (4) | NextPageId (4) 19 | * ------------------------------ 20 | */ 21 | #pragma once 22 | #include 23 | #include 24 | 25 | #include "page/b_plus_tree_page.h" 26 | 27 | 28 | namespace cmudb { 29 | #define B_PLUS_TREE_LEAF_PAGE_TYPE \ 30 | BPlusTreeLeafPage 31 | 32 | INDEX_TEMPLATE_ARGUMENTS 33 | class BPlusTreeLeafPage : public BPlusTreePage { 34 | 35 | public: 36 | // After creating a new leaf page from buffer pool, must call initialize 37 | // method to set default values 38 | void Init(page_id_t page_id, page_id_t parent_id = INVALID_PAGE_ID); 39 | // helper methods 40 | page_id_t GetNextPageId() const; 41 | void SetNextPageId(page_id_t next_page_id); 42 | KeyType KeyAt(int index) const; 43 | int KeyIndex(const KeyType &key, const KeyComparator &comparator) const; 44 | const MappingType &GetItem(int index); 45 | 46 | // insert and delete methods 47 | int Insert(const KeyType &key, const ValueType &value, 48 | const KeyComparator &comparator); 49 | bool Lookup(const KeyType &key, ValueType &value, 50 | const KeyComparator &comparator) const; 51 | int RemoveAndDeleteRecord(const KeyType &key, 52 | const KeyComparator &comparator); 53 | // Split and Merge utility methods 54 | void MoveHalfTo(BPlusTreeLeafPage *recipient, 55 | BufferPoolManager *buffer_pool_manager /* Unused */); 56 | void MoveAllTo(BPlusTreeLeafPage *recipient, int /* Unused */, 57 | BufferPoolManager * /* Unused */); 58 | void MoveFirstToEndOf(BPlusTreeLeafPage *recipient, 59 | BufferPoolManager *buffer_pool_manager); 60 | void MoveLastToFrontOf(BPlusTreeLeafPage *recipient, int parentIndex, 61 | BufferPoolManager *buffer_pool_manager); 62 | // Debug 63 | std::string ToString(bool verbose = false) const; 64 | 65 | private: 66 | void CopyHalfFrom(MappingType *items, int size); 67 | void CopyAllFrom(MappingType *items, int size); 68 | void CopyLastFrom(const MappingType &item); 69 | void CopyFirstFrom(const MappingType &item, int parentIndex, 70 | BufferPoolManager *buffer_pool_manager); 71 | page_id_t next_page_id_; 72 | MappingType array[0]; 73 | }; 74 | } // namespace cmudb 75 | -------------------------------------------------------------------------------- /src/include/page/b_plus_tree_page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_page.h 3 | * 4 | * Both internal and leaf page are inherited from this page. 5 | * 6 | * It actually serves as a header part for each B+ tree page and 7 | * contains information shared by both leaf page and internal page. 8 | * 9 | * Header format (size in byte, 20 bytes in total): 10 | * ---------------------------------------------------------------------------- 11 | * | PageType (4) | LSN (4) | CurrentSize (4) | MaxSize (4) | 12 | * ---------------------------------------------------------------------------- 13 | * | ParentPageId (4) | PageId(4) | 14 | * ---------------------------------------------------------------------------- 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "buffer/buffer_pool_manager.h" 25 | #include "index/generic_key.h" 26 | 27 | namespace cmudb { 28 | 29 | #define MappingType std::pair 30 | 31 | #define INDEX_TEMPLATE_ARGUMENTS \ 32 | template 33 | 34 | // define page type enum 35 | enum class IndexPageType { INVALID_INDEX_PAGE = 0, LEAF_PAGE, INTERNAL_PAGE }; 36 | 37 | // Abstract class. 38 | class BPlusTreePage { 39 | public: 40 | bool IsLeafPage() const; 41 | bool IsRootPage() const; 42 | void SetPageType(IndexPageType page_type); 43 | 44 | int GetSize() const; 45 | void SetSize(int size); 46 | void IncreaseSize(int amount); 47 | 48 | int GetMaxSize() const; 49 | void SetMaxSize(int max_size); 50 | int GetMinSize() const; 51 | 52 | page_id_t GetParentPageId() const; 53 | void SetParentPageId(page_id_t parent_page_id); 54 | 55 | page_id_t GetPageId() const; 56 | void SetPageId(page_id_t page_id); 57 | 58 | void SetLSN(lsn_t lsn = INVALID_LSN); 59 | 60 | private: 61 | // member variable, attributes that both internal and leaf page share 62 | IndexPageType page_type_; 63 | lsn_t lsn_; 64 | int size_; 65 | int max_size_; 66 | page_id_t parent_page_id_; 67 | page_id_t page_id_; 68 | }; 69 | 70 | } // namespace cmudb 71 | -------------------------------------------------------------------------------- /src/include/page/header_page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * header_page.h 3 | * 4 | * Database use the first page (page_id = 0) as header page to store metadata, in 5 | * our case, we will contain information about table/index name (length less than 6 | * 32 bytes) and their corresponding root_id 7 | * 8 | * Format (size in byte): 9 | * ----------------------------------------------------------------- 10 | * | RecordCount (4) | Entry_1 name (32) | Entry_1 root_id (4) | ... | 11 | * ----------------------------------------------------------------- 12 | */ 13 | 14 | #pragma once 15 | 16 | #include "page/page.h" 17 | 18 | #include 19 | 20 | namespace cmudb { 21 | 22 | class HeaderPage : public Page { 23 | public: 24 | void Init() { SetRecordCount(0); } 25 | /** 26 | * Record related 27 | */ 28 | bool InsertRecord(const std::string &name, const page_id_t root_id); 29 | bool DeleteRecord(const std::string &name); 30 | bool UpdateRecord(const std::string &name, const page_id_t root_id); 31 | 32 | // return root_id if success 33 | bool GetRootId(const std::string &name, page_id_t &root_id); 34 | int GetRecordCount(); 35 | 36 | private: 37 | /** 38 | * helper functions 39 | */ 40 | int FindRecord(const std::string &name); 41 | 42 | void SetRecordCount(int record_count); 43 | }; 44 | } // namespace cmudb 45 | -------------------------------------------------------------------------------- /src/include/page/page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * page.h 3 | * 4 | * Wrapper around actual data page in main memory and also contains bookkeeping 5 | * information used by buffer pool manager like pin_count/dirty_flag/page_id. 6 | * Use page as a basic unit within the database system 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include "common/config.h" 15 | #include "common/rwmutex.h" 16 | 17 | namespace cmudb { 18 | 19 | class Page { 20 | friend class BufferPoolManager; 21 | 22 | public: 23 | Page() { ResetMemory(); } 24 | ~Page(){}; 25 | // get actual data page content 26 | inline char *GetData() { return data_; } 27 | // get page id 28 | inline page_id_t GetPageId() { return page_id_; } 29 | // get page pin count 30 | inline int GetPinCount() { return pin_count_; } 31 | // method use to latch/unlatch page content 32 | inline void WUnlatch() { rwlatch_.WUnlock(); } 33 | inline void WLatch() { rwlatch_.WLock(); } 34 | inline void RUnlatch() { rwlatch_.RUnlock(); } 35 | inline void RLatch() { rwlatch_.RLock(); } 36 | 37 | inline lsn_t GetLSN() { return *reinterpret_cast(GetData() + 4); } 38 | inline void SetLSN(lsn_t lsn) { memcpy(GetData() + 4, &lsn, 4); } 39 | 40 | private: 41 | // method used by buffer pool manager 42 | inline void ResetMemory() { memset(data_, 0, PAGE_SIZE); } 43 | // members 44 | char data_[PAGE_SIZE]; // actual data 45 | page_id_t page_id_ = INVALID_PAGE_ID; 46 | int pin_count_ = 0; 47 | bool is_dirty_ = false; 48 | RWMutex rwlatch_; 49 | }; 50 | 51 | } // namespace cmudb 52 | -------------------------------------------------------------------------------- /src/include/page/table_page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * table_page.h 3 | * 4 | * Slotted page format: 5 | * --------------------------------------- 6 | * | HEADER | ... FREE SPACES ... | TUPLES | 7 | * --------------------------------------- 8 | * ^ 9 | * free space pointer 10 | * 11 | * Header format (size in byte): 12 | * -------------------------------------------------------------------------- 13 | * | PageId (4)| LSN (4)| PrevPageId (4)| NextPageId (4)| FreeSpacePointer(4) | 14 | * -------------------------------------------------------------------------- 15 | * -------------------------------------------------------------- 16 | * | TupleCount (4) | Tuple_1 offset (4) | Tuple_1 size (4) | ... | 17 | * -------------------------------------------------------------- 18 | * 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | #include "common/rid.h" 26 | #include "concurrency/lock_manager.h" 27 | #include "logging/log_manager.h" 28 | #include "page/page.h" 29 | #include "table/tuple.h" 30 | 31 | namespace cmudb { 32 | 33 | class TablePage : public Page { 34 | public: 35 | /** 36 | * Header related 37 | */ 38 | void Init(page_id_t page_id, size_t page_size, page_id_t prev_page_id, 39 | LogManager *log_manager, Transaction *txn); 40 | page_id_t GetPageId(); 41 | page_id_t GetPrevPageId(); 42 | page_id_t GetNextPageId(); 43 | void SetPrevPageId(page_id_t prev_page_id); 44 | void SetNextPageId(page_id_t next_page_id); 45 | 46 | /** 47 | * Tuple related 48 | */ 49 | bool InsertTuple(const Tuple &tuple, RID &rid, Transaction *txn, 50 | LockManager *lock_manager, 51 | LogManager *log_manager); // return rid if success 52 | bool MarkDelete(const RID &rid, Transaction *txn, LockManager *lock_manager, 53 | LogManager *log_manager); // delete 54 | bool UpdateTuple(const Tuple &new_tuple, Tuple &old_tuple, const RID &rid, 55 | Transaction *txn, LockManager *lock_manager, 56 | LogManager *log_manager); 57 | 58 | // commit/abort time 59 | void ApplyDelete(const RID &rid, Transaction *txn, 60 | LogManager *log_manager); // when commit success 61 | void RollbackDelete(const RID &rid, Transaction *txn, 62 | LogManager *log_manager); // when commit abort 63 | 64 | // return tuple (with data pointing to heap) if success 65 | bool GetTuple(const RID &rid, Tuple &tuple, Transaction *txn, 66 | LockManager *lock_manager); 67 | 68 | /** 69 | * Tuple iterator 70 | */ 71 | bool GetFirstTupleRid(RID &first_rid); 72 | bool GetNextTupleRid(const RID &cur_rid, RID &next_rid); 73 | 74 | private: 75 | /** 76 | * helper functions 77 | */ 78 | int32_t GetTupleOffset(int slot_num); 79 | int32_t GetTupleSize(int slot_num); 80 | void SetTupleOffset(int slot_num, int32_t offset); 81 | void SetTupleSize(int slot_num, int32_t offset); 82 | int32_t GetFreeSpacePointer(); // offset of the beginning of free space 83 | void SetFreeSpacePointer(int32_t free_space_pointer); 84 | int32_t GetTupleCount(); // Note that this tuple count may be larger than # of 85 | // actual tuples because some slots may be empty 86 | void SetTupleCount(int32_t tuple_count); 87 | int32_t GetFreeSpaceSize(); 88 | }; 89 | } // namespace cmudb 90 | -------------------------------------------------------------------------------- /src/include/table/table_heap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * table_heap.h 3 | * 4 | * doubly-linked list of heap pages 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "buffer/buffer_pool_manager.h" 10 | #include "logging/log_manager.h" 11 | #include "page/table_page.h" 12 | #include "table/table_iterator.h" 13 | #include "table/tuple.h" 14 | 15 | namespace cmudb { 16 | 17 | class TableHeap { 18 | friend class TableIterator; 19 | 20 | public: 21 | ~TableHeap() {} 22 | 23 | // open a table heap 24 | TableHeap(BufferPoolManager *buffer_pool_manager, LockManager *lock_manager, 25 | LogManager *log_manager, page_id_t first_page_id); 26 | 27 | // create table heap 28 | TableHeap(BufferPoolManager *buffer_pool_manager, LockManager *lock_manager, 29 | LogManager *log_manager, Transaction *txn); 30 | 31 | // for insert, if tuple is too large (>~page_size), return false 32 | bool InsertTuple(const Tuple &tuple, RID &rid, Transaction *txn); 33 | 34 | bool MarkDelete(const RID &rid, Transaction *txn); // for delete 35 | 36 | // if the new tuple is too large to fit in the old page, return false (will 37 | // delete and insert) 38 | bool UpdateTuple(const Tuple &tuple, const RID &rid, Transaction *txn); 39 | 40 | // commit/abort time 41 | void ApplyDelete(const RID &rid, 42 | Transaction *txn); // when commit delete or rollback insert 43 | void RollbackDelete(const RID &rid, Transaction *txn); // when rollback delete 44 | 45 | bool GetTuple(const RID &rid, Tuple &tuple, Transaction *txn); 46 | 47 | bool DeleteTableHeap(); 48 | 49 | TableIterator begin(Transaction *txn); 50 | 51 | TableIterator end(); 52 | 53 | inline page_id_t GetFirstPageId() const { return first_page_id_; } 54 | 55 | private: 56 | /** 57 | * Members 58 | */ 59 | BufferPoolManager *buffer_pool_manager_; 60 | LockManager *lock_manager_; 61 | LogManager *log_manager_; 62 | page_id_t first_page_id_; 63 | }; 64 | 65 | } // namespace cmudb 66 | -------------------------------------------------------------------------------- /src/include/table/table_iterator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * table_iterator.h 3 | * 4 | * For seq scan of table heap 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "common/rid.h" 12 | #include "table/tuple.h" 13 | 14 | namespace cmudb { 15 | 16 | class TableHeap; 17 | 18 | class TableIterator { 19 | friend class Cursor; 20 | 21 | public: 22 | TableIterator(TableHeap *table_heap, RID rid, Transaction *txn); 23 | 24 | ~TableIterator() { delete tuple_; } 25 | 26 | inline bool operator==(const TableIterator &itr) const { 27 | return tuple_->rid_.Get() == itr.tuple_->rid_.Get(); 28 | } 29 | 30 | inline bool operator!=(const TableIterator &itr) const { 31 | return !(*this == itr); 32 | } 33 | 34 | const Tuple &operator*(); 35 | 36 | Tuple *operator->(); 37 | 38 | TableIterator &operator++(); 39 | 40 | TableIterator operator++(int); 41 | 42 | private: 43 | TableHeap *table_heap_; 44 | Tuple *tuple_; 45 | Transaction *txn_; 46 | }; 47 | 48 | } // namespace cmudb -------------------------------------------------------------------------------- /src/include/table/tuple.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tuple.h 3 | * 4 | * Tuple format: 5 | * ------------------------------------------------------------------ 6 | * | FIXED-SIZE or VARIED-SIZED OFFSET | PAYLOAD OF VARIED-SIZED FIELD| 7 | * ------------------------------------------------------------------ 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "catalog/schema.h" 13 | #include "common/rid.h" 14 | #include "type/value.h" 15 | 16 | namespace cmudb { 17 | 18 | class Tuple { 19 | friend class TablePage; 20 | 21 | friend class TableHeap; 22 | 23 | friend class TableIterator; 24 | 25 | public: 26 | // Default constructor (to create a dummy tuple) 27 | inline Tuple() : allocated_(false), rid_(RID()), size_(0), data_(nullptr) {} 28 | 29 | // constructor for table heap tuple 30 | Tuple(RID rid) : allocated_(false), rid_(rid) {} 31 | 32 | // constructor for creating a new tuple based on input value 33 | Tuple(std::vector values, Schema *schema); 34 | 35 | // copy constructor, deep copy 36 | Tuple(const Tuple &other); 37 | 38 | // assign operator, deep copy 39 | Tuple &operator=(const Tuple &other); 40 | 41 | ~Tuple() { 42 | if (allocated_) 43 | delete[] data_; 44 | allocated_ = false; 45 | data_ = nullptr; 46 | } 47 | // serialize tuple data 48 | void SerializeTo(char *storage) const; 49 | 50 | // deserialize tuple data(deep copy) 51 | void DeserializeFrom(const char *storage); 52 | 53 | // return RID of current tuple 54 | inline RID GetRid() const { return rid_; } 55 | 56 | // Get the address of this tuple in the table's backing store 57 | inline char *GetData() const { return data_; } 58 | 59 | // Get length of the tuple, including varchar legth 60 | inline int32_t GetLength() const { return size_; } 61 | 62 | // Get the value of a specified column (const) 63 | // checks the schema to see how to return the Value. 64 | Value GetValue(Schema *schema, const int column_id) const; 65 | 66 | // Is the column value null ? 67 | inline bool IsNull(Schema *schema, const int column_id) const { 68 | Value value = GetValue(schema, column_id); 69 | return value.IsNull(); 70 | } 71 | inline bool IsAllocated() { return allocated_; } 72 | 73 | std::string ToString(Schema *schema) const; 74 | 75 | private: 76 | // Get the starting storage address of specific column 77 | const char *GetDataPtr(Schema *schema, const int column_id) const; 78 | 79 | bool allocated_; // is allocated? 80 | RID rid_; // if pointing to the table heap, the rid is valid 81 | int32_t size_; 82 | char *data_; 83 | }; 84 | 85 | } // namespace cmudb 86 | -------------------------------------------------------------------------------- /src/include/type/bigint_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * bigint_type.h 3 | */ 4 | #pragma once 5 | #include "type/integer_parent_type.h" 6 | 7 | namespace cmudb { 8 | // An integer value of the common sizes. 9 | class BigintType : public IntegerParentType { 10 | public: 11 | ~BigintType() {} 12 | 13 | BigintType(); 14 | 15 | // Other mathematical functions 16 | Value Add(const Value &left, const Value &right) const override; 17 | Value Subtract(const Value &left, const Value &right) const override; 18 | Value Multiply(const Value &left, const Value &right) const override; 19 | Value Divide(const Value &left, const Value &right) const override; 20 | Value Modulo(const Value &left, const Value &right) const override; 21 | Value Sqrt(const Value &val) const override; 22 | 23 | // Comparison functions 24 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 25 | CmpBool CompareNotEquals(const Value &left, 26 | const Value &right) const override; 27 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 28 | CmpBool CompareLessThanEquals(const Value &left, 29 | const Value &right) const override; 30 | CmpBool CompareGreaterThan(const Value &left, 31 | const Value &right) const override; 32 | CmpBool CompareGreaterThanEquals(const Value &left, 33 | const Value &right) const override; 34 | 35 | Value CastAs(const Value &val, const TypeId type_id) const override; 36 | 37 | // Debug 38 | std::string ToString(const Value &val) const override; 39 | 40 | // Serialize this value into the given storage space 41 | void SerializeTo(const Value &val, char *storage) const override; 42 | 43 | // Deserialize a value of the given type from the given storage space. 44 | Value DeserializeFrom(const char *storage) const override; 45 | 46 | // Create a copy of this value 47 | Value Copy(const Value &val) const override; 48 | 49 | protected: 50 | Value OperateNull(const Value &left, const Value &right) const override; 51 | 52 | bool IsZero(const Value &val) const override; 53 | }; 54 | } // namespace cmudb 55 | -------------------------------------------------------------------------------- /src/include/type/boolean_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * boolean_type.h 3 | */ 4 | #pragma once 5 | #include "common/exception.h" 6 | #include "type/type.h" 7 | #include "type/value.h" 8 | 9 | namespace cmudb { 10 | // A boolean value isn't a real SQL type, but we treat it as one to keep 11 | // consistent in the expression subsystem. 12 | class BooleanType : public Type { 13 | public: 14 | ~BooleanType() {} 15 | BooleanType(); 16 | 17 | // Comparison functions 18 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 19 | CmpBool CompareNotEquals(const Value &left, 20 | const Value &right) const override; 21 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 22 | CmpBool CompareLessThanEquals(const Value &left, 23 | const Value &right) const override; 24 | CmpBool CompareGreaterThan(const Value &left, 25 | const Value &right) const override; 26 | CmpBool CompareGreaterThanEquals(const Value &left, 27 | const Value &right) const override; 28 | 29 | // Decimal types are always inlined 30 | bool IsInlined(const Value &) const override { return true; } 31 | 32 | // Debug 33 | std::string ToString(const Value &val) const override; 34 | 35 | // Serialize this value into the given storage space 36 | void SerializeTo(const Value &val, char *storage) const override; 37 | 38 | // Deserialize a value of the given type from the given storage space. 39 | Value DeserializeFrom(const char *storage) const override; 40 | 41 | // Create a copy of this value 42 | Value Copy(const Value &val) const override; 43 | 44 | Value CastAs(const Value &val, const TypeId type_id) const override; 45 | }; 46 | } // namespace cmudb 47 | -------------------------------------------------------------------------------- /src/include/type/decimal_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * decimal_type.h 3 | */ 4 | #pragma once 5 | #include "type/numeric_type.h" 6 | 7 | namespace cmudb { 8 | class DecimalType : public NumericType { 9 | public: 10 | DecimalType(); 11 | // DecimalValue(DecDef definition); 12 | 13 | // Other mathematical functions 14 | Value Add(const Value &left, const Value &right) const override; 15 | Value Subtract(const Value &left, const Value &right) const override; 16 | Value Multiply(const Value &left, const Value &right) const override; 17 | Value Divide(const Value &left, const Value &right) const override; 18 | Value Modulo(const Value &left, const Value &right) const override; 19 | Value Min(const Value &left, const Value &right) const override; 20 | Value Max(const Value &left, const Value &right) const override; 21 | Value Sqrt(const Value &val) const override; 22 | bool IsZero(const Value &val) const override; 23 | 24 | // Comparison functions 25 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 26 | CmpBool CompareNotEquals(const Value &left, 27 | const Value &right) const override; 28 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 29 | CmpBool CompareLessThanEquals(const Value &left, 30 | const Value &right) const override; 31 | CmpBool CompareGreaterThan(const Value &left, 32 | const Value &right) const override; 33 | CmpBool CompareGreaterThanEquals(const Value &left, 34 | const Value &right) const override; 35 | 36 | Value CastAs(const Value &val, const TypeId type_id) const override; 37 | 38 | // Decimal types are always inlined 39 | bool IsInlined(const Value &) const override { return true; } 40 | 41 | // Debug 42 | std::string ToString(const Value &val) const override; 43 | 44 | // Serialize this value into the given storage space 45 | void SerializeTo(const Value &val, char *storage) const override; 46 | 47 | // Deserialize a value of the given type from the given storage space. 48 | Value DeserializeFrom(const char *storage) const override; 49 | 50 | // Create a copy of this value 51 | Value Copy(const Value &val) const override; 52 | 53 | private: 54 | Value OperateNull(const Value &left, const Value &right) const override; 55 | }; 56 | } // namespace cmudb 57 | -------------------------------------------------------------------------------- /src/include/type/integer_parent_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * integer_parent_type.h 3 | */ 4 | #pragma once 5 | #include "common/exception.h" 6 | #include "type/numeric_type.h" 7 | 8 | namespace cmudb { 9 | // An integer value of the common sizes. 10 | class IntegerParentType : public NumericType { 11 | public: 12 | ~IntegerParentType() {} 13 | 14 | IntegerParentType(TypeId type); 15 | 16 | // Other mathematical functions 17 | virtual Value Add(const Value &left, const Value &right) const override = 0; 18 | virtual Value Subtract(const Value &left, 19 | const Value &right) const override = 0; 20 | virtual Value Multiply(const Value &left, 21 | const Value &right) const override = 0; 22 | virtual Value Divide(const Value &left, 23 | const Value &right) const override = 0; 24 | virtual Value Modulo(const Value &left, 25 | const Value &right) const override = 0; 26 | Value Min(const Value &left, const Value &right) const override; 27 | Value Max(const Value &left, const Value &right) const override; 28 | virtual Value Sqrt(const Value &val) const override = 0; 29 | 30 | // Comparison functions 31 | virtual CmpBool CompareEquals(const Value &left, 32 | const Value &right) const override = 0; 33 | virtual CmpBool CompareNotEquals(const Value &left, 34 | const Value &right) const override = 0; 35 | virtual CmpBool CompareLessThan(const Value &left, 36 | const Value &right) const override = 0; 37 | virtual CmpBool CompareLessThanEquals(const Value &left, 38 | const Value &right) const override = 0; 39 | virtual CmpBool CompareGreaterThan(const Value &left, 40 | const Value &right) const override = 0; 41 | virtual CmpBool 42 | CompareGreaterThanEquals(const Value &left, 43 | const Value &right) const override = 0; 44 | 45 | virtual Value CastAs(const Value &val, 46 | const TypeId type_id) const override = 0; 47 | 48 | // Integer types are always inlined 49 | bool IsInlined(const Value &) const override { return true; } 50 | 51 | // Debug 52 | virtual std::string ToString(const Value &val) const override = 0; 53 | 54 | // Serialize this value into the given storage space 55 | virtual void SerializeTo(const Value &val, char *storage) const override = 0; 56 | 57 | // Deserialize a value of the given type from the given storage space. 58 | virtual Value DeserializeFrom(const char *storage) const override = 0; 59 | 60 | // Create a copy of this value 61 | virtual Value Copy(const Value &val) const override = 0; 62 | 63 | protected: 64 | template 65 | Value AddValue(const Value &left, const Value &right) const; 66 | template 67 | Value SubtractValue(const Value &left, const Value &right) const; 68 | template 69 | Value MultiplyValue(const Value &left, const Value &right) const; 70 | template 71 | Value DivideValue(const Value &left, const Value &right) const; 72 | template 73 | Value ModuloValue(const Value &left, const Value &right) const; 74 | 75 | virtual Value OperateNull(const Value &left, 76 | const Value &right) const override = 0; 77 | 78 | virtual bool IsZero(const Value &val) const override = 0; 79 | }; 80 | 81 | template 82 | Value IntegerParentType::AddValue(const Value &left, const Value &right) const { 83 | T1 x = left.GetAs(); 84 | T2 y = right.GetAs(); 85 | T1 sum1 = (T1)(x + y); 86 | T2 sum2 = (T2)(x + y); 87 | 88 | if ((x + y) != sum1 && (x + y) != sum2) { 89 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, "Numeric value out of range."); 90 | } 91 | // Overflow detection 92 | if (sizeof(x) >= sizeof(y)) { 93 | if ((x > 0 && y > 0 && sum1 < 0) || (x < 0 && y < 0 && sum1 > 0)) { 94 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 95 | "Numeric value out of range."); 96 | } 97 | return Value(left.GetTypeId(), sum1); 98 | } 99 | if ((x > 0 && y > 0 && sum2 < 0) || (x < 0 && y < 0 && sum2 > 0)) { 100 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, "Numeric value out of range."); 101 | } 102 | return Value(right.GetTypeId(), sum2); 103 | } 104 | 105 | template 106 | Value IntegerParentType::SubtractValue(const Value &left, 107 | const Value &right) const { 108 | T1 x = left.GetAs(); 109 | T2 y = right.GetAs(); 110 | T1 diff1 = (T1)(x - y); 111 | T2 diff2 = (T2)(x - y); 112 | if ((x - y) != diff1 && (x - y) != diff2) { 113 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, "Numeric value out of range."); 114 | } 115 | // Overflow detection 116 | if (sizeof(x) >= sizeof(y)) { 117 | if ((x > 0 && y < 0 && diff1 < 0) || (x < 0 && y > 0 && diff1 > 0)) { 118 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 119 | "Numeric value out of range."); 120 | } 121 | return Value(left.GetTypeId(), diff1); 122 | } 123 | if ((x > 0 && y < 0 && diff2 < 0) || (x < 0 && y > 0 && diff2 > 0)) { 124 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, "Numeric value out of range."); 125 | } 126 | return Value(right.GetTypeId(), diff2); 127 | } 128 | 129 | template 130 | Value IntegerParentType::MultiplyValue(const Value &left, 131 | const Value &right) const { 132 | T1 x = left.GetAs(); 133 | T2 y = right.GetAs(); 134 | T1 prod1 = (T1)(x * y); 135 | T2 prod2 = (T2)(x * y); 136 | if ((x * y) != prod1 && (x * y) != prod2) { 137 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, "Numeric value out of range."); 138 | } 139 | // Overflow detection 140 | if (sizeof(x) >= sizeof(y)) { 141 | if ((y != 0 && prod1 / y != x)) { 142 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 143 | "Numeric value out of range."); 144 | } 145 | return Value(left.GetTypeId(), prod1); 146 | } 147 | if (y != 0 && prod2 / y != x) { 148 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, "Numeric value out of range."); 149 | } 150 | return Value(right.GetTypeId(), prod2); 151 | } 152 | 153 | template 154 | Value IntegerParentType::DivideValue(const Value &left, 155 | const Value &right) const { 156 | T1 x = left.GetAs(); 157 | T2 y = right.GetAs(); 158 | if (y == 0) { 159 | throw Exception(EXCEPTION_TYPE_DIVIDE_BY_ZERO, "Division by zero."); 160 | } 161 | T1 quot1 = (T1)(x / y); 162 | T2 quot2 = (T2)(x / y); 163 | if (sizeof(x) >= sizeof(y)) { 164 | return Value(left.GetTypeId(), quot1); 165 | } 166 | return Value(right.GetTypeId(), quot2); 167 | } 168 | 169 | template 170 | Value IntegerParentType::ModuloValue(const Value &left, 171 | const Value &right) const { 172 | T1 x = left.GetAs(); 173 | T2 y = right.GetAs(); 174 | if (y == 0) { 175 | throw Exception(EXCEPTION_TYPE_DIVIDE_BY_ZERO, "Division by zero."); 176 | } 177 | T1 quot1 = (T1)(x % y); 178 | T2 quot2 = (T2)(x % y); 179 | if (sizeof(x) >= sizeof(y)) { 180 | return Value(left.GetTypeId(), quot1); 181 | } 182 | return Value(right.GetTypeId(), quot2); 183 | } 184 | } // namespace cmudb 185 | -------------------------------------------------------------------------------- /src/include/type/integer_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * integer_type.h 3 | */ 4 | #pragma once 5 | #include "type/integer_parent_type.h" 6 | 7 | namespace cmudb { 8 | // An integer value of the common sizes. 9 | class IntegerType : public IntegerParentType { 10 | public: 11 | ~IntegerType() {} 12 | 13 | IntegerType(TypeId type); 14 | 15 | // Other mathematical functions 16 | Value Add(const Value &left, const Value &right) const override; 17 | Value Subtract(const Value &left, const Value &right) const override; 18 | Value Multiply(const Value &left, const Value &right) const override; 19 | Value Divide(const Value &left, const Value &right) const override; 20 | Value Modulo(const Value &left, const Value &right) const override; 21 | Value Sqrt(const Value &val) const override; 22 | 23 | // Comparison functions 24 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 25 | CmpBool CompareNotEquals(const Value &left, 26 | const Value &right) const override; 27 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 28 | CmpBool CompareLessThanEquals(const Value &left, 29 | const Value &right) const override; 30 | CmpBool CompareGreaterThan(const Value &left, 31 | const Value &right) const override; 32 | CmpBool CompareGreaterThanEquals(const Value &left, 33 | const Value &right) const override; 34 | 35 | Value CastAs(const Value &val, const TypeId type_id) const override; 36 | 37 | // Debug 38 | std::string ToString(const Value &val) const override; 39 | 40 | // Serialize this value into the given storage space 41 | void SerializeTo(const Value &val, char *storage) const override; 42 | 43 | // Deserialize a value of the given type from the given storage space. 44 | Value DeserializeFrom(const char *storage) const override; 45 | 46 | // Create a copy of this value 47 | Value Copy(const Value &val) const override; 48 | 49 | protected: 50 | Value OperateNull(const Value &left, const Value &right) const override; 51 | 52 | bool IsZero(const Value &val) const override; 53 | }; 54 | } // namespace cmudb 55 | -------------------------------------------------------------------------------- /src/include/type/limits.h: -------------------------------------------------------------------------------- 1 | /** 2 | * limits.h 3 | */ 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace cmudb { 11 | 12 | static const double DBL_LOWEST = std::numeric_limits::lowest(); 13 | static const double FLT_LOWEST = std::numeric_limits::lowest(); 14 | 15 | static const int8_t PELOTON_INT8_MIN = (SCHAR_MIN + 1); 16 | static const int16_t PELOTON_INT16_MIN = (SHRT_MIN + 1); 17 | static const int32_t PELOTON_INT32_MIN = (INT_MIN + 1); 18 | static const int64_t PELOTON_INT64_MIN = (LLONG_MIN + 1); 19 | static const double PELOTON_DECIMAL_MIN = FLT_LOWEST; 20 | static const uint64_t PELOTON_TIMESTAMP_MIN = 0; 21 | static const uint32_t PELOTON_DATE_MIN = 0; 22 | static const int8_t PELOTON_BOOLEAN_MIN = 0; 23 | 24 | static const int8_t PELOTON_INT8_MAX = SCHAR_MAX; 25 | static const int16_t PELOTON_INT16_MAX = SHRT_MAX; 26 | static const int32_t PELOTON_INT32_MAX = INT_MAX; 27 | static const int64_t PELOTON_INT64_MAX = LLONG_MAX; 28 | static const uint64_t PELOTON_UINT64_MAX = ULLONG_MAX - 1; 29 | static const double PELOTON_DECIMAL_MAX = DBL_MAX; 30 | static const uint64_t PELOTON_TIMESTAMP_MAX = 11231999986399999999U; 31 | static const uint64_t PELOTON_DATE_MAX = INT_MAX; 32 | static const int8_t PELOTON_BOOLEAN_MAX = 1; 33 | 34 | static const uint32_t PELOTON_VALUE_NULL = UINT_MAX; 35 | static const int8_t PELOTON_INT8_NULL = SCHAR_MIN; 36 | static const int16_t PELOTON_INT16_NULL = SHRT_MIN; 37 | static const int32_t PELOTON_INT32_NULL = INT_MIN; 38 | static const int64_t PELOTON_INT64_NULL = LLONG_MIN; 39 | static const uint64_t PELOTON_DATE_NULL = 0; 40 | static const uint64_t PELOTON_TIMESTAMP_NULL = ULLONG_MAX; 41 | static const double PELOTON_DECIMAL_NULL = DBL_LOWEST; 42 | static const int8_t PELOTON_BOOLEAN_NULL = SCHAR_MIN; 43 | 44 | static const uint32_t PELOTON_VARCHAR_MAX_LEN = UINT_MAX; 45 | 46 | // Use to make TEXT type as the alias of VARCHAR(TEXT_MAX_LENGTH) 47 | static const uint32_t PELOTON_TEXT_MAX_LEN = 1000000000; 48 | 49 | // Objects (i.e., VARCHAR) with length prefix of -1 are NULL 50 | #define OBJECTLENGTH_NULL -1 51 | } // namespace cmudb 52 | -------------------------------------------------------------------------------- /src/include/type/numeric_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * numeric_type.h 3 | */ 4 | #pragma once 5 | #include 6 | 7 | #include "type/value.h" 8 | 9 | namespace cmudb { 10 | // A numeric value is an abstract type representing a number. Numerics can be 11 | // either integral or non-integral (decimal), but must provide arithmetic 12 | // operations on its value. 13 | class NumericType : public Type { 14 | public: 15 | NumericType(TypeId type) : Type(type) {} 16 | ~NumericType() {} 17 | 18 | // Other mathematical functions 19 | virtual Value Add(const Value &left, const Value &right) const = 0; 20 | virtual Value Subtract(const Value &left, const Value &right) const = 0; 21 | virtual Value Multiply(const Value &left, const Value &right) const = 0; 22 | virtual Value Divide(const Value &left, const Value &right) const = 0; 23 | virtual Value Modulo(const Value &left, const Value &right) const = 0; 24 | virtual Value Min(const Value &left, const Value &right) const = 0; 25 | virtual Value Max(const Value &left, const Value &right) const = 0; 26 | virtual Value Sqrt(const Value &val) const = 0; 27 | virtual Value OperateNull(const Value &left, const Value &right) const = 0; 28 | virtual bool IsZero(const Value &val) const = 0; 29 | 30 | protected: 31 | static inline double ValMod(double x, double y) { 32 | return x - std::trunc((double)x / (double)y) * y; 33 | } 34 | }; 35 | } // namespace cmudb 36 | -------------------------------------------------------------------------------- /src/include/type/smallint_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * smallint_type.h 3 | */ 4 | #pragma once 5 | #include "type/integer_parent_type.h" 6 | 7 | namespace cmudb { 8 | // An integer value of the common sizes. 9 | class SmallintType : public IntegerParentType { 10 | public: 11 | ~SmallintType() {} 12 | 13 | SmallintType(); 14 | 15 | // Other mathematical functions 16 | Value Add(const Value &left, const Value &right) const override; 17 | Value Subtract(const Value &left, const Value &right) const override; 18 | Value Multiply(const Value &left, const Value &right) const override; 19 | Value Divide(const Value &left, const Value &right) const override; 20 | Value Modulo(const Value &left, const Value &right) const override; 21 | Value Sqrt(const Value &val) const override; 22 | 23 | // Comparison functions 24 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 25 | CmpBool CompareNotEquals(const Value &left, 26 | const Value &right) const override; 27 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 28 | CmpBool CompareLessThanEquals(const Value &left, 29 | const Value &right) const override; 30 | CmpBool CompareGreaterThan(const Value &left, 31 | const Value &right) const override; 32 | CmpBool CompareGreaterThanEquals(const Value &left, 33 | const Value &right) const override; 34 | 35 | Value CastAs(const Value &val, const TypeId type_id) const override; 36 | 37 | // Debug 38 | std::string ToString(const Value &val) const override; 39 | 40 | // Serialize this value into the given storage space 41 | void SerializeTo(const Value &val, char *storage) const override; 42 | 43 | // Deserialize a value of the given type from the given storage space. 44 | Value DeserializeFrom(const char *storage) const override; 45 | 46 | // Create a copy of this value 47 | Value Copy(const Value &val) const override; 48 | 49 | protected: 50 | Value OperateNull(const Value &left, const Value &right) const override; 51 | 52 | bool IsZero(const Value &val) const override; 53 | }; 54 | } // namespace cmudb 55 | -------------------------------------------------------------------------------- /src/include/type/tinyint_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tinyint_type.h 3 | */ 4 | #pragma once 5 | #include "type/integer_parent_type.h" 6 | 7 | namespace cmudb { 8 | // An integer value of the common sizes. 9 | class TinyintType : public IntegerParentType { 10 | public: 11 | ~TinyintType() {} 12 | 13 | TinyintType(); 14 | 15 | // Other mathematical functions 16 | Value Add(const Value &left, const Value &right) const override; 17 | Value Subtract(const Value &left, const Value &right) const override; 18 | Value Multiply(const Value &left, const Value &right) const override; 19 | Value Divide(const Value &left, const Value &right) const override; 20 | Value Modulo(const Value &left, const Value &right) const override; 21 | Value Sqrt(const Value &val) const override; 22 | 23 | // Comparison functions 24 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 25 | CmpBool CompareNotEquals(const Value &left, 26 | const Value &right) const override; 27 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 28 | CmpBool CompareLessThanEquals(const Value &left, 29 | const Value &right) const override; 30 | CmpBool CompareGreaterThan(const Value &left, 31 | const Value &right) const override; 32 | CmpBool CompareGreaterThanEquals(const Value &left, 33 | const Value &right) const override; 34 | 35 | Value CastAs(const Value &val, const TypeId type_id) const override; 36 | 37 | // Debug 38 | std::string ToString(const Value &val) const override; 39 | 40 | // Serialize this value into the given storage space 41 | void SerializeTo(const Value &val, char *storage) const override; 42 | 43 | // Deserialize a value of the given type from the given storage space. 44 | Value DeserializeFrom(const char *storage) const override; 45 | 46 | // Create a copy of this value 47 | Value Copy(const Value &val) const override; 48 | 49 | protected: 50 | Value OperateNull(const Value &left, const Value &right) const override; 51 | 52 | bool IsZero(const Value &val) const override; 53 | }; 54 | } // namespace cmudb 55 | -------------------------------------------------------------------------------- /src/include/type/type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * type.h 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include "type/type_id.h" 10 | 11 | namespace cmudb { 12 | 13 | class Value; 14 | 15 | enum CmpBool { CMP_FALSE = 0, CMP_TRUE = 1, CMP_NULL = 2 }; 16 | 17 | class Type { 18 | public: 19 | Type(TypeId type_id) : type_id_(type_id) {} 20 | 21 | virtual ~Type() {} 22 | // Get the size of this data type in bytes 23 | static uint64_t GetTypeSize(TypeId type_id); 24 | 25 | // Is this type coercable from the other type 26 | bool IsCoercableFrom(const TypeId type_id) const; 27 | 28 | // Debug info 29 | static std::string TypeIdToString(const TypeId type_id); 30 | 31 | static Value GetMinValue(TypeId type_id); 32 | static Value GetMaxValue(TypeId type_id); 33 | 34 | inline static Type *GetInstance(TypeId type_id) { return kTypes[type_id]; } 35 | 36 | inline TypeId GetTypeId() const { return type_id_; } 37 | 38 | // Comparison functions 39 | // 40 | // NOTE: 41 | // We could get away with only CompareLessThan() being purely virtual, since 42 | // the remaining comparison functions can derive their logic from 43 | // CompareLessThan(). For example: 44 | // 45 | // CompareEquals(o) = !CompareLessThan(o) && !o.CompareLessThan(this) 46 | // CompareNotEquals(o) = !CompareEquals(o) 47 | // CompareLessThanEquals(o) = CompareLessThan(o) || CompareEquals(o) 48 | // CompareGreaterThan(o) = !CompareLessThanEquals(o) 49 | // ... etc. ... 50 | // 51 | // We don't do this for two reasons: 52 | // (1) The redundant calls to CompareLessThan() may be a performance problem, 53 | // and since Value is a core component of the execution engine, we want to 54 | // make it as performant as possible. 55 | // (2) Keep the interface consistent by making all functions purely virtual. 56 | virtual CmpBool CompareEquals(const Value &left, const Value &right) const; 57 | virtual CmpBool CompareNotEquals(const Value &left, const Value &right) const; 58 | virtual CmpBool CompareLessThan(const Value &left, const Value &right) const; 59 | virtual CmpBool CompareLessThanEquals(const Value &left, 60 | const Value &right) const; 61 | virtual CmpBool CompareGreaterThan(const Value &left, 62 | const Value &right) const; 63 | virtual CmpBool CompareGreaterThanEquals(const Value &left, 64 | const Value &right) const; 65 | 66 | // Other mathematical functions 67 | virtual Value Add(const Value &left, const Value &right) const; 68 | virtual Value Subtract(const Value &left, const Value &right) const; 69 | virtual Value Multiply(const Value &left, const Value &right) const; 70 | virtual Value Divide(const Value &left, const Value &right) const; 71 | virtual Value Modulo(const Value &left, const Value &right) const; 72 | virtual Value Min(const Value &left, const Value &right) const; 73 | virtual Value Max(const Value &left, const Value &right) const; 74 | virtual Value Sqrt(const Value &val) const; 75 | virtual Value OperateNull(const Value &val, const Value &right) const; 76 | virtual bool IsZero(const Value &val) const; 77 | 78 | // Is the data inlined into this classes storage space, or must it be accessed 79 | // through an indirection/pointer? 80 | virtual bool IsInlined(const Value &val) const; 81 | 82 | // Return a stringified version of this value 83 | virtual std::string ToString(const Value &val) const; 84 | 85 | // Serialize this value into the given storage space. The inlined parameter 86 | // indicates whether we are allowed to inline this value into the storage 87 | // space, or whether we must store only a reference to this value. If inlined 88 | // is false, we may use the provided data pool to allocate space for this 89 | // value, storing a reference into the allocated pool space in the storage. 90 | virtual void SerializeTo(const Value &val, char *storage) const; 91 | 92 | // Deserialize a value of the given type from the given storage space. 93 | virtual Value DeserializeFrom(const char *storage) const; 94 | 95 | // Create a copy of this value 96 | virtual Value Copy(const Value &val) const; 97 | 98 | virtual Value CastAs(const Value &val, const TypeId type_id) const; 99 | 100 | // Access the raw variable length data 101 | virtual const char *GetData(const Value &val) const; 102 | 103 | // Get the length of the variable length data 104 | virtual uint32_t GetLength(const Value &val) const; 105 | 106 | // Access the raw varlen data stored from the tuple storage 107 | virtual char *GetData(char *storage); 108 | 109 | protected: 110 | // The actual type ID 111 | TypeId type_id_; 112 | // Singleton instances. 113 | static Type *kTypes[14]; 114 | }; 115 | } // namespace cmudb 116 | -------------------------------------------------------------------------------- /src/include/type/type_id.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cmudb { 4 | // Every possible SQL type ID 5 | enum TypeId { 6 | INVALID = 0, 7 | BOOLEAN, 8 | TINYINT, 9 | SMALLINT, 10 | INTEGER, 11 | BIGINT, 12 | DECIMAL, 13 | VARCHAR, 14 | TIMESTAMP, 15 | }; 16 | } // namespace cmudb 17 | -------------------------------------------------------------------------------- /src/include/type/value.h: -------------------------------------------------------------------------------- 1 | /** 2 | * value.h 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "type/limits.h" 9 | #include "type/type.h" 10 | #include 11 | 12 | namespace cmudb { 13 | 14 | class type; 15 | 16 | inline CmpBool GetCmpBool(bool boolean) { 17 | return boolean ? CMP_TRUE : CMP_FALSE; 18 | } 19 | 20 | // A value is an abstract class that represents a view over SQL data stored in 21 | // some materialized state. All values have a type and comparison functions, but 22 | // subclasses implement other type-specific functionality. 23 | class Value { 24 | // Friend Type classes 25 | friend class Type; 26 | friend class NumericType; 27 | friend class IntegerParentType; 28 | friend class TinyintType; 29 | friend class SmallintType; 30 | friend class IntegerType; 31 | friend class BigintType; 32 | friend class DecimalType; 33 | friend class TimestampType; 34 | friend class BooleanType; 35 | friend class VarlenType; 36 | 37 | public: 38 | Value(const TypeId type) : manage_data_(false), type_id_(type) { 39 | size_.len = PELOTON_VALUE_NULL; 40 | } 41 | // BOOLEAN and TINYINT 42 | Value(TypeId type, int8_t val); 43 | // DECIMAL 44 | Value(TypeId type, double d); 45 | Value(TypeId type, float f); 46 | // SMALLINT 47 | Value(TypeId type, int16_t i); 48 | // INTEGER 49 | Value(TypeId type, int32_t i); 50 | // BIGINT 51 | Value(TypeId type, int64_t i); 52 | // TIMESTAMP 53 | Value(TypeId type, uint64_t i); 54 | // VARCHAR 55 | Value(TypeId type, const char *data, uint32_t len, bool manage_data); 56 | Value(TypeId type, const std::string &data); 57 | 58 | Value(); 59 | Value(const Value &other); 60 | Value &operator=(Value other); 61 | ~Value(); 62 | // nothrow 63 | friend void swap(Value &first, Value &second) { 64 | std::swap(first.value_, second.value_); 65 | std::swap(first.size_, second.size_); 66 | std::swap(first.manage_data_, second.manage_data_); 67 | std::swap(first.type_id_, second.type_id_); 68 | } 69 | // check whether value is integer 70 | bool CheckInteger() const; 71 | bool CheckComparable(const Value &o) const; 72 | 73 | // Get the type of this value 74 | inline TypeId GetTypeId() const { return type_id_; } 75 | 76 | // Get the length of the variable length data 77 | inline uint32_t GetLength() const { 78 | return Type::GetInstance(type_id_)->GetLength(*this); 79 | } 80 | // Access the raw variable length data 81 | inline const char *GetData() const { 82 | return Type::GetInstance(type_id_)->GetData(*this); 83 | } 84 | 85 | template inline T GetAs() const { 86 | return *reinterpret_cast(&value_); 87 | } 88 | 89 | inline Value CastAs(const TypeId type_id) const { 90 | return Type::GetInstance(type_id_)->CastAs(*this, type_id); 91 | } 92 | // Comparison Methods 93 | inline CmpBool CompareEquals(const Value &o) const { 94 | return Type::GetInstance(type_id_)->CompareEquals(*this, o); 95 | } 96 | inline CmpBool CompareNotEquals(const Value &o) const { 97 | return Type::GetInstance(type_id_)->CompareNotEquals(*this, o); 98 | } 99 | inline CmpBool CompareLessThan(const Value &o) const { 100 | return Type::GetInstance(type_id_)->CompareLessThan(*this, o); 101 | } 102 | inline CmpBool CompareLessThanEquals(const Value &o) const { 103 | return Type::GetInstance(type_id_)->CompareLessThanEquals(*this, o); 104 | } 105 | inline CmpBool CompareGreaterThan(const Value &o) const { 106 | return Type::GetInstance(type_id_)->CompareGreaterThan(*this, o); 107 | } 108 | inline CmpBool CompareGreaterThanEquals(const Value &o) const { 109 | return Type::GetInstance(type_id_)->CompareGreaterThanEquals(*this, o); 110 | } 111 | 112 | // Other mathematical functions 113 | inline Value Add(const Value &o) const { 114 | return Type::GetInstance(type_id_)->Add(*this, o); 115 | } 116 | inline Value Subtract(const Value &o) const { 117 | return Type::GetInstance(type_id_)->Subtract(*this, o); 118 | } 119 | inline Value Multiply(const Value &o) const { 120 | return Type::GetInstance(type_id_)->Multiply(*this, o); 121 | } 122 | inline Value Divide(const Value &o) const { 123 | return Type::GetInstance(type_id_)->Divide(*this, o); 124 | } 125 | inline Value Modulo(const Value &o) const { 126 | return Type::GetInstance(type_id_)->Modulo(*this, o); 127 | } 128 | inline Value Min(const Value &o) const { 129 | return Type::GetInstance(type_id_)->Min(*this, o); 130 | } 131 | inline Value Max(const Value &o) const { 132 | return Type::GetInstance(type_id_)->Max(*this, o); 133 | } 134 | inline Value Sqrt() const { return Type::GetInstance(type_id_)->Sqrt(*this); } 135 | 136 | inline Value OperateNull(const Value &o) const { 137 | return Type::GetInstance(type_id_)->OperateNull(*this, o); 138 | } 139 | inline bool IsZero() const { 140 | return Type::GetInstance(type_id_)->IsZero(*this); 141 | } 142 | inline bool IsNull() const { return size_.len == PELOTON_VALUE_NULL; } 143 | 144 | // Serialize this value into the given storage space. The inlined parameter 145 | // indicates whether we are allowed to inline this value into the storage 146 | // space, or whether we must store only a reference to this value. If inlined 147 | // is false, we may use the provided data pool to allocate space for this 148 | // value, storing a reference into the allocated pool space in the storage. 149 | inline void SerializeTo(char *storage) const { 150 | Type::GetInstance(type_id_)->SerializeTo(*this, storage); 151 | } 152 | 153 | // Deserialize a value of the given type from the given storage space. 154 | inline static Value DeserializeFrom(const char *storage, 155 | const TypeId type_id) { 156 | return Type::GetInstance(type_id)->DeserializeFrom(storage); 157 | } 158 | 159 | // Return a string version of this value 160 | inline std::string ToString() const { 161 | return Type::GetInstance(type_id_)->ToString(*this); 162 | } 163 | // Create a copy of this value 164 | inline Value Copy() const { return Type::GetInstance(type_id_)->Copy(*this); } 165 | 166 | protected: 167 | // The actual value item 168 | union Val { 169 | int8_t boolean; 170 | int8_t tinyint; 171 | int16_t smallint; 172 | int32_t integer; 173 | int64_t bigint; 174 | double decimal; 175 | uint64_t timestamp; 176 | char *varlen; 177 | const char *const_varlen; 178 | } value_; 179 | 180 | union { 181 | uint32_t len; 182 | TypeId elem_type_id; 183 | } size_; 184 | 185 | bool manage_data_; 186 | // The data type 187 | TypeId type_id_; 188 | }; 189 | } // namespace cmudb 190 | -------------------------------------------------------------------------------- /src/include/type/varlen_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * type.h 3 | */ 4 | #pragma once 5 | #include "type/value.h" 6 | 7 | namespace cmudb { 8 | /* A varlen value is an abstract class representing all objects that have 9 | * variable length. 10 | * For simplicity, for valen_type we always set flag "inline" as true, which 11 | * means we store actual data along with its size rather than a pointer 12 | */ 13 | class VarlenType : public Type { 14 | public: 15 | VarlenType(TypeId type); 16 | ~VarlenType(); 17 | 18 | // Access the raw variable length data 19 | const char *GetData(const Value &val) const override; 20 | 21 | // Get the length of the variable length data 22 | uint32_t GetLength(const Value &val) const override; 23 | 24 | // Comparison functions 25 | CmpBool CompareEquals(const Value &left, const Value &right) const override; 26 | CmpBool CompareNotEquals(const Value &left, 27 | const Value &right) const override; 28 | CmpBool CompareLessThan(const Value &left, const Value &right) const override; 29 | CmpBool CompareLessThanEquals(const Value &left, 30 | const Value &right) const override; 31 | CmpBool CompareGreaterThan(const Value &left, 32 | const Value &right) const override; 33 | CmpBool CompareGreaterThanEquals(const Value &left, 34 | const Value &right) const override; 35 | 36 | // Other mathematical functions 37 | virtual Value Min(const Value &left, const Value &right) const override; 38 | virtual Value Max(const Value &left, const Value &right) const override; 39 | 40 | Value CastAs(const Value &value, const TypeId type_id) const override; 41 | 42 | // Decimal types are always inlined 43 | bool IsInlined(const Value &) const override { return false; } 44 | 45 | // Debug 46 | std::string ToString(const Value &val) const override; 47 | 48 | // Serialize this value into the given storage space 49 | void SerializeTo(const Value &val, char *storage) const override; 50 | 51 | // Deserialize a value of the given type from the given storage space. 52 | Value DeserializeFrom(const char *storage) const override; 53 | 54 | // Create a copy of this value 55 | Value Copy(const Value &val) const override; 56 | }; 57 | } // namespace cmudb 58 | -------------------------------------------------------------------------------- /src/index/b_plus_tree_index.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_index.cpp 3 | */ 4 | 5 | #include "index/b_plus_tree_index.h" 6 | 7 | namespace cmudb { 8 | /* 9 | * Constructor 10 | */ 11 | INDEX_TEMPLATE_ARGUMENTS 12 | BPLUSTREE_INDEX_TYPE::BPlusTreeIndex(IndexMetadata *metadata, 13 | BufferPoolManager *buffer_pool_manager, 14 | page_id_t root_page_id) 15 | : Index(metadata), comparator_(metadata->GetKeySchema()), 16 | container_(metadata->GetName(), buffer_pool_manager, comparator_, 17 | root_page_id) {} 18 | 19 | INDEX_TEMPLATE_ARGUMENTS 20 | void BPLUSTREE_INDEX_TYPE::InsertEntry(const Tuple &key, RID rid, 21 | Transaction *transaction) { 22 | // construct insert index key 23 | KeyType index_key; 24 | index_key.SetFromKey(key); 25 | 26 | container_.Insert(index_key, rid, transaction); 27 | } 28 | 29 | INDEX_TEMPLATE_ARGUMENTS 30 | void BPLUSTREE_INDEX_TYPE::DeleteEntry(const Tuple &key, 31 | Transaction *transaction) { 32 | // construct delete index key 33 | KeyType index_key; 34 | index_key.SetFromKey(key); 35 | 36 | container_.Remove(index_key, transaction); 37 | } 38 | 39 | INDEX_TEMPLATE_ARGUMENTS 40 | void BPLUSTREE_INDEX_TYPE::ScanKey(const Tuple &key, std::vector &result, 41 | Transaction *transaction) { 42 | // construct scan index key 43 | KeyType index_key; 44 | index_key.SetFromKey(key); 45 | 46 | container_.GetValue(index_key, result, transaction); 47 | } 48 | template class BPlusTreeIndex, RID, GenericComparator<4>>; 49 | template class BPlusTreeIndex, RID, GenericComparator<8>>; 50 | template class BPlusTreeIndex, RID, GenericComparator<16>>; 51 | template class BPlusTreeIndex, RID, GenericComparator<32>>; 52 | template class BPlusTreeIndex, RID, GenericComparator<64>>; 53 | 54 | } // namespace cmudb 55 | -------------------------------------------------------------------------------- /src/index/index_iterator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * index_iterator.cpp 3 | */ 4 | #include 5 | 6 | #include "index/index_iterator.h" 7 | 8 | namespace cmudb { 9 | 10 | /* 11 | * NOTE: you can change the destructor/constructor method here 12 | * set your own input parameters 13 | */ 14 | INDEX_TEMPLATE_ARGUMENTS 15 | INDEXITERATOR_TYPE::IndexIterator(page_id_t page_id, int index, BufferPoolManager* manager) : 16 | page_id_(page_id), index_(index), buffer_pool_manager_(manager) { 17 | auto page = buffer_pool_manager_->FetchPage(page_id_); 18 | if(page == nullptr){ 19 | throw Exception(EXCEPTION_TYPE_INDEX, "Index iterator: cannot get page"); 20 | } 21 | leaf_ = reinterpret_cast(page->GetData()); 22 | } 23 | 24 | INDEX_TEMPLATE_ARGUMENTS 25 | INDEXITERATOR_TYPE::~IndexIterator() { 26 | buffer_pool_manager_->UnpinPage(page_id_, false); 27 | } 28 | 29 | 30 | INDEX_TEMPLATE_ARGUMENTS 31 | bool INDEXITERATOR_TYPE::isEnd(){ 32 | auto next_id = leaf_->GetNextPageId(); 33 | return (index_ >= leaf_->GetSize() && next_id == INVALID_PAGE_ID); 34 | } 35 | 36 | INDEX_TEMPLATE_ARGUMENTS 37 | const MappingType &INDEXITERATOR_TYPE::operator*(){ 38 | if(isEnd()){ 39 | throw std::out_of_range("IndexIterator: out of range"); 40 | } 41 | return leaf_->GetItem(index_); 42 | } 43 | 44 | INDEX_TEMPLATE_ARGUMENTS 45 | IndexIterator &INDEXITERATOR_TYPE::operator++(){ 46 | index_ ++; 47 | if(index_ >= leaf_->GetSize()){ 48 | //Fetch next page 49 | page_id_t next = leaf_->GetNextPageId(); 50 | if(next != INVALID_PAGE_ID){ 51 | buffer_pool_manager_->UnpinPage(page_id_, false); 52 | auto page = buffer_pool_manager_->FetchPage(next); 53 | if(page == nullptr){ 54 | throw Exception(EXCEPTION_TYPE_INDEX, "Operator ++: cannot get page"); 55 | } 56 | leaf_ = reinterpret_cast(page->GetData()); 57 | index_ = 0; 58 | page_id_ = next; 59 | } 60 | } 61 | return *this; 62 | } 63 | 64 | 65 | template class IndexIterator, RID, GenericComparator<4>>; 66 | template class IndexIterator, RID, GenericComparator<8>>; 67 | template class IndexIterator, RID, GenericComparator<16>>; 68 | template class IndexIterator, RID, GenericComparator<32>>; 69 | template class IndexIterator, RID, GenericComparator<64>>; 70 | 71 | } // namespace cmudb 72 | -------------------------------------------------------------------------------- /src/logging/log_manager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * log_manager.cpp 3 | */ 4 | 5 | #include "logging/log_manager.h" 6 | 7 | namespace cmudb { 8 | /* 9 | * set ENABLE_LOGGING = true 10 | * Start a separate thread to execute flush to disk operation periodically 11 | * The flush can be triggered when the log buffer is full or buffer pool 12 | * manager wants to force flush (it only happens when the flushed page has a 13 | * larger LSN than persistent LSN) 14 | */ 15 | 16 | void LogManager::ForceFlush(){ 17 | std::unique_lock locker(latch_); 18 | allow_to_flush_ = true; 19 | locker.unlock(); 20 | cv_.notify_one(); 21 | while(!persistent_lsn_changed_); // waiting 22 | persistent_lsn_changed_ = false; 23 | } 24 | 25 | void LogManager::RunFlushThread() { 26 | // If ENABLE_LOGGING already set, no need to set up background thread again 27 | if(!ENABLE_LOGGING){ 28 | ENABLE_LOGGING = true; 29 | flush_thread_ = new std::thread([&](){ 30 | while(ENABLE_LOGGING){ 31 | int flush_log_size = 0, flush_lsn = 0; 32 | std::unique_lock locker(latch_); 33 | if(cv_.wait_for(locker, LOG_TIMEOUT) == std::cv_status::timeout 34 | || allow_to_flush_){ 35 | //Start condition, timeout or nitified 36 | LOG_INFO("Start flush"); 37 | flush_log_size = offset_; 38 | SwapBuffer(); 39 | } 40 | allow_to_flush_ = false; 41 | flush_lsn = next_lsn_ - 1; 42 | locker.unlock(); 43 | 44 | if(ENABLE_LOGGING){ 45 | disk_manager_->WriteLog(flush_buffer_, flush_log_size); 46 | SetPersistentLSN(flush_lsn); 47 | LOG_INFO("End flush, current flush lsn: %d", flush_lsn); 48 | } 49 | } 50 | LOG_INFO("Flush thread end"); 51 | }); 52 | } 53 | } 54 | 55 | /* 56 | * Stop and join the flush thread, set ENABLE_LOGGING = false 57 | */ 58 | void LogManager::StopFlushThread() { 59 | if(ENABLE_LOGGING){ 60 | ENABLE_LOGGING = false; 61 | std::unique_lock locker(latch_); 62 | allow_to_flush_ = true; 63 | locker.unlock(); 64 | cv_.notify_one(); 65 | 66 | if(flush_thread_->joinable()){ 67 | flush_thread_->join(); 68 | } 69 | } 70 | } 71 | 72 | /* 73 | * append a log record into log buffer 74 | * you MUST set the log record's lsn within this method 75 | * @return: lsn that is assigned to this log record 76 | * 77 | * 78 | * example below 79 | * // First, serialize the must have fields(20 bytes in total) 80 | * log_record.lsn_ = next_lsn_++; 81 | * memcpy(log_buffer_ + offset_, &log_record, 20); 82 | * int pos = offset_ + 20; 83 | * 84 | * if (log_record.log_record_type_ == LogRecordType::INSERT) { 85 | * memcpy(log_buffer_ + pos, &log_record.insert_rid_, sizeof(RID)); 86 | * pos += sizeof(RID); 87 | * // we have provided serialize function for tuple class 88 | * log_record.insert_tuple_.SerializeTo(log_buffer_ + pos); 89 | * } 90 | * 91 | */ 92 | lsn_t LogManager::AppendLogRecord(LogRecord &log_record) { 93 | std::unique_lock locker(latch_); 94 | // Current log buffer is full, wake flush thread 95 | if(log_record.GetSize() > LOG_BUFFER_SIZE - offset_){ 96 | allow_to_flush_ = true; 97 | locker.unlock(); 98 | cv_.notify_all(); // notify the flusing thread 99 | locker.lock(); 100 | } 101 | 102 | // Current log buffer can held this record 103 | log_record.lsn_ = next_lsn_++; 104 | memcpy(log_buffer_ + offset_, &log_record, 20); 105 | int pos = offset_ + 20; 106 | 107 | if(log_record.log_record_type_ == LogRecordType::INSERT){ 108 | // Insert log 109 | memcpy(log_buffer_ + pos, &log_record.insert_rid_, sizeof(RID)); 110 | pos += sizeof(RID); 111 | log_record.insert_tuple_.SerializeTo(log_buffer_ + pos); 112 | } else if (log_record.log_record_type_ == LogRecordType::MARKDELETE || 113 | log_record.log_record_type_ == LogRecordType::ROLLBACKDELETE || 114 | log_record.log_record_type_ == LogRecordType::APPLYDELETE){ 115 | // Delete log 116 | memcpy(log_buffer_ + pos, &log_record.delete_rid_, sizeof(RID)); 117 | pos += sizeof(RID); 118 | log_record.delete_tuple_.SerializeTo(log_buffer_ + pos); 119 | 120 | } else if(log_record.log_record_type_ == LogRecordType::UPDATE){ 121 | // Update log 122 | memcpy(log_buffer_ + pos, &log_record.update_rid_, sizeof(RID)); 123 | pos += sizeof(RID); 124 | log_record.old_tuple_.SerializeTo(log_buffer_ + pos); 125 | pos += log_record.old_tuple_.GetLength(); 126 | log_record.new_tuple_.SerializeTo(log_buffer_ + pos); 127 | } else if(log_record.log_record_type_ == LogRecordType::NEWPAGE) { 128 | // New page log 129 | memcpy(log_buffer_ + pos, &log_record.prev_page_id_, sizeof(page_id_t)); 130 | } 131 | 132 | offset_ += log_record.size_; 133 | return log_record.lsn_; 134 | } 135 | 136 | } // namespace cmudb 137 | -------------------------------------------------------------------------------- /src/page/b_plus_tree_page.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_page.cpp 3 | */ 4 | #include "page/b_plus_tree_page.h" 5 | 6 | namespace cmudb { 7 | 8 | /* 9 | * Helper methods to get/set page type 10 | * Page type enum class is defined in b_plus_tree_page.h 11 | */ 12 | bool BPlusTreePage::IsLeafPage() const { 13 | return page_type_ == IndexPageType::LEAF_PAGE; 14 | } 15 | bool BPlusTreePage::IsRootPage() const { 16 | return parent_page_id_ == INVALID_PAGE_ID; 17 | } 18 | void BPlusTreePage::SetPageType(IndexPageType page_type) { 19 | page_type_ = page_type; 20 | } 21 | 22 | /* 23 | * Helper methods to get/set size (number of key/value pairs stored in that 24 | * page) 25 | */ 26 | int BPlusTreePage::GetSize() const { return size_; } 27 | void BPlusTreePage::SetSize(int size) { size_ = size;} 28 | void BPlusTreePage::IncreaseSize(int amount) {size_ += amount;} 29 | 30 | /* 31 | * Helper methods to get/set max size (capacity) of the page 32 | */ 33 | int BPlusTreePage::GetMaxSize() const { return max_size_; } 34 | void BPlusTreePage::SetMaxSize(int size) {max_size_ = size;} 35 | 36 | /* 37 | * Helper method to get min page size 38 | * Generally, min page size == max page size / 2 39 | */ 40 | int BPlusTreePage::GetMinSize() const { return max_size_/2; } 41 | 42 | /* 43 | * Helper methods to get/set parent page id 44 | */ 45 | page_id_t BPlusTreePage::GetParentPageId() const { return parent_page_id_; } 46 | void BPlusTreePage::SetParentPageId(page_id_t parent_page_id) {parent_page_id_ = parent_page_id;} 47 | 48 | /* 49 | * Helper methods to get/set self page id 50 | */ 51 | page_id_t BPlusTreePage::GetPageId() const { return page_id_; } 52 | void BPlusTreePage::SetPageId(page_id_t page_id) {page_id_ = page_id;} 53 | 54 | /* 55 | * Helper methods to set lsn 56 | */ 57 | void BPlusTreePage::SetLSN(lsn_t lsn) { lsn_ = lsn; } 58 | 59 | } // namespace cmudb 60 | -------------------------------------------------------------------------------- /src/page/header_page.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * header_page.cpp 3 | */ 4 | #include 5 | #include 6 | 7 | #include "page/header_page.h" 8 | 9 | namespace cmudb { 10 | 11 | /** 12 | * Record related 13 | */ 14 | bool HeaderPage::InsertRecord(const std::string &name, 15 | const page_id_t root_id) { 16 | assert(name.length() < 32); 17 | assert(root_id > INVALID_PAGE_ID); 18 | 19 | int record_num = GetRecordCount(); 20 | int offset = 4 + record_num * 36; 21 | // check for duplicate name 22 | if (FindRecord(name) != -1) 23 | return false; 24 | // copy record content 25 | memcpy(GetData() + offset, name.c_str(), (name.length() + 1)); 26 | memcpy((GetData() + offset + 32), &root_id, 4); 27 | 28 | SetRecordCount(record_num + 1); 29 | return true; 30 | } 31 | 32 | bool HeaderPage::DeleteRecord(const std::string &name) { 33 | int record_num = GetRecordCount(); 34 | assert(record_num > 0); 35 | 36 | int index = FindRecord(name); 37 | // record does not exsit 38 | if (index == -1) 39 | return false; 40 | int offset = index * 36 + 4; 41 | memmove(GetData() + offset, GetData() + offset + 36, 42 | (record_num - index - 1) * 36); 43 | 44 | SetRecordCount(record_num - 1); 45 | return true; 46 | } 47 | 48 | bool HeaderPage::UpdateRecord(const std::string &name, 49 | const page_id_t root_id) { 50 | assert(name.length() < 32); 51 | 52 | int index = FindRecord(name); 53 | // record does not exsit 54 | if (index == -1) 55 | return false; 56 | int offset = index * 36 + 4; 57 | // update record content, only root_id 58 | memcpy((GetData() + offset + 32), &root_id, 4); 59 | 60 | return true; 61 | } 62 | 63 | bool HeaderPage::GetRootId(const std::string &name, page_id_t &root_id) { 64 | assert(name.length() < 32); 65 | 66 | int index = FindRecord(name); 67 | // record does not exsit 68 | if (index == -1) 69 | return false; 70 | int offset = (index + 1) * 36; 71 | root_id = *reinterpret_cast(GetData() + offset); 72 | 73 | return true; 74 | } 75 | 76 | /** 77 | * helper functions 78 | */ 79 | // record count 80 | int HeaderPage::GetRecordCount() { return *reinterpret_cast(GetData()); } 81 | 82 | void HeaderPage::SetRecordCount(int record_count) { 83 | memcpy(GetData(), &record_count, 4); 84 | } 85 | 86 | int HeaderPage::FindRecord(const std::string &name) { 87 | int record_num = GetRecordCount(); 88 | 89 | for (int i = 0; i < record_num; i++) { 90 | char *raw_name = reinterpret_cast(GetData() + (4 + i * 36)); 91 | if (strcmp(raw_name, name.c_str()) == 0) 92 | return i; 93 | } 94 | return -1; 95 | } 96 | } // namespace cmudb 97 | -------------------------------------------------------------------------------- /src/table/table_heap.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * table_heap.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "common/logger.h" 8 | #include "table/table_heap.h" 9 | 10 | namespace cmudb { 11 | 12 | // open table 13 | TableHeap::TableHeap(BufferPoolManager *buffer_pool_manager, 14 | LockManager *lock_manager, LogManager *log_manager, 15 | page_id_t first_page_id) 16 | : buffer_pool_manager_(buffer_pool_manager), lock_manager_(lock_manager), 17 | log_manager_(log_manager), first_page_id_(first_page_id) {} 18 | 19 | // create table 20 | TableHeap::TableHeap(BufferPoolManager *buffer_pool_manager, 21 | LockManager *lock_manager, LogManager *log_manager, 22 | Transaction *txn) 23 | : buffer_pool_manager_(buffer_pool_manager), lock_manager_(lock_manager), 24 | log_manager_(log_manager) { 25 | auto first_page = 26 | static_cast(buffer_pool_manager_->NewPage(first_page_id_)); 27 | assert(first_page != nullptr); // todo: abort table creation? 28 | first_page->WLatch(); 29 | LOG_DEBUG("new table page created %d", first_page_id_); 30 | 31 | first_page->Init(first_page_id_, PAGE_SIZE, INVALID_LSN, log_manager_, txn); 32 | first_page->WUnlatch(); 33 | buffer_pool_manager_->UnpinPage(first_page_id_, true); 34 | } 35 | 36 | bool TableHeap::InsertTuple(const Tuple &tuple, RID &rid, Transaction *txn) { 37 | if (tuple.size_ + 32 > PAGE_SIZE) { // larger than one page size 38 | txn->SetState(TransactionState::ABORTED); 39 | return false; 40 | } 41 | 42 | auto cur_page = 43 | static_cast(buffer_pool_manager_->FetchPage(first_page_id_)); 44 | if (cur_page == nullptr) { 45 | txn->SetState(TransactionState::ABORTED); 46 | return false; 47 | } 48 | 49 | cur_page->WLatch(); 50 | while (!cur_page->InsertTuple( 51 | tuple, rid, txn, lock_manager_, 52 | log_manager_)) { // fail to insert due to not enough space 53 | auto next_page_id = cur_page->GetNextPageId(); 54 | if (next_page_id != INVALID_PAGE_ID) { // valid next page 55 | cur_page->WUnlatch(); 56 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), false); 57 | cur_page = static_cast( 58 | buffer_pool_manager_->FetchPage(next_page_id)); 59 | cur_page->WLatch(); 60 | } else { // create new page 61 | auto new_page = 62 | static_cast(buffer_pool_manager_->NewPage(next_page_id)); 63 | if (new_page == nullptr) { 64 | cur_page->WUnlatch(); 65 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), false); 66 | txn->SetState(TransactionState::ABORTED); 67 | return false; 68 | } 69 | new_page->WLatch(); 70 | // std::cout << "new table page " << next_page_id << " created" << 71 | // std::endl; 72 | cur_page->SetNextPageId(next_page_id); 73 | new_page->Init(next_page_id, PAGE_SIZE, cur_page->GetPageId(), 74 | log_manager_, txn); 75 | cur_page->WUnlatch(); 76 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), true); 77 | cur_page = new_page; 78 | } 79 | } 80 | cur_page->WUnlatch(); 81 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), true); 82 | txn->GetWriteSet()->emplace_back(rid, WType::INSERT, Tuple{}, this); 83 | return true; 84 | } 85 | 86 | bool TableHeap::MarkDelete(const RID &rid, Transaction *txn) { 87 | // todo: remove empty page 88 | auto page = reinterpret_cast( 89 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 90 | if (page == nullptr) { 91 | txn->SetState(TransactionState::ABORTED); 92 | return false; 93 | } 94 | page->WLatch(); 95 | page->MarkDelete(rid, txn, lock_manager_, log_manager_); 96 | page->WUnlatch(); 97 | buffer_pool_manager_->UnpinPage(page->GetPageId(), true); 98 | txn->GetWriteSet()->emplace_back(rid, WType::DELETE, Tuple{}, this); 99 | return true; 100 | } 101 | 102 | bool TableHeap::UpdateTuple(const Tuple &tuple, const RID &rid, 103 | Transaction *txn) { 104 | auto page = reinterpret_cast( 105 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 106 | if (page == nullptr) { 107 | txn->SetState(TransactionState::ABORTED); 108 | return false; 109 | } 110 | Tuple old_tuple; 111 | page->WLatch(); 112 | bool is_updated = page->UpdateTuple(tuple, old_tuple, rid, txn, lock_manager_, 113 | log_manager_); 114 | page->WUnlatch(); 115 | buffer_pool_manager_->UnpinPage(page->GetPageId(), is_updated); 116 | if (is_updated && txn->GetState() != TransactionState::ABORTED) 117 | txn->GetWriteSet()->emplace_back(rid, WType::UPDATE, old_tuple, this); 118 | return is_updated; 119 | } 120 | 121 | void TableHeap::ApplyDelete(const RID &rid, Transaction *txn) { 122 | auto page = reinterpret_cast( 123 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 124 | assert(page != nullptr); 125 | page->WLatch(); 126 | page->ApplyDelete(rid, txn, log_manager_); 127 | lock_manager_->Unlock(txn, rid); 128 | page->WUnlatch(); 129 | buffer_pool_manager_->UnpinPage(page->GetPageId(), true); 130 | } 131 | 132 | void TableHeap::RollbackDelete(const RID &rid, Transaction *txn) { 133 | auto page = reinterpret_cast( 134 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 135 | assert(page != nullptr); 136 | page->WLatch(); 137 | page->RollbackDelete(rid, txn, log_manager_); 138 | page->WUnlatch(); 139 | buffer_pool_manager_->UnpinPage(page->GetPageId(), true); 140 | } 141 | 142 | // called by tuple iterator 143 | bool TableHeap::GetTuple(const RID &rid, Tuple &tuple, Transaction *txn) { 144 | auto page = static_cast( 145 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 146 | if (page == nullptr) { 147 | txn->SetState(TransactionState::ABORTED); 148 | return false; 149 | } 150 | page->RLatch(); 151 | bool res = page->GetTuple(rid, tuple, txn, lock_manager_); 152 | page->RUnlatch(); 153 | buffer_pool_manager_->UnpinPage(rid.GetPageId(), false); 154 | return res; 155 | } 156 | 157 | bool TableHeap::DeleteTableHeap() { 158 | // todo: real delete 159 | return true; 160 | } 161 | 162 | TableIterator TableHeap::begin(Transaction *txn) { 163 | auto page = 164 | static_cast(buffer_pool_manager_->FetchPage(first_page_id_)); 165 | page->RLatch(); 166 | RID rid; 167 | // if failed (no tuple), rid will be the result of default 168 | // constructor, which means eof 169 | page->GetFirstTupleRid(rid); 170 | page->RUnlatch(); 171 | buffer_pool_manager_->UnpinPage(first_page_id_, false); 172 | return TableIterator(this, rid, txn); 173 | } 174 | 175 | TableIterator TableHeap::end() { 176 | return TableIterator(this, RID(INVALID_PAGE_ID, -1), nullptr); 177 | } 178 | 179 | } // namespace cmudb 180 | -------------------------------------------------------------------------------- /src/table/table_iterator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * table_iterator.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "table/table_heap.h" 8 | 9 | namespace cmudb { 10 | 11 | TableIterator::TableIterator(TableHeap *table_heap, RID rid, Transaction *txn) 12 | : table_heap_(table_heap), tuple_(new Tuple(rid)), txn_(txn) { 13 | if (rid.GetPageId() != INVALID_PAGE_ID) { 14 | table_heap_->GetTuple(tuple_->rid_, *tuple_, txn_); 15 | } 16 | }; 17 | 18 | const Tuple &TableIterator::operator*() { 19 | assert(*this != table_heap_->end()); 20 | return *tuple_; 21 | } 22 | 23 | Tuple *TableIterator::operator->() { 24 | assert(*this != table_heap_->end()); 25 | return tuple_; 26 | } 27 | 28 | TableIterator &TableIterator::operator++() { 29 | BufferPoolManager *buffer_pool_manager = table_heap_->buffer_pool_manager_; 30 | auto cur_page = static_cast( 31 | buffer_pool_manager->FetchPage(tuple_->rid_.GetPageId())); 32 | cur_page->RLatch(); 33 | assert(cur_page != nullptr); // all pages are pinned 34 | 35 | RID next_tuple_rid; 36 | if (!cur_page->GetNextTupleRid(tuple_->rid_, 37 | next_tuple_rid)) { // end of this page 38 | while (cur_page->GetNextPageId() != INVALID_PAGE_ID) { 39 | auto next_page = static_cast( 40 | buffer_pool_manager->FetchPage(cur_page->GetNextPageId())); 41 | cur_page->RUnlatch(); 42 | buffer_pool_manager->UnpinPage(cur_page->GetPageId(), false); 43 | cur_page = next_page; 44 | cur_page->RLatch(); 45 | if (cur_page->GetFirstTupleRid(next_tuple_rid)) 46 | break; 47 | } 48 | } 49 | tuple_->rid_ = next_tuple_rid; 50 | 51 | if (*this != table_heap_->end()) { 52 | table_heap_->GetTuple(tuple_->rid_, *tuple_, txn_); 53 | } 54 | // release until copy the tuple 55 | cur_page->RUnlatch(); 56 | buffer_pool_manager->UnpinPage(cur_page->GetPageId(), false); 57 | return *this; 58 | } 59 | 60 | TableIterator TableIterator::operator++(int) { 61 | TableIterator clone(*this); 62 | ++(*this); 63 | return clone; 64 | } 65 | 66 | } // namespace cmudb 67 | -------------------------------------------------------------------------------- /src/table/tuple.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * tuple.cpp 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common/logger.h" 10 | #include "table/tuple.h" 11 | 12 | namespace cmudb { 13 | 14 | Tuple::Tuple(std::vector values, Schema *schema) : allocated_(true) { 15 | assert((int)values.size() == schema->GetColumnCount()); 16 | 17 | // step1: calculate size of the tuple 18 | int32_t tuple_size = schema->GetLength(); 19 | for (auto &i : schema->GetUnlinedColumns()) 20 | tuple_size += (values[i].GetLength() + sizeof(uint32_t)); 21 | // allocate memory using new, allocated_ flag set as true 22 | size_ = tuple_size; 23 | data_ = new char[size_]; 24 | 25 | // step2: Serialize each column(attribute) based on input value 26 | int column_count = schema->GetColumnCount(); 27 | int32_t offset = schema->GetLength(); 28 | for (int i = 0; i < column_count; i++) { 29 | if (!schema->IsInlined(i)) { 30 | // Serialize relative offset, where the actual varchar data is stored 31 | *reinterpret_cast(data_ + schema->GetOffset(i)) = offset; 32 | // Serialize varchar value, in place(size+data) 33 | values[i].SerializeTo(data_ + offset); 34 | offset += (values[i].GetLength() + sizeof(uint32_t)); 35 | } else { 36 | values[i].SerializeTo(data_ + schema->GetOffset(i)); 37 | } 38 | } 39 | } 40 | 41 | // Copy constructor 42 | Tuple::Tuple(const Tuple &other) 43 | : allocated_(other.allocated_), rid_(other.rid_), size_(other.size_) { 44 | // deep copy 45 | if (allocated_ == true) { 46 | // LOG_DEBUG("tuple deep copy"); 47 | data_ = new char[size_]; 48 | memcpy(data_, other.data_, size_); 49 | } else { 50 | // LOG_DEBUG("tuple shallow copy"); 51 | data_ = other.data_; 52 | } 53 | } 54 | 55 | Tuple &Tuple::operator=(const Tuple &other) { 56 | allocated_ = other.allocated_; 57 | rid_ = other.rid_; 58 | size_ = other.size_; 59 | // deep copy 60 | if (allocated_ == true) { 61 | // LOG_DEBUG("tuple deep copy"); 62 | data_ = new char[size_]; 63 | memcpy(data_, other.data_, size_); 64 | } else { 65 | // LOG_DEBUG("tuple shallow copy"); 66 | data_ = other.data_; 67 | } 68 | 69 | return *this; 70 | } 71 | 72 | // Get the value of a specified column (const) 73 | Value Tuple::GetValue(Schema *schema, const int column_id) const { 74 | assert(schema); 75 | assert(data_); 76 | const TypeId column_type = schema->GetType(column_id); 77 | const char *data_ptr = GetDataPtr(schema, column_id); 78 | // the third parameter "is_inlined" is unused 79 | return Value::DeserializeFrom(data_ptr, column_type); 80 | } 81 | 82 | const char *Tuple::GetDataPtr(Schema *schema, const int column_id) const { 83 | assert(schema); 84 | assert(data_); 85 | bool is_inlined = schema->IsInlined(column_id); 86 | // for inline type, data are stored where they are 87 | if (is_inlined) 88 | return (data_ + schema->GetOffset(column_id)); 89 | else { 90 | // step1: read relative offset from tuple data 91 | int32_t offset = 92 | *reinterpret_cast(data_ + schema->GetOffset(column_id)); 93 | // step 2: return beginning address of the real data for VARCHAR type 94 | return (data_ + offset); 95 | } 96 | } 97 | 98 | std::string Tuple::ToString(Schema *schema) const { 99 | std::stringstream os; 100 | 101 | int column_count = schema->GetColumnCount(); 102 | bool first = true; 103 | os << "("; 104 | for (int column_itr = 0; column_itr < column_count; column_itr++) { 105 | if (first) { 106 | first = false; 107 | } else { 108 | os << ", "; 109 | } 110 | if (IsNull(schema, column_itr)) { 111 | os << ""; 112 | } else { 113 | Value val = (GetValue(schema, column_itr)); 114 | os << val.ToString(); 115 | } 116 | } 117 | os << ")"; 118 | os << " Tuple size is " << size_; 119 | 120 | return os.str(); 121 | } 122 | 123 | void Tuple::SerializeTo(char *storage) const { 124 | memcpy(storage, &size_, sizeof(int32_t)); 125 | memcpy(storage + sizeof(int32_t), data_, size_); 126 | } 127 | 128 | void Tuple::DeserializeFrom(const char *storage) { 129 | uint32_t size = *reinterpret_cast(storage); 130 | // construct a tuple 131 | this->size_ = size; 132 | if (this->allocated_) 133 | delete[] this->data_; 134 | this->data_ = new char[this->size_]; 135 | memcpy(this->data_, storage + sizeof(int32_t), this->size_); 136 | this->allocated_ = true; 137 | } 138 | 139 | } // namespace cmudb 140 | -------------------------------------------------------------------------------- /src/type/boolean_type.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * boolean_type.cpp 3 | */ 4 | #include 5 | 6 | #include "type/boolean_type.h" 7 | 8 | namespace cmudb { 9 | #define BOOLEAN_COMPARE_FUNC(OP) \ 10 | GetCmpBool( \ 11 | left.value_.boolean OP right.CastAs(TypeId::BOOLEAN).value_.boolean) 12 | 13 | BooleanType::BooleanType() : Type(TypeId::BOOLEAN) {} 14 | 15 | CmpBool BooleanType::CompareEquals(const Value &left, 16 | const Value &right) const { 17 | assert(GetTypeId() == TypeId::BOOLEAN); 18 | assert(left.CheckComparable(right)); 19 | if (left.IsNull() || right.IsNull()) 20 | return CMP_NULL; 21 | return BOOLEAN_COMPARE_FUNC(==); 22 | } 23 | 24 | CmpBool BooleanType::CompareNotEquals(const Value &left, 25 | const Value &right) const { 26 | assert(GetTypeId() == TypeId::BOOLEAN); 27 | assert(left.CheckComparable(right)); 28 | if (left.IsNull() || right.IsNull()) 29 | return CMP_NULL; 30 | return BOOLEAN_COMPARE_FUNC(!=); 31 | } 32 | 33 | CmpBool BooleanType::CompareLessThan(const Value &left, 34 | const Value &right) const { 35 | assert(GetTypeId() == TypeId::BOOLEAN); 36 | assert(left.CheckComparable(right)); 37 | if (left.IsNull() || right.IsNull()) 38 | return CMP_NULL; 39 | return BOOLEAN_COMPARE_FUNC(<); 40 | } 41 | 42 | CmpBool BooleanType::CompareLessThanEquals(const Value &left, 43 | const Value &right) const { 44 | assert(GetTypeId() == TypeId::BOOLEAN); 45 | assert(left.CheckComparable(right)); 46 | if (left.IsNull() || right.IsNull()) 47 | return CMP_NULL; 48 | return BOOLEAN_COMPARE_FUNC(<=); 49 | } 50 | 51 | CmpBool BooleanType::CompareGreaterThan(const Value &left, 52 | const Value &right) const { 53 | assert(GetTypeId() == TypeId::BOOLEAN); 54 | assert(left.CheckComparable(right)); 55 | if (left.IsNull() || right.IsNull()) 56 | return CMP_NULL; 57 | return BOOLEAN_COMPARE_FUNC(>); 58 | } 59 | 60 | CmpBool BooleanType::CompareGreaterThanEquals(const Value &left, 61 | const Value &right) const { 62 | assert(GetTypeId() == TypeId::BOOLEAN); 63 | assert(left.CheckComparable(right)); 64 | if (left.IsNull() || right.IsNull()) 65 | return CMP_NULL; 66 | return BOOLEAN_COMPARE_FUNC(>=); 67 | } 68 | 69 | std::string BooleanType::ToString(const Value &val) const { 70 | assert(GetTypeId() == TypeId::BOOLEAN); 71 | if (val.value_.boolean == 1) 72 | return "true"; 73 | else if (val.value_.boolean == 0) 74 | return "false"; 75 | return "boolean_null"; 76 | } 77 | 78 | void BooleanType::SerializeTo(const Value &val, char *storage) const { 79 | *reinterpret_cast(storage) = val.value_.boolean; 80 | return; 81 | } 82 | 83 | // Deserialize a value of the given type from the given storage space. 84 | Value BooleanType::DeserializeFrom(const char *storage) const { 85 | int8_t val = *reinterpret_cast(storage); 86 | return Value(TypeId::BOOLEAN, val); 87 | } 88 | 89 | Value BooleanType::Copy(const Value &val) const { 90 | return Value(TypeId::BOOLEAN, val.value_.boolean); 91 | } 92 | 93 | Value BooleanType::CastAs(const Value &val, const TypeId type_id) const { 94 | switch (type_id) { 95 | case TypeId::BOOLEAN: 96 | return Copy(val); 97 | case TypeId::VARCHAR: { 98 | if (val.IsNull()) 99 | return Value(TypeId::VARCHAR, nullptr, 0, false); 100 | return Value(TypeId::VARCHAR, val.ToString()); 101 | } 102 | default: 103 | break; 104 | } 105 | throw Exception("BOOLEAN is not coercable to " + 106 | Type::TypeIdToString(type_id)); 107 | } 108 | } // namespace cmudb 109 | -------------------------------------------------------------------------------- /src/type/integer_parent_type.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * integer_parent_type.cpp 3 | */ 4 | #include 5 | #include 6 | 7 | #include "type/integer_parent_type.h" 8 | 9 | namespace cmudb { 10 | IntegerParentType::IntegerParentType(TypeId type) : NumericType(type) {} 11 | 12 | Value IntegerParentType::Min(const Value &left, const Value &right) const { 13 | assert(left.CheckInteger()); 14 | assert(left.CheckComparable(right)); 15 | if (left.IsNull() || right.IsNull()) 16 | return left.OperateNull(right); 17 | 18 | if (left.CompareLessThan(right) == CMP_TRUE) 19 | return left.Copy(); 20 | return right.Copy(); 21 | } 22 | 23 | Value IntegerParentType::Max(const Value &left, const Value &right) const { 24 | assert(left.CheckInteger()); 25 | assert(left.CheckComparable(right)); 26 | if (left.IsNull() || right.IsNull()) 27 | return left.OperateNull(right); 28 | 29 | if (left.CompareGreaterThanEquals(right) == CMP_TRUE) 30 | return left.Copy(); 31 | return right.Copy(); 32 | } 33 | } // namespace cmudb 34 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | #TEST CMAKELISTS 3 | ################################################################################## 4 | 5 | #--[Tests lists 6 | file(GLOB test_srcs_temp ${PROJECT_SOURCE_DIR}/test/*/*test.cpp) 7 | 8 | set(test_srcs "") 9 | 10 | foreach(test_src_temp ${test_srcs_temp} ) 11 | string(REPLACE "//" "/" test_src ${test_src_temp}) 12 | list(APPEND test_srcs ${test_src}) 13 | endforeach(test_src_temp ${test_srcs_temp}) 14 | 15 | ################################################################################## 16 | 17 | # --[ Gmock 18 | set(GMOCK_DIR "${PROJECT_SOURCE_DIR}/third_party/gmock") 19 | file(GLOB gmock_srcs ${GMOCK_DIR}/*.cc) 20 | include_directories(SYSTEM ${GMOCK_DIR}) 21 | add_library(gtest EXCLUDE_FROM_ALL ${gmock_srcs}) 22 | target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT}) 23 | 24 | ################################################################################## 25 | 26 | # --[ Add "make check" target 27 | set(CTEST_FLAGS "") 28 | add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} ${CTEST_FLAGS} --verbose) 29 | 30 | ################################################################################## 31 | # --[ Memcheck 32 | find_program(MEMORYCHECK_COMMAND valgrind REQUIRED) 33 | # Note you can add '--gen-suppressions=all' to MEMORYCHECK_COMMAND_OPTIONS 34 | # if you want valgrind to print out the syntax to use to suppress a particular 35 | # memory leak 36 | set(MEMORYCHECK_COMMAND_OPTIONS 37 | --trace-children=yes 38 | --leak-check=full 39 | --track-origins=yes 40 | --soname-synonyms=somalloc=*jemalloc* 41 | --error-exitcode=1) 42 | set(MEMORYCHECK_SUPPRESSIONS_FILE ${PROJECT_SOURCE_DIR}/third_party/valgrind/valgrind.supp) 43 | 44 | ################################################################################## 45 | # --[ Functionality Tests 46 | foreach(test_src ${test_srcs} ) 47 | # get test file name 48 | get_filename_component(test_bare_name ${test_src} NAME) 49 | string(REPLACE ".cpp" "" test_bare_name_without_extension ${test_bare_name}) 50 | string(REPLACE "\"" "" test_name ${test_bare_name_without_extension}) 51 | 52 | # create executable 53 | add_executable(${test_name} EXCLUDE_FROM_ALL ${test_src}) 54 | add_dependencies(check ${test_name}) 55 | 56 | # link libraries 57 | target_link_libraries(${test_name} vtable sqlite3 gtest) 58 | 59 | # set target properties 60 | set_target_properties(${test_name} 61 | PROPERTIES 62 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test" 63 | COMMAND ${test_name} 64 | ) 65 | 66 | # add test 67 | add_test(${test_name} ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} 68 | --suppressions=${MEMORYCHECK_SUPPRESSIONS_FILE} ${CMAKE_BINARY_DIR}/test/${test_name} 69 | --gtest_color=yes --gtest_output=xml:${CMAKE_BINARY_DIR}/test/unit_${test_name}.xml) 70 | add_test(${test_name} ${CMAKE_BINARY_DIR}/test/${test_name} --gtest_color=yes 71 | --gtest_output=xml:${CMAKE_BINARY_DIR}/test/${test_name}.xml) 72 | 73 | endforeach(test_src ${test_srcs}) 74 | -------------------------------------------------------------------------------- /test/buffer/buffer_pool_manager_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * buffer_pool_manager_test.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "buffer/buffer_pool_manager.h" 8 | #include "gtest/gtest.h" 9 | 10 | namespace cmudb { 11 | 12 | TEST(BufferPoolManagerTest, SampleTest) { 13 | page_id_t temp_page_id; 14 | 15 | DiskManager *disk_manager = new DiskManager("test.db"); 16 | BufferPoolManager bpm(10, disk_manager); 17 | 18 | auto page_zero = bpm.NewPage(temp_page_id); 19 | EXPECT_NE(nullptr, page_zero); 20 | EXPECT_EQ(0, temp_page_id); 21 | 22 | // The test will fail here if the page is null 23 | ASSERT_NE(nullptr, page_zero); 24 | 25 | // change content in page one 26 | strcpy(page_zero->GetData(), "Hello"); 27 | 28 | for (int i = 1; i < 10; ++i) { 29 | EXPECT_NE(nullptr, bpm.NewPage(temp_page_id)); 30 | } 31 | // all the pages are pinned, the buffer pool is full 32 | for (int i = 10; i < 15; ++i) { 33 | EXPECT_EQ(nullptr, bpm.NewPage(temp_page_id)); 34 | } 35 | // upin the first five pages, add them to LRU list, set as dirty 36 | for (int i = 0; i < 5; ++i) { 37 | EXPECT_EQ(true, bpm.UnpinPage(i, true)); 38 | } 39 | // we have 5 empty slots in LRU list, evict page zero out of buffer pool 40 | for (int i = 10; i < 14; ++i) { 41 | EXPECT_NE(nullptr, bpm.NewPage(temp_page_id)); 42 | } 43 | // fetch page one again 44 | page_zero = bpm.FetchPage(0); 45 | // check read content 46 | EXPECT_EQ(0, strcmp(page_zero->GetData(), "Hello")); 47 | 48 | remove("test.db"); 49 | } 50 | 51 | } // namespace cmudb 52 | -------------------------------------------------------------------------------- /test/buffer/lru_replacer_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * lru_replacer_test.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "buffer/lru_replacer.h" 8 | #include "gtest/gtest.h" 9 | 10 | namespace cmudb { 11 | 12 | TEST(LRUReplacerTest, SampleTest) { 13 | LRUReplacer lru_replacer; 14 | 15 | // push element into replacer 16 | lru_replacer.Insert(1); 17 | lru_replacer.Insert(2); 18 | lru_replacer.Insert(3); 19 | lru_replacer.Insert(4); 20 | lru_replacer.Insert(5); 21 | lru_replacer.Insert(6); 22 | lru_replacer.Insert(1); 23 | EXPECT_EQ(6, lru_replacer.Size()); 24 | 25 | // pop element from replacer 26 | int value; 27 | lru_replacer.Victim(value); 28 | EXPECT_EQ(2, value); 29 | lru_replacer.Victim(value); 30 | EXPECT_EQ(3, value); 31 | lru_replacer.Victim(value); 32 | EXPECT_EQ(4, value); 33 | 34 | // remove element from replacer 35 | EXPECT_EQ(false, lru_replacer.Erase(4)); 36 | EXPECT_EQ(true, lru_replacer.Erase(6)); 37 | EXPECT_EQ(2, lru_replacer.Size()); 38 | 39 | // pop element from replacer after removal 40 | lru_replacer.Victim(value); 41 | EXPECT_EQ(5, value); 42 | lru_replacer.Victim(value); 43 | EXPECT_EQ(1, value); 44 | } 45 | 46 | } // namespace cmudb 47 | -------------------------------------------------------------------------------- /test/common/rwmutex_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * rwmutex_test.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "common/rwmutex.h" 8 | #include "gtest/gtest.h" 9 | 10 | namespace cmudb { 11 | 12 | class Counter { 13 | public: 14 | Counter() : count_(0), mutex{} {} 15 | void Add(int num) { 16 | mutex.WLock(); 17 | count_ += num; 18 | mutex.WUnlock(); 19 | } 20 | int Read() { 21 | int res; 22 | mutex.RLock(); 23 | res = count_; 24 | mutex.RUnlock(); 25 | return res; 26 | } 27 | private: 28 | int count_; 29 | RWMutex mutex; 30 | }; 31 | 32 | TEST(RWMutexTest, BasicTest) { 33 | int num_threads = 100; 34 | Counter counter{}; 35 | counter.Add(5); 36 | std::vector threads; 37 | for (int tid = 0; tid < num_threads; tid++) { 38 | if (tid % 2 == 0) { 39 | threads.push_back(std::thread([tid, &counter]() { 40 | counter.Read(); 41 | })); 42 | } else { 43 | threads.push_back(std::thread([tid, &counter]() { 44 | counter.Add(1); 45 | })); 46 | } 47 | } 48 | for (int i = 0; i < num_threads; i++) { 49 | threads[i].join(); 50 | } 51 | EXPECT_EQ(counter.Read(), 55); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/hash/extendible_hash_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * extendible_hash_test.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "hash/extendible_hash.h" 8 | #include "gtest/gtest.h" 9 | 10 | namespace cmudb { 11 | 12 | TEST(ExtendibleHashTest, SampleTest) { 13 | // set leaf size as 2 14 | ExtendibleHash *test = 15 | new ExtendibleHash(2); 16 | 17 | // insert several key/value pairs 18 | test->Insert(1, "a"); 19 | test->Insert(2, "b"); 20 | test->Insert(3, "c"); 21 | test->Insert(4, "d"); 22 | test->Insert(5, "e"); 23 | test->Insert(6, "f"); 24 | test->Insert(7, "g"); 25 | test->Insert(8, "h"); 26 | test->Insert(9, "i"); 27 | EXPECT_EQ(2, test->GetLocalDepth(0)); 28 | EXPECT_EQ(3, test->GetLocalDepth(1)); 29 | EXPECT_EQ(2, test->GetLocalDepth(2)); 30 | EXPECT_EQ(2, test->GetLocalDepth(3)); 31 | 32 | // find test 33 | std::string result; 34 | test->Find(9, result); 35 | EXPECT_EQ("i", result); 36 | test->Find(8, result); 37 | EXPECT_EQ("h", result); 38 | test->Find(2, result); 39 | EXPECT_EQ("b", result); 40 | EXPECT_EQ(0, test->Find(10, result)); 41 | 42 | // delete test 43 | EXPECT_EQ(1, test->Remove(8)); 44 | EXPECT_EQ(1, test->Remove(4)); 45 | EXPECT_EQ(1, test->Remove(1)); 46 | EXPECT_EQ(0, test->Remove(20)); 47 | 48 | delete test; 49 | } 50 | 51 | TEST(ExtendibleHashTest, ConcurrentInsertTest) { 52 | const int num_runs = 50; 53 | const int num_threads = 3; 54 | // Run concurrent test multiple times to guarantee correctness. 55 | for (int run = 0; run < num_runs; run++) { 56 | std::shared_ptr> test{new ExtendibleHash(2)}; 57 | std::vector threads; 58 | for (int tid = 0; tid < num_threads; tid++) { 59 | threads.push_back(std::thread([tid, &test]() { 60 | test->Insert(tid, tid); 61 | })); 62 | } 63 | for (int i = 0; i < num_threads; i++) { 64 | threads[i].join(); 65 | } 66 | EXPECT_EQ(test->GetGlobalDepth(), 1); 67 | for (int i = 0; i < num_threads; i++) { 68 | int val; 69 | EXPECT_TRUE(test->Find(i, val)); 70 | EXPECT_EQ(val, i); 71 | } 72 | } 73 | } 74 | 75 | TEST(ExtendibleHashTest, ConcurrentRemoveTest) { 76 | const int num_threads = 5; 77 | const int num_runs = 50; 78 | for (int run = 0; run < num_runs; run++) { 79 | std::shared_ptr> test{new ExtendibleHash(2)}; 80 | std::vector threads; 81 | std::vector values{0, 10, 16, 32, 64}; 82 | for (int value : values) { 83 | test->Insert(value, value); 84 | } 85 | EXPECT_EQ(test->GetGlobalDepth(), 6); 86 | for (int tid = 0; tid < num_threads; tid++) { 87 | threads.push_back(std::thread([tid, &test, &values]() { 88 | test->Remove(values[tid]); 89 | test->Insert(tid + 4, tid + 4); 90 | })); 91 | } 92 | for (int i = 0; i < num_threads; i++) { 93 | threads[i].join(); 94 | } 95 | EXPECT_EQ(test->GetGlobalDepth(), 6); 96 | int val; 97 | EXPECT_EQ(0, test->Find(0, val)); 98 | EXPECT_EQ(1, test->Find(8, val)); 99 | EXPECT_EQ(0, test->Find(16, val)); 100 | EXPECT_EQ(0, test->Find(3, val)); 101 | EXPECT_EQ(1, test->Find(4, val)); 102 | } 103 | } 104 | 105 | } // namespace cmudb 106 | -------------------------------------------------------------------------------- /test/include/logging/common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * common.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "table/tuple.h" 13 | 14 | namespace cmudb { 15 | 16 | // use a fixed schema to construct a random tuple 17 | Tuple ConstructTuple(Schema *schema) { 18 | std::vector values; 19 | Value v(TypeId::INVALID); 20 | 21 | struct timeval t1; 22 | gettimeofday(&t1, NULL); 23 | srand(t1.tv_usec * t1.tv_sec); 24 | 25 | for (int i = 0; i < schema->GetColumnCount(); i++) { 26 | // get type 27 | TypeId type = schema->GetType(i); 28 | switch (type) { 29 | case TypeId::BOOLEAN: 30 | v = Value(type, rand() % 2); 31 | break; 32 | case TypeId::TINYINT: 33 | v = Value(type, (int8_t)rand() % 1000); 34 | break; 35 | case TypeId::SMALLINT: 36 | case TypeId::INTEGER: 37 | v = Value(type, (int32_t)rand() % 1000); 38 | break; 39 | case TypeId::BIGINT: 40 | v = Value(type, (int64_t)rand() % 100000); 41 | break; 42 | case TypeId::VARCHAR: { 43 | static const char alphanum[] = "0123456789" 44 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 45 | "abcdefghijklmnopqrstuvwxyz"; 46 | int len = 1 + rand() % 9; 47 | char s[10]; 48 | for (int i = 0; i < len; ++i) { 49 | s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; 50 | } 51 | s[len] = 0; 52 | v = Value(type, s, len + 1, true); 53 | break; 54 | } 55 | default: 56 | break; 57 | } 58 | values.emplace_back(v); 59 | } 60 | return Tuple(values, schema); 61 | } 62 | 63 | } // namespace cmudb 64 | -------------------------------------------------------------------------------- /test/include/vtable/testing_vtable_util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * testing_vtable_util.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "sqlite/sqlite3.h" 12 | #include "gtest/gtest.h" 13 | 14 | namespace cmudb { 15 | 16 | // For printing result 17 | int ExecCallback(void *NotUsed, int argc, char **argv, char **azColName) { 18 | int i; 19 | for (i = 0; i < argc; i++) { 20 | std::printf("%10s|", azColName[i]); 21 | } 22 | std::printf("\n"); 23 | for (i = 0; i < argc; i++) { 24 | std::printf("%10s|", argv[i] ? argv[i] : "NULL"); 25 | } 26 | std::printf("\n"); 27 | return 0; 28 | } 29 | 30 | bool ExecSQL(sqlite3 *db, std::string sql) { 31 | char *zErrMsg = 0; 32 | int rc = sqlite3_exec(db, sql.c_str(), ExecCallback, 0, &zErrMsg); 33 | if (rc != SQLITE_OK) { 34 | std::cerr << "SQL error: " + std::string(zErrMsg) << std::endl; 35 | sqlite3_free(zErrMsg); 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | } // namespace cmudb -------------------------------------------------------------------------------- /test/index/b_plus_tree_print_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_test.cpp 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "buffer/buffer_pool_manager.h" 10 | #include "common/logger.h" 11 | #include "index/b_plus_tree.h" 12 | #include "vtable/virtual_table.h" 13 | #include "gtest/gtest.h" 14 | 15 | namespace cmudb { 16 | 17 | std::string usageMessage() { 18 | std::string message = 19 | "Enter any of the following commands after the prompt > :\n" 20 | "\ti -- Insert (int64_t) as both key and value).\n" 21 | "\tf -- insert keys bying reading file.\n" 22 | "\td -- delete keys bying reading file.\n" 23 | "\ta -- Delete key and its associated value.\n" 24 | "\tr -- Print the keys and values found in the range [, " 25 | "]\n" 26 | "\tx -- Destroy the whole tree. Start again with an empty tree of the " 27 | "same order.\n" 28 | "\tt -- Print the B+ tree.\n" 29 | "\tq -- Quit. (Or use Ctl-D.)\n" 30 | "\t? -- Print this help message.\n\n"; 31 | return message; 32 | } 33 | 34 | TEST(BptTreeTest, UnitTest) { 35 | int64_t key = 0; 36 | GenericKey<8> index_key; 37 | RID rid; 38 | std::string filename; 39 | char instruction; 40 | bool quit = false; 41 | bool verbose = false; 42 | 43 | std::cout << usageMessage(); 44 | // create KeyComparator and index schema 45 | std::string createStmt = "a bigint"; 46 | Schema *key_schema = ParseCreateStatement(createStmt); 47 | GenericComparator<8> comparator(key_schema); 48 | 49 | DiskManager *disk_manager = new DiskManager("test.db"); 50 | BufferPoolManager *bpm = new BufferPoolManager(100, disk_manager); 51 | // create and fetch header_page 52 | page_id_t page_id; 53 | auto header_page = bpm->NewPage(page_id); 54 | (void)header_page; 55 | // create b+ tree 56 | BPlusTree, RID, GenericComparator<8>> tree("foo_pk", bpm, 57 | comparator); 58 | // create transaction 59 | Transaction *transaction = new Transaction(0); 60 | while (!quit) { 61 | std::cout << "> "; 62 | std::cin >> instruction; 63 | switch (instruction) { 64 | case 'd': 65 | std::cin >> filename; 66 | tree.RemoveFromFile(filename, transaction); 67 | std::cout << tree.ToString(verbose) << '\n'; 68 | break; 69 | case 'a': 70 | std::cin >> key; 71 | index_key.SetFromInteger(key); 72 | tree.Remove(index_key, transaction); 73 | std::cout << tree.ToString(verbose) << '\n'; 74 | break; 75 | case 'i': 76 | std::cin >> key; 77 | rid.Set((int32_t)(key >> 32), (int)(key & 0xFFFFFFFF)); 78 | index_key.SetFromInteger(key); 79 | tree.Insert(index_key, rid, transaction); 80 | std::cout << tree.ToString(verbose) << '\n'; 81 | break; 82 | case 'f': 83 | std::cin >> filename; 84 | tree.InsertFromFile(filename, transaction); 85 | std::cout << tree.ToString(verbose) << '\n'; 86 | break; 87 | case 'q': 88 | quit = true; 89 | break; 90 | case 'r': 91 | std::cin >> key; 92 | index_key.SetFromInteger(key); 93 | for (auto iterator = tree.Begin(index_key); iterator.isEnd() == false; 94 | ++iterator) 95 | std::cout << "key is " << (*iterator).first << " value is " 96 | << (*iterator).second << '\n'; 97 | 98 | break; 99 | case 'v': 100 | verbose = !verbose; 101 | tree.ToString(verbose); 102 | break; 103 | // case 'x': 104 | // tree.destroyTree(); 105 | // tree.print(); 106 | // break; 107 | case '?': 108 | std::cout << usageMessage(); 109 | break; 110 | default: 111 | std::cin.ignore(256, '\n'); 112 | std::cout << usageMessage(); 113 | break; 114 | } 115 | } 116 | bpm->UnpinPage(HEADER_PAGE_ID, true); 117 | delete bpm; 118 | delete transaction; 119 | delete disk_manager; 120 | remove("test.db"); 121 | remove("test.log"); 122 | } 123 | } // namespace cmudb 124 | -------------------------------------------------------------------------------- /test/logging/log_manager_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "logging/common.h" 6 | #include "logging/log_recovery.h" 7 | #include "vtable/virtual_table.h" 8 | #include "gtest/gtest.h" 9 | 10 | namespace cmudb { 11 | 12 | TEST(LogManagerTest, BasicLogging) { 13 | StorageEngine *storage_engine = new StorageEngine("test.db"); 14 | 15 | EXPECT_FALSE(ENABLE_LOGGING); 16 | LOG_DEBUG("Skip system recovering..."); 17 | 18 | storage_engine->log_manager_->RunFlushThread(); 19 | EXPECT_TRUE(ENABLE_LOGGING); 20 | LOG_DEBUG("System logging thread running..."); 21 | 22 | LOG_DEBUG("Create a test table"); 23 | Transaction *txn = storage_engine->transaction_manager_->Begin(); 24 | LOG_INFO("transaction manager start"); 25 | TableHeap *test_table = new TableHeap(storage_engine->buffer_pool_manager_, 26 | storage_engine->lock_manager_, 27 | storage_engine->log_manager_, txn); 28 | LOG_DEBUG("Insert and delete a random tuple"); 29 | 30 | std::string createStmt = 31 | "a varchar, b smallint, c bigint, d bool, e varchar(16)"; 32 | Schema *schema = ParseCreateStatement(createStmt); 33 | RID rid; 34 | Tuple tuple = ConstructTuple(schema); 35 | EXPECT_TRUE(test_table->InsertTuple(tuple, rid, txn)); 36 | EXPECT_TRUE(test_table->MarkDelete(rid, txn)); 37 | storage_engine->transaction_manager_->Commit(txn); 38 | LOG_DEBUG("Commit txn"); 39 | 40 | storage_engine->log_manager_->StopFlushThread(); 41 | EXPECT_FALSE(ENABLE_LOGGING); 42 | LOG_DEBUG("Turning off flushing thread"); 43 | 44 | // some basic manually checking here 45 | char buffer[PAGE_SIZE]; 46 | storage_engine->disk_manager_->ReadLog(buffer, PAGE_SIZE, 0); 47 | int32_t size = *reinterpret_cast(buffer); 48 | LOG_DEBUG("size = %d", size); 49 | size = *reinterpret_cast(buffer + 20); 50 | LOG_DEBUG("size = %d", size); 51 | size = *reinterpret_cast(buffer + 44); 52 | LOG_DEBUG("size = %d", size); 53 | 54 | delete txn; 55 | delete storage_engine; 56 | LOG_DEBUG("Teared down the system"); 57 | remove("test.db"); 58 | remove("test.log"); 59 | } 60 | 61 | // actually LogRecovery 62 | TEST(LogManagerTest, RedoTestWithOneTxn) { 63 | StorageEngine *storage_engine = new StorageEngine("test.db"); 64 | 65 | EXPECT_FALSE(ENABLE_LOGGING); 66 | LOG_DEBUG("Skip system recovering..."); 67 | 68 | storage_engine->log_manager_->RunFlushThread(); 69 | EXPECT_TRUE(ENABLE_LOGGING); 70 | LOG_DEBUG("System logging thread running..."); 71 | 72 | LOG_DEBUG("Create a test table"); 73 | Transaction *txn = storage_engine->transaction_manager_->Begin(); 74 | TableHeap *test_table = new TableHeap(storage_engine->buffer_pool_manager_, 75 | storage_engine->lock_manager_, 76 | storage_engine->log_manager_, txn); 77 | page_id_t first_page_id = test_table->GetFirstPageId(); 78 | 79 | std::string createStmt = 80 | "a varchar, b smallint, c bigint, d bool, e varchar(16)"; 81 | Schema *schema = ParseCreateStatement(createStmt); 82 | 83 | RID rid; 84 | Tuple tuple = ConstructTuple(schema); 85 | std::cout << "Tuple: " << tuple.ToString(schema) << "\n"; 86 | Tuple tuple1 = ConstructTuple(schema); 87 | std::cout << "Tuple1: " << tuple1.ToString(schema) << "\n"; 88 | 89 | auto val = tuple.GetValue(schema, 4); 90 | EXPECT_TRUE(test_table->InsertTuple(tuple, rid, txn)); 91 | storage_engine->transaction_manager_->Commit(txn); 92 | LOG_DEBUG("Commit txn"); 93 | delete txn; 94 | delete test_table; 95 | 96 | LOG_DEBUG("SLEEPING for 2s"); 97 | std::this_thread::sleep_for(std::chrono::seconds(2)); 98 | 99 | // shutdown System 100 | delete storage_engine; 101 | 102 | // restart system 103 | LOG_DEBUG("Restart system"); 104 | storage_engine = new StorageEngine("test.db"); 105 | LogRecovery *log_recovery = new LogRecovery( 106 | storage_engine->disk_manager_, storage_engine->buffer_pool_manager_); 107 | 108 | LOG_DEBUG("System redo"); 109 | log_recovery->Redo(); 110 | LOG_DEBUG("System undo"); 111 | log_recovery->Undo(); 112 | 113 | Tuple old_tuple; 114 | txn = storage_engine->transaction_manager_->Begin(); 115 | test_table = new TableHeap(storage_engine->buffer_pool_manager_, 116 | storage_engine->lock_manager_, 117 | storage_engine->log_manager_, first_page_id); 118 | EXPECT_EQ(test_table->GetTuple(rid, old_tuple, txn), 1); 119 | storage_engine->transaction_manager_->Commit(txn); 120 | delete txn; 121 | delete test_table; 122 | 123 | EXPECT_EQ(old_tuple.GetValue(schema, 4).CompareEquals(val), 1); 124 | 125 | delete storage_engine; 126 | LOG_DEBUG("Teared down the system"); 127 | remove("test.db"); 128 | remove("test.log"); 129 | } 130 | 131 | } // namespace cmudb 132 | -------------------------------------------------------------------------------- /test/table/header_page_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "buffer/buffer_pool_manager.h" 5 | #include "page/header_page.h" 6 | #include "gtest/gtest.h" 7 | 8 | // NOTE: when you running this test, make sure page size(config.h) is at least 9 | // 4096 10 | namespace cmudb { 11 | 12 | TEST(HeaderPageTest, UnitTest) { 13 | DiskManager *disk_manager = new DiskManager("test.db"); 14 | BufferPoolManager *buffer_pool_manager = 15 | new BufferPoolManager(20, disk_manager); 16 | page_id_t header_page_id; 17 | HeaderPage *page = 18 | static_cast(buffer_pool_manager->NewPage(header_page_id)); 19 | ASSERT_NE(nullptr, page); 20 | page->Init(); 21 | 22 | for (int i = 1; i < 28; i++) { 23 | std::string name = std::to_string(i); 24 | EXPECT_EQ(page->InsertRecord(name, i), true); 25 | } 26 | 27 | for (int i = 27; i >= 1; i--) { 28 | std::string name = std::to_string(i); 29 | page_id_t root_id; 30 | EXPECT_EQ(page->GetRootId(name, root_id), true); 31 | // std::cout << "root page id is " << root_id << '\n'; 32 | } 33 | 34 | for (int i = 1; i < 28; i++) { 35 | std::string name = std::to_string(i); 36 | EXPECT_EQ(page->UpdateRecord(name, i + 10), true); 37 | } 38 | 39 | for (int i = 27; i >= 1; i--) { 40 | std::string name = std::to_string(i); 41 | page_id_t root_id; 42 | EXPECT_EQ(page->GetRootId(name, root_id), true); 43 | // std::cout << "root page id is " << root_id << '\n'; 44 | } 45 | 46 | for (int i = 1; i < 28; i++) { 47 | std::string name = std::to_string(i); 48 | EXPECT_EQ(page->DeleteRecord(name), true); 49 | } 50 | 51 | EXPECT_EQ(page->GetRecordCount(), 0); 52 | 53 | delete buffer_pool_manager; 54 | delete disk_manager; 55 | remove("test.db"); 56 | remove("test.log"); 57 | } 58 | } // namespace cmudb 59 | -------------------------------------------------------------------------------- /test/table/tuple_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * tuple_test.cpp 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "buffer/buffer_pool_manager.h" 12 | #include "logging/common.h" 13 | #include "table/table_heap.h" 14 | #include "table/tuple.h" 15 | #include "vtable/virtual_table.h" 16 | #include "gtest/gtest.h" 17 | 18 | namespace cmudb { 19 | TEST(TupleTest, TableHeapTest) { 20 | // test1: parse create sql statement 21 | std::string createStmt = 22 | "a varchar, b smallint, c bigint, d bool, e varchar(16)"; 23 | Schema *schema = ParseCreateStatement(createStmt); 24 | Tuple tuple = ConstructTuple(schema); 25 | 26 | // create transaction 27 | Transaction *transaction = new Transaction(0); 28 | DiskManager *disk_manager = new DiskManager("test.db"); 29 | BufferPoolManager *buffer_pool_manager = 30 | new BufferPoolManager(50, disk_manager); 31 | LockManager *lock_manager = new LockManager(true); 32 | LogManager *log_manager = new LogManager(disk_manager); 33 | TableHeap *table = new TableHeap(buffer_pool_manager, lock_manager, 34 | log_manager, transaction); 35 | 36 | RID rid; 37 | std::vector rid_v; 38 | for (int i = 0; i < 5000; ++i) { 39 | table->InsertTuple(tuple, rid, transaction); 40 | // std::cout << rid << '\n'; 41 | rid_v.push_back(rid); 42 | } 43 | 44 | TableIterator itr = table->begin(transaction); 45 | while (itr != table->end()) { 46 | // std::cout << itr->ToString(schema) << std::endl; 47 | ++itr; 48 | } 49 | 50 | // int i = 0; 51 | std::random_shuffle(rid_v.begin(), rid_v.end()); 52 | for (auto rid : rid_v) { 53 | // std::cout << i++ << std::endl; 54 | assert(table->MarkDelete(rid, transaction) == 1); 55 | } 56 | remove("test.db"); // remove db file 57 | remove("test.log"); 58 | delete schema; 59 | delete table; 60 | delete buffer_pool_manager; 61 | delete disk_manager; 62 | } 63 | 64 | } // namespace cmudb 65 | -------------------------------------------------------------------------------- /test/type/type_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * type_test.cpp 3 | */ 4 | #include "common/exception.h" 5 | #include "type/value.h" 6 | #include "gtest/gtest.h" 7 | 8 | namespace cmudb { 9 | //===--------------------------------------------------------------------===// 10 | // Type Tests 11 | //===--------------------------------------------------------------------===// 12 | const std::vector typeTestTypes = { 13 | TypeId::BOOLEAN, TypeId::TINYINT, TypeId::SMALLINT, 14 | TypeId::INTEGER, TypeId::BIGINT, TypeId::DECIMAL, 15 | }; 16 | 17 | template class BPlusTreePage { 18 | public: 19 | void GetInfo(KeyType key, ValueType val) { 20 | if (key.CompareEquals(val) == CMP_TRUE) 21 | std::cout << "key info" << key.ToString() << '\n'; 22 | } 23 | }; 24 | 25 | TEST(TypeTests, InvalidTypeTest) { 26 | // First get the INVALID type instance 27 | TypeId type_id = TypeId::INVALID; 28 | auto t = Type::GetInstance(type_id); 29 | EXPECT_NE(nullptr, t); 30 | EXPECT_EQ(type_id, t->GetTypeId()); 31 | EXPECT_FALSE(t->IsCoercableFrom(type_id)); 32 | 33 | // Then hit up all of the mofos methods 34 | // They should all throw exceptions 35 | EXPECT_THROW(Type::GetTypeSize(type_id), Exception); 36 | EXPECT_THROW(Type::GetMinValue(type_id), Exception); 37 | EXPECT_THROW(Type::GetMaxValue(type_id), Exception); 38 | } 39 | 40 | TEST(TypeTests, GetInstanceTest) { 41 | for (auto col_type : typeTestTypes) { 42 | auto t = Type::GetInstance(col_type); 43 | EXPECT_NE(nullptr, t); 44 | EXPECT_EQ(col_type, t->GetTypeId()); 45 | EXPECT_TRUE(t->IsCoercableFrom(col_type)); 46 | } 47 | } 48 | 49 | TEST(TypeTests, MaxValueTest) { 50 | for (auto col_type : typeTestTypes) { 51 | auto maxVal = Type::GetMaxValue(col_type); 52 | EXPECT_FALSE(maxVal.IsNull()); 53 | // TODO: We should not be allowed to create a value that is greater than 54 | // the max value. 55 | } 56 | } 57 | 58 | TEST(TypeTests, MinValueTest) { 59 | for (auto col_type : typeTestTypes) { 60 | auto minVal = Type::GetMinValue(col_type); 61 | EXPECT_FALSE(minVal.IsNull()); 62 | // TODO: We should not be allowed to create a value that is less than 63 | // the min value. 64 | } 65 | std::string temp = "32"; 66 | Value val1(TypeId::VARCHAR, temp); 67 | Value val2(TypeId::INTEGER, 32); 68 | EXPECT_EQ(val1.CompareEquals(val2), CMP_TRUE); 69 | } 70 | 71 | TEST(TypeTests, TemplateTest) { 72 | std::string temp = "32"; 73 | Value val1(TypeId::INTEGER, 32); 74 | Value val2(TypeId::INTEGER, 32); 75 | std::cout << "size is " << sizeof(std::make_pair(val1, val2)) << '\n'; 76 | BPlusTreePage node; 77 | node.GetInfo(val1, val2); 78 | } 79 | } // namespace cmudb 80 | -------------------------------------------------------------------------------- /test/vtable/virtual_table_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * virtual_table_test.cpp 3 | */ 4 | #include "vtable/testing_vtable_util.h" 5 | 6 | namespace cmudb { 7 | /** Load the virtual table extension 8 | * Ref: https://sqlite.org/c3ref/load_extension.html 9 | */ 10 | TEST(VtableTest, CreateTest) { 11 | EXPECT_TRUE(sqlite3_threadsafe()); 12 | std::string db_file = "sqlite.db"; 13 | remove(db_file.c_str()); 14 | remove("vtable.db"); 15 | sqlite3 *db; 16 | int rc; 17 | rc = sqlite3_open(db_file.c_str(), &db); 18 | EXPECT_EQ(rc, SQLITE_OK); 19 | 20 | rc = sqlite3_enable_load_extension(db, 1); 21 | EXPECT_EQ(rc, SQLITE_OK); 22 | 23 | const char *zFile = "libvtable"; // shared library name 24 | const char *zProc = 0; // entry point within library 25 | char *zErrMsg = 0; 26 | rc = sqlite3_load_extension(db, zFile, zProc, &zErrMsg); 27 | EXPECT_EQ(rc, SQLITE_OK); 28 | 29 | EXPECT_TRUE(ExecSQL( 30 | db, "CREATE VIRTUAL TABLE foo1 USING vtable ('a INT, b " 31 | "int, c smallint, d varchar, e bigint, f bool', 'foo1_pk b')")); 32 | EXPECT_TRUE(ExecSQL(db, "INSERT INTO foo1 VALUES(1, 2, 3, 'hello', 2,1)")); 33 | EXPECT_TRUE(ExecSQL(db, "INSERT INTO foo1 VALUES(3, 4, 5, 'Nihao',4, 1)")); 34 | EXPECT_TRUE(ExecSQL(db, "INSERT INTO foo1 VALUES(2, 3, 4, 'world',3, 1)")); 35 | EXPECT_TRUE(ExecSQL(db, "SELECT * FROM foo1")); 36 | EXPECT_TRUE(ExecSQL(db, "DELETE FROM foo1 WHERE b = 2")); 37 | EXPECT_TRUE(ExecSQL(db, "SELECT * FROM foo1")); 38 | EXPECT_TRUE(ExecSQL(db, "DROP TABLE foo1")); 39 | 40 | rc = sqlite3_close(db); 41 | EXPECT_EQ(rc, SQLITE_OK); 42 | 43 | remove(db_file.c_str()); 44 | remove("vtable.db"); 45 | return; 46 | } 47 | } // namespace cmudb 48 | -------------------------------------------------------------------------------- /third_party/gmock/gmock_main.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: wan@google.com (Zhanyong Wan) 31 | 32 | #include 33 | #include "gmock/gmock.h" 34 | #include "gtest/gtest.h" 35 | 36 | // MS C++ compiler/linker has a bug on Windows (not on Windows CE), which 37 | // causes a link error when _tmain is defined in a static library and UNICODE 38 | // is enabled. For this reason instead of _tmain, main function is used on 39 | // Windows. See the following link to track the current status of this bug: 40 | // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=394464 // NOLINT 41 | #if GTEST_OS_WINDOWS_MOBILE 42 | # include // NOLINT 43 | 44 | GTEST_API_ int _tmain(int argc, TCHAR** argv) { 45 | #else 46 | GTEST_API_ int main(int argc, char** argv) { 47 | #endif // GTEST_OS_WINDOWS_MOBILE 48 | std::cout << "Running main() from gmock_main.cc\n"; 49 | // Since Google Mock depends on Google Test, InitGoogleMock() is 50 | // also responsible for initializing Google Test. Therefore there's 51 | // no need for calling testing::InitGoogleTest() separately. 52 | testing::InitGoogleMock(&argc, argv); 53 | return RUN_ALL_TESTS(); 54 | } 55 | -------------------------------------------------------------------------------- /third_party/valgrind/valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | Handle GFLAGS leaks - Debug mode 3 | Memcheck:Leak 4 | ... 5 | fun:_ZNSs12_S_constructIPKcEEPcT_S3_RKSaIcESt20forward_iterator_tag 6 | fun:_ZNSsC1EPKcRKSaIcE 7 | fun:_ZN3fLS25dont_pass0toDEFINE_stringEPcPKc 8 | ... 9 | } 10 | 11 | { 12 | Handle GFLAGS leaks - Release mode 13 | Memcheck:Leak 14 | ... 15 | fun:_ZNSsC1EPKcRKSaIcE 16 | fun:_GLOBAL__sub_I_config.cpp 17 | ... 18 | } 19 | 20 | { 21 | Handle init leak (32 bytes) 22 | Memcheck:Leak 23 | ... 24 | fun:_GLOBAL__sub_I_configuration.cpp 25 | fun:call_init.part.0 26 | fun:call_init 27 | fun:_dl_init 28 | ... 29 | } 30 | 31 | { 32 | Handles invalid free in NetworkAddress::Parse because of 'getaddrinfo' 33 | Memcheck:Free 34 | fun:free 35 | fun:__libc_freeres 36 | fun:_vgnU_freeres 37 | fun:__run_exit_handlers 38 | fun:exit 39 | fun:(below main) 40 | } 41 | 42 | { 43 | Handles leaks in libpg_query 44 | Memcheck:Leak 45 | match-leak-kinds: definite 46 | ... 47 | fun:AllocSetContextCreate 48 | fun:MemoryContextInit 49 | fun:pg_query_init 50 | fun:pg_query_enter_memory_context 51 | ... 52 | } 53 | 54 | --------------------------------------------------------------------------------