├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── VERSION.txt ├── src ├── CMakeLists.txt ├── buffer │ ├── buffer_pool_manager.cpp │ └── lru_replacer.cpp ├── catalog │ ├── column.cpp │ └── schema.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 │ ├── 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 ├── 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 │ └── vtable │ │ └── testing_vtable_util.h ├── index │ ├── b_plus_tree_concurrent_test.cpp │ ├── b_plus_tree_print_test.cpp │ └── b_plus_tree_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 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Abhiroop Dabral 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 15-445 Database Systems 2 | # SQLite Project Source Code 3 | 4 | ### Build 5 | ``` 6 | mkdir build 7 | cd build 8 | cmake .. 9 | make 10 | ``` 11 | Debug mode: 12 | 13 | ``` 14 | cmake -DCMAKE_BUILD_TYPE=Debug .. 15 | make 16 | ``` 17 | 18 | ### Testing 19 | ``` 20 | cd build 21 | make check 22 | ``` 23 | 24 | ### Run virtual table extension in SQLite 25 | Start SQLite with: 26 | ``` 27 | cd build 28 | ./bin/sqlite3 29 | ``` 30 | 31 | In SQLite, load virtual table extension with: 32 | 33 | ``` 34 | .load ./lib/libvtable.dylib 35 | ``` 36 | or load `libvtable.so` (Linux), `libvtable.dll` (Windows) 37 | 38 | Create virtual table: 39 | 1.The first input parameter defines the virtual table schema. Please follow the format of (column_name [space] column_type) seperated by comma. We only support basic data types including INTEGER, BIGINT, SMALLINT, BOOLEAN, DECIMAL and VARCHAR. 40 | 2.The second parameter define the index schema. Please follow the format of (index_name [space] indexed_column_names) seperated by comma. 41 | ``` 42 | sqlite> CREATE VIRTUAL TABLE foo USING vtable('a int, b varchar(13)','foo_pk a') 43 | ``` 44 | 45 | After creating virtual table: 46 | Type in any sql statements as you want. 47 | ``` 48 | sqlite> INSERT INTO foo values(1,'hello'); 49 | sqlite> SELECT * FROM foo ORDER BY a; 50 | a b 51 | ---------- ---------- 52 | 1 hello 53 | ``` 54 | See [Run-Time Loadable Extensions](https://sqlite.org/loadext.html) and [CREATE VIRTUAL TABLE](https://sqlite.org/lang_createvtab.html) for further information. 55 | 56 | ### Virtual table API 57 | https://sqlite.org/vtab.html 58 | 59 | ### TODO 60 | * update: when size exceed that page, table heap returns false and delete/insert tuple (rid will change and need to delete/insert from index) 61 | * delete empty page from table heap when delete tuple 62 | * implement delete table, with empty page bitmap in disk manager (how to persistent?) 63 | * index: unique/dup key, variable key 64 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 15-445/645 Project Source Code 2 | ------------------------------------------------------------ 3 | Created: Oct 26 2017 @ 18:01:53 4 | Last Commit: fc181c8abb34ddb964c7f75cf70a16d994a2c70a 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/buffer_pool_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "buffer/buffer_pool_manager.h" 2 | 3 | namespace cmudb { 4 | 5 | /* 6 | * BufferPoolManager Constructor 7 | * WARNING: Do Not Edit This Function 8 | */ 9 | BufferPoolManager::BufferPoolManager(size_t pool_size, 10 | const std::string &db_file) 11 | : pool_size_(pool_size), disk_manager_{db_file} { 12 | // a consecutive memory space for buffer pool 13 | pages_ = new Page[pool_size_]; 14 | page_table_ = new ExtendibleHash(100); 15 | replacer_ = new LRUReplacer; 16 | free_list_ = new std::list; 17 | 18 | // put all the pages into free list 19 | for (size_t i = 0; i < pool_size_; ++i) { 20 | free_list_->push_back(&pages_[i]); 21 | } 22 | } 23 | 24 | /* 25 | * BufferPoolManager Deconstructor 26 | * WARNING: Do Not Edit This Function 27 | */ 28 | BufferPoolManager::~BufferPoolManager() { 29 | FlushAllPages(); 30 | delete[] pages_; 31 | delete page_table_; 32 | delete replacer_; 33 | delete free_list_; 34 | } 35 | 36 | /** 37 | * 1. search hash table. 38 | * 1.1 if exist, pin the page and return immediately 39 | * 1.2 if no exist, find a replacement entry from either free list or lru 40 | * replacer. (NOTE: always find from free list first) 41 | * 2. If the entry chosen for replacement is dirty, write it back to disk. 42 | * 3. Delete the entry for the old page from the hash table and insert an entry 43 | * for the new page. 44 | * 4. Update page metadata, read page content from disk file and return page 45 | * pointer 46 | */ 47 | Page *BufferPoolManager::FetchPage(page_id_t page_id) { return nullptr; } 48 | 49 | /* 50 | * Implementation of unpin page 51 | * if pin_count>0, decrement it and if it becomes zero, put it back to replacer 52 | * if pin_count<=0 before this call, return false. 53 | * is_dirty: set the dirty flag of this page 54 | */ 55 | bool BufferPoolManager::UnpinPage(page_id_t page_id, bool is_dirty) { 56 | return false; 57 | } 58 | 59 | /* 60 | * Used to flush a particular page of the buffer pool to disk. Should call the 61 | * write_page method of the disk manager 62 | * if page is not found in page table, return false 63 | * NOTE: make sure page_id != INVALID_PAGE_ID 64 | */ 65 | bool BufferPoolManager::FlushPage(page_id_t page_id) { return false; } 66 | 67 | /* 68 | * Used to flush all dirty pages in the buffer pool manager 69 | */ 70 | void BufferPoolManager::FlushAllPages() {} 71 | 72 | /** 73 | * User should call this method for deleting a page. This routine will call disk 74 | * manager to deallocate the page. 75 | * First, if page is found within page table, buffer pool manager should be 76 | * reponsible for removing this entry out of page table, reseting page metadata 77 | * and adding back to free list. Second, call disk manager's DeallocatePage() 78 | * method to delete from disk file. 79 | * If the page is found within page table, but pin_count != 0, return false 80 | */ 81 | bool BufferPoolManager::DeletePage(page_id_t page_id) { return false; } 82 | 83 | /** 84 | * User should call this method if needs to create a new page. This routine 85 | * will call disk manager to allocate a page. 86 | * Buffer pool manager should be responsible to choose a victim page either from 87 | * free list or lru replacer(NOTE: always choose from free list first), update 88 | * new page's metadata, zero out memory and add corresponding entry into page 89 | * table. 90 | * return nullptr is all the pages in pool are pinned 91 | */ 92 | Page *BufferPoolManager::NewPage(page_id_t &page_id) { return nullptr; } 93 | } // namespace cmudb 94 | -------------------------------------------------------------------------------- /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 LRUReplacer::LRUReplacer() {} 10 | 11 | template LRUReplacer::~LRUReplacer() {} 12 | 13 | /* 14 | * Insert value into LRU 15 | */ 16 | template void LRUReplacer::Insert(const T &value) {} 17 | 18 | /* If LRU is non-empty, pop the head member from LRU to argument "value", and 19 | * return true. If LRU is empty, return false 20 | */ 21 | template bool LRUReplacer::Victim(T &value) { 22 | return false; 23 | } 24 | 25 | /* 26 | * Remove value from LRU. If removal is successful, return true, otherwise 27 | * return false 28 | */ 29 | template bool LRUReplacer::Erase(const T &value) { 30 | return false; 31 | } 32 | 33 | template size_t LRUReplacer::Size() { return 0; } 34 | 35 | template class LRUReplacer; 36 | // test only 37 | template class LRUReplacer; 38 | 39 | } // namespace cmudb 40 | -------------------------------------------------------------------------------- /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/concurrency/lock_manager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * lock_manager.cpp 3 | */ 4 | 5 | #include "concurrency/lock_manager.h" 6 | 7 | namespace cmudb { 8 | 9 | bool LockManager::LockShared(Transaction *txn, const RID &rid) { 10 | return false; 11 | } 12 | 13 | bool LockManager::LockExclusive(Transaction *txn, const RID &rid) { 14 | return false; 15 | } 16 | 17 | bool LockManager::LockUpgrade(Transaction *txn, const RID &rid) { 18 | return false; 19 | } 20 | 21 | bool LockManager::Unlock(Transaction *txn, const RID &rid) { 22 | return false; 23 | } 24 | 25 | } // namespace cmudb 26 | -------------------------------------------------------------------------------- /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 | namespace cmudb { 9 | 10 | void TransactionManager::Commit(Transaction *txn) { 11 | txn->SetState(TransactionState::COMMITTED); 12 | // truly delete before commit 13 | auto write_set = txn->GetWriteSet(); 14 | while (!write_set->empty()) { 15 | auto &item = write_set->back(); 16 | auto table = item.table_; 17 | if (item.wtype_ == WType::DELETE) { 18 | // this also release the lock when holding the page latch 19 | table->ApplyDelete(item.rid_, txn); 20 | } 21 | write_set->pop_back(); 22 | } 23 | write_set->clear(); 24 | 25 | std::unordered_set lock_set; 26 | for (auto item : *txn->GetSharedLockSet()) 27 | lock_set.emplace(item); 28 | for (auto item : *txn->GetExclusiveLockSet()) 29 | lock_set.emplace(item); 30 | // release all the lock 31 | for (auto locked_rid : lock_set) { 32 | lock_manager_->Unlock(txn, locked_rid); 33 | } 34 | } 35 | 36 | void TransactionManager::Abort(Transaction *txn) { 37 | txn->SetState(TransactionState::ABORTED); 38 | // rollback before releasing lock 39 | auto write_set = txn->GetWriteSet(); 40 | while (!write_set->empty()) { 41 | auto &item = write_set->back(); 42 | auto table = item.table_; 43 | if (item.wtype_ == WType::DELETE) { 44 | LOG_DEBUG("rollback delete"); 45 | table->RollbackDelete(item.rid_, txn); 46 | } else if (item.wtype_ == WType::INSERT) { 47 | LOG_DEBUG("rollback insert"); 48 | // this also release the lock when holding the page latch 49 | table->ApplyDelete(item.rid_, txn); 50 | } else if (item.wtype_ == WType::UPDATE) { 51 | LOG_DEBUG("rollback update"); 52 | table->UpdateTuple(item.tuple_, item.rid_, txn); 53 | } 54 | write_set->pop_back(); 55 | } 56 | write_set->clear(); 57 | 58 | std::unordered_set lock_set; 59 | for (auto item : *txn->GetSharedLockSet()) 60 | lock_set.emplace(item); 61 | for (auto item : *txn->GetExclusiveLockSet()) 62 | lock_set.emplace(item); 63 | // release all the lock 64 | for (auto locked_rid : lock_set) { 65 | lock_manager_->Unlock(txn, locked_rid); 66 | } 67 | } 68 | } // namespace cmudb 69 | -------------------------------------------------------------------------------- /src/disk/disk_manager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * disk_manager.cpp 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/logger.h" 9 | #include "disk/disk_manager.h" 10 | 11 | namespace cmudb { 12 | 13 | /** 14 | * Constructor: open/create a single database file 15 | * @input db_file: database file name 16 | */ 17 | DiskManager::DiskManager(const std::string &db_file) 18 | : file_name_(db_file), next_page_id_(0) { 19 | db_io_.open(db_file, std::ios::binary | std::ios::in | std::ios::out); 20 | // directory or file does not exist 21 | if (!db_io_.is_open()) { 22 | db_io_.clear(); 23 | // create a new file 24 | db_io_.open(db_file, std::ios::binary | std::ios::trunc | std::ios::out); 25 | db_io_.close(); 26 | // reopen with original mode 27 | db_io_.open(db_file, std::ios::binary | std::ios::in | std::ios::out); 28 | } 29 | } 30 | 31 | DiskManager::~DiskManager() { db_io_.close(); } 32 | 33 | /** 34 | * Write the contents of the specified page into disk file 35 | */ 36 | void DiskManager::WritePage(page_id_t page_id, const char *page_data) { 37 | size_t offset = page_id * PAGE_SIZE; 38 | // set write cursor to offset 39 | db_io_.seekp(offset); 40 | db_io_.write(page_data, PAGE_SIZE); 41 | // check for I/O error 42 | if (db_io_.bad()) { 43 | LOG_DEBUG("I/O error while writing"); 44 | return; 45 | } 46 | // needs to flush to keep disk file in sync 47 | db_io_.flush(); 48 | } 49 | 50 | /** 51 | * Read the contents of the specified page into the given memory area 52 | */ 53 | void DiskManager::ReadPage(page_id_t page_id, char *page_data) { 54 | int offset = page_id * PAGE_SIZE; 55 | // check if read beyond file length 56 | if (offset >= GetFileSize()) { 57 | LOG_DEBUG("I/O error while reading"); 58 | // std::cerr << "I/O error while reading" << std::endl; 59 | } else { 60 | // set read cursor to offset 61 | db_io_.seekp(offset); 62 | db_io_.read(page_data, PAGE_SIZE); 63 | // if file ends before reading PAGE_SIZE 64 | int read_count = db_io_.gcount(); 65 | if (read_count < PAGE_SIZE) { 66 | LOG_DEBUG("Read less than a page"); 67 | // std::cerr << "Read less than a page" << std::endl; 68 | memset(page_data + read_count, 0, PAGE_SIZE - read_count); 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Allocate new page (operations like create index/table) 75 | * For now just keep an increasing counter 76 | */ 77 | page_id_t DiskManager::AllocatePage() { return next_page_id_++; } 78 | 79 | /** 80 | * Deallocate page (operations like drop index/table) 81 | * Need bitmap in header page for tracking pages 82 | */ 83 | void DiskManager::DeallocatePage(__attribute__((unused)) page_id_t page_id) { 84 | return; 85 | } 86 | 87 | /** 88 | * Private helper function to get disk file size 89 | */ 90 | int DiskManager::GetFileSize() { 91 | struct stat stat_buf; 92 | int rc = stat(file_name_.c_str(), &stat_buf); 93 | return rc == 0 ? stat_buf.st_size : -1; 94 | } 95 | 96 | } // namespace cmudb 97 | -------------------------------------------------------------------------------- /src/hash/extendible_hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hash/extendible_hash.h" 4 | #include "page/page.h" 5 | 6 | namespace cmudb { 7 | 8 | /* 9 | * constructor 10 | * array_size: fixed array size for each bucket 11 | */ 12 | template 13 | ExtendibleHash::ExtendibleHash(size_t size) {} 14 | 15 | /* 16 | * helper function to calculate the hashing address of input key 17 | */ 18 | template 19 | size_t ExtendibleHash::HashKey(const K &key) { 20 | return 0; 21 | } 22 | 23 | /* 24 | * helper function to return global depth of hash table 25 | * NOTE: you must implement this function in order to pass test 26 | */ 27 | template 28 | int ExtendibleHash::GetGlobalDepth() const { 29 | return 0; 30 | } 31 | 32 | /* 33 | * helper function to return local depth of one specific bucket 34 | * NOTE: you must implement this function in order to pass test 35 | */ 36 | template 37 | int ExtendibleHash::GetLocalDepth(int bucket_id) const { 38 | return 0; 39 | } 40 | 41 | /* 42 | * helper function to return current number of bucket in hash table 43 | */ 44 | template 45 | int ExtendibleHash::GetNumBuckets() const { 46 | return 0; 47 | } 48 | 49 | /* 50 | * lookup function to find value associate with input key 51 | */ 52 | template 53 | bool ExtendibleHash::Find(const K &key, V &value) { 54 | return false; 55 | } 56 | 57 | /* 58 | * delete entry in hash table 59 | * Shrink & Combination is not required for this project 60 | */ 61 | template 62 | bool ExtendibleHash::Remove(const K &key) { 63 | return false; 64 | } 65 | 66 | /* 67 | * insert entry in hash table 68 | * Split & Redistribute bucket when there is overflow and if necessary increase 69 | * global depth 70 | */ 71 | template 72 | void ExtendibleHash::Insert(const K &key, const V &value) {} 73 | 74 | template class ExtendibleHash; 75 | template class ExtendibleHash::iterator>; 76 | // test purpose 77 | template class ExtendibleHash; 78 | template class ExtendibleHash::iterator>; 79 | template class ExtendibleHash; 80 | } // namespace cmudb 81 | -------------------------------------------------------------------------------- /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 | 13 | #include "buffer/lru_replacer.h" 14 | #include "disk/disk_manager.h" 15 | #include "hash/extendible_hash.h" 16 | #include "page/page.h" 17 | 18 | namespace cmudb { 19 | class BufferPoolManager { 20 | public: 21 | BufferPoolManager(size_t pool_size, const std::string &db_file); 22 | 23 | ~BufferPoolManager(); 24 | 25 | Page *FetchPage(page_id_t page_id); 26 | 27 | bool UnpinPage(page_id_t page_id, bool is_dirty); 28 | 29 | bool FlushPage(page_id_t page_id); 30 | 31 | void FlushAllPages(); 32 | 33 | Page *NewPage(page_id_t &page_id); 34 | 35 | bool DeletePage(page_id_t page_id); 36 | 37 | private: 38 | size_t pool_size_; 39 | // array of pages 40 | Page *pages_; 41 | DiskManager disk_manager_; 42 | // to keep track of page id and its memory location 43 | HashTable *page_table_; 44 | // to collect unpinned pages for replacement 45 | Replacer *replacer_; 46 | // to collect free pages for replacement 47 | std::list *free_list_; 48 | // to protect shared data structure, you may need it for synchronization 49 | // between replacer and page table 50 | std::mutex latch_; 51 | }; 52 | } // namespace cmudb 53 | -------------------------------------------------------------------------------- /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 | 15 | namespace cmudb { 16 | 17 | template class LRUReplacer : public Replacer { 18 | public: 19 | // do not change public interface 20 | LRUReplacer(); 21 | 22 | ~LRUReplacer(); 23 | 24 | void Insert(const T &value); 25 | 26 | bool Victim(T &value); 27 | 28 | bool Erase(const T &value); 29 | 30 | size_t Size(); 31 | 32 | private: 33 | // add your member variables here 34 | }; 35 | 36 | } // namespace cmudb 37 | -------------------------------------------------------------------------------- /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 | #include 8 | 9 | namespace cmudb { 10 | 11 | #define INVALID_PAGE_ID -1 // representing an invalid page id 12 | #define INVALID_TXN_ID -1 // representing an invalid txn id 13 | #define HEADER_PAGE_ID 0 // the header page id 14 | #define PAGE_SIZE 4096 // size of a data page in byte 15 | #define BUCKET_SIZE 50 // size of extendible hash bucket 16 | 17 | typedef int32_t page_id_t; // page id type 18 | typedef int32_t txn_id_t; // transaction id type 19 | 20 | } // namespace cmudb 21 | -------------------------------------------------------------------------------- /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 | 15 | #include "common/rid.h" 16 | #include "concurrency/transaction.h" 17 | 18 | namespace cmudb { 19 | 20 | class LockManager { 21 | 22 | public: 23 | LockManager(bool strict_2PL) : strict_2PL_(strict_2PL){}; 24 | 25 | /*** below are APIs need to implement ***/ 26 | // lock: 27 | // return false if transaction is aborted 28 | // it should be blocked on waiting and should return true when granted 29 | // note the behavior of trying to lock locked rids by same txn is undefined 30 | // it is transaction's job to keep track of its current locks 31 | bool LockShared(Transaction *txn, const RID &rid); 32 | bool LockExclusive(Transaction *txn, const RID &rid); 33 | bool LockUpgrade(Transaction *txn, const RID &rid); 34 | 35 | // unlock: 36 | // release the lock hold by the txn 37 | bool Unlock(Transaction *txn, const RID &rid); 38 | /*** END OF APIs ***/ 39 | 40 | private: 41 | bool strict_2PL_; 42 | }; 43 | 44 | } // namespace cmudb 45 | -------------------------------------------------------------------------------- /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), 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 | 80 | inline std::shared_ptr> GetDeletedPageSet() { 81 | return deleted_page_set_; 82 | } 83 | 84 | inline void AddIntoDeletedPageSet(page_id_t page_id) { 85 | deleted_page_set_->insert(page_id); 86 | } 87 | 88 | inline std::shared_ptr> GetSharedLockSet() { 89 | return shared_lock_set_; 90 | } 91 | 92 | inline std::shared_ptr> GetExclusiveLockSet() { 93 | return exclusive_lock_set_; 94 | } 95 | 96 | inline TransactionState GetState() { return state_; } 97 | 98 | inline void SetState(TransactionState state) { state_ = state; } 99 | 100 | private: 101 | TransactionState state_; 102 | // thread id, single-threaded transactions 103 | std::thread::id thread_id_; 104 | // transaction id 105 | txn_id_t txn_id_; 106 | // Below are used by transaction, undo set 107 | std::shared_ptr> write_set_; 108 | 109 | // Below are used by concurrent index 110 | // this deque contains page pointer that was latche during index operation 111 | std::shared_ptr> page_set_; 112 | // this set contains page_id that was deleted during index operation 113 | std::shared_ptr> deleted_page_set_; 114 | 115 | // Below are used by lock manager 116 | // this set contains rid of shared-locked tuples by this transaction 117 | std::shared_ptr> shared_lock_set_; 118 | // this set contains rid of exclusive-locked tuples by this transaction 119 | std::shared_ptr> exclusive_lock_set_; 120 | }; 121 | } // namespace cmudb 122 | -------------------------------------------------------------------------------- /src/include/concurrency/transaction_manager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * transaction_manager.h 3 | * 4 | */ 5 | 6 | #pragma once 7 | #include "concurrency/lock_manager.h" 8 | 9 | namespace cmudb { 10 | class TransactionManager { 11 | public: 12 | TransactionManager(LockManager *lock_manager) : lock_manager_(lock_manager) {} 13 | 14 | void Commit(Transaction *txn); 15 | void Abort(Transaction *txn); 16 | 17 | private: 18 | LockManager *lock_manager_; 19 | }; 20 | 21 | } // namespace cmudb 22 | -------------------------------------------------------------------------------- /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 | 15 | #include "common/config.h" 16 | 17 | namespace cmudb { 18 | 19 | class DiskManager { 20 | public: 21 | DiskManager(const std::string &db_file); 22 | ~DiskManager(); 23 | 24 | void WritePage(page_id_t page_id, const char *page_data); 25 | void ReadPage(page_id_t page_id, char *page_data); 26 | 27 | page_id_t AllocatePage(); 28 | void DeallocatePage(page_id_t page_id); 29 | 30 | private: 31 | int GetFileSize(); 32 | std::fstream db_io_; 33 | std::string file_name_; 34 | std::atomic next_page_id_; 35 | }; 36 | 37 | } // namespace cmudb 38 | -------------------------------------------------------------------------------- /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 | 16 | #include "hash/hash_table.h" 17 | 18 | namespace cmudb { 19 | 20 | template 21 | class ExtendibleHash : public HashTable { 22 | public: 23 | // constructor 24 | ExtendibleHash(size_t size); 25 | // helper function to generate hash addressing 26 | size_t HashKey(const K &key); 27 | // helper function to get global & local depth 28 | int GetGlobalDepth() const; 29 | int GetLocalDepth(int bucket_id) const; 30 | int GetNumBuckets() const; 31 | // lookup and modifier 32 | bool Find(const K &key, V &value) override; 33 | bool Remove(const K &key) override; 34 | void Insert(const K &key, const V &value) override; 35 | 36 | private: 37 | // add your own member variables here 38 | }; 39 | } // namespace cmudb 40 | -------------------------------------------------------------------------------- /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 | namespace cmudb { 22 | 23 | #define BPLUSTREE_TYPE BPlusTree 24 | // Main class providing the API for the Interactive B+ Tree. 25 | INDEX_TEMPLATE_ARGUMENTS 26 | class BPlusTree { 27 | public: 28 | explicit BPlusTree(const std::string &name, 29 | BufferPoolManager *buffer_pool_manager, 30 | const KeyComparator &comparator, 31 | page_id_t root_page_id = INVALID_PAGE_ID); 32 | 33 | // Returns true if this B+ tree has no keys and values. 34 | bool IsEmpty() const; 35 | 36 | // Insert a key-value pair into this B+ tree. 37 | bool Insert(const KeyType &key, const ValueType &value, 38 | Transaction *transaction = nullptr); 39 | 40 | // Remove a key and its value from this B+ tree. 41 | void Remove(const KeyType &key, Transaction *transaction = nullptr); 42 | 43 | // return the value associated with a given key 44 | bool GetValue(const KeyType &key, std::vector &result, 45 | Transaction *transaction = nullptr); 46 | 47 | // index iterator 48 | INDEXITERATOR_TYPE Begin(); 49 | INDEXITERATOR_TYPE Begin(const KeyType &key); 50 | 51 | // Print this B+ tree to stdout using a simple command-line 52 | std::string ToString(bool verbose = false); 53 | 54 | // read data from file and insert one by one 55 | void InsertFromFile(const std::string &file_name, 56 | Transaction *transaction = nullptr); 57 | 58 | // read data from file and remove one by one 59 | void RemoveFromFile(const std::string &file_name, 60 | Transaction *transaction = nullptr); 61 | // expose for test purpose 62 | B_PLUS_TREE_LEAF_PAGE_TYPE *FindLeafPage(const KeyType &key, 63 | bool leftMost = false); 64 | 65 | private: 66 | void StartNewTree(const KeyType &key, const ValueType &value); 67 | 68 | bool InsertIntoLeaf(const KeyType &key, const ValueType &value, 69 | Transaction *transaction = nullptr); 70 | 71 | void InsertIntoParent(BPlusTreePage *old_node, const KeyType &key, 72 | BPlusTreePage *new_node, 73 | Transaction *transaction = nullptr); 74 | 75 | template N *Split(N *node); 76 | 77 | template 78 | bool CoalesceOrRedistribute(N *node, Transaction *transaction = nullptr); 79 | 80 | template 81 | bool Coalesce( 82 | N *&neighbor_node, N *&node, 83 | BPlusTreeInternalPage *&parent, 84 | int index, Transaction *transaction = nullptr); 85 | 86 | template void Redistribute(N *neighbor_node, N *node, int index); 87 | 88 | bool AdjustRoot(BPlusTreePage *node); 89 | 90 | void UpdateRootPageId(int insert_record = false); 91 | 92 | // member variable 93 | std::string index_name_; 94 | page_id_t root_page_id_; 95 | BufferPoolManager *buffer_pool_manager_; 96 | KeyComparator comparator_; 97 | }; 98 | 99 | } // namespace cmudb 100 | -------------------------------------------------------------------------------- /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 | 8 | namespace cmudb { 9 | 10 | #define INDEXITERATOR_TYPE \ 11 | IndexIterator 12 | 13 | INDEX_TEMPLATE_ARGUMENTS 14 | class IndexIterator { 15 | public: 16 | // you may define your own constructor based on your member variables 17 | IndexIterator(); 18 | ~IndexIterator(); 19 | 20 | bool isEnd(); 21 | 22 | const MappingType &operator*(); 23 | 24 | IndexIterator &operator++(); 25 | 26 | private: 27 | // add your own private member variables here 28 | }; 29 | 30 | } // namespace cmudb 31 | -------------------------------------------------------------------------------- /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 | namespace cmudb { 28 | #define B_PLUS_TREE_LEAF_PAGE_TYPE \ 29 | BPlusTreeLeafPage 30 | 31 | INDEX_TEMPLATE_ARGUMENTS 32 | class BPlusTreeLeafPage : public BPlusTreePage { 33 | 34 | public: 35 | // After creating a new leaf page from buffer pool, must call initialize 36 | // method to set default values 37 | void Init(page_id_t page_id, page_id_t parent_id = INVALID_PAGE_ID); 38 | // helper methods 39 | page_id_t GetNextPageId() const; 40 | void SetNextPageId(page_id_t next_page_id); 41 | KeyType KeyAt(int index) const; 42 | int KeyIndex(const KeyType &key, const KeyComparator &comparator) const; 43 | const MappingType &GetItem(int index); 44 | 45 | // insert and delete methods 46 | int Insert(const KeyType &key, const ValueType &value, 47 | const KeyComparator &comparator); 48 | bool Lookup(const KeyType &key, ValueType &value, 49 | const KeyComparator &comparator) const; 50 | int RemoveAndDeleteRecord(const KeyType &key, 51 | const KeyComparator &comparator); 52 | // Split and Merge utility methods 53 | void MoveHalfTo(BPlusTreeLeafPage *recipient, 54 | BufferPoolManager *buffer_pool_manager /* Unused */); 55 | void MoveAllTo(BPlusTreeLeafPage *recipient, int /* Unused */, 56 | BufferPoolManager * /* Unused */); 57 | void MoveFirstToEndOf(BPlusTreeLeafPage *recipient, 58 | BufferPoolManager *buffer_pool_manager); 59 | void MoveLastToFrontOf(BPlusTreeLeafPage *recipient, int parentIndex, 60 | BufferPoolManager *buffer_pool_manager); 61 | // Debug 62 | std::string ToString(bool verbose = false) const; 63 | 64 | private: 65 | void CopyHalfFrom(MappingType *items, int size); 66 | void CopyAllFrom(MappingType *items, int size); 67 | void CopyLastFrom(const MappingType &item); 68 | void CopyFirstFrom(const MappingType &item, int parentIndex, 69 | BufferPoolManager *buffer_pool_manager); 70 | page_id_t next_page_id_; 71 | MappingType array[0]; 72 | }; 73 | } // namespace cmudb 74 | -------------------------------------------------------------------------------- /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) | CurrentSize (4) | MaxSize (4) | ParentPageId (4) | PageId(4) 12 | * ---------------------------------------------------------------------------- 13 | */ 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "buffer/buffer_pool_manager.h" 23 | #include "index/generic_key.h" 24 | 25 | namespace cmudb { 26 | 27 | #define MappingType std::pair 28 | 29 | #define INDEX_TEMPLATE_ARGUMENTS \ 30 | template 31 | 32 | // define page type enum 33 | enum class IndexPageType { INVALID_INDEX_PAGE = 0, LEAF_PAGE, INTERNAL_PAGE }; 34 | 35 | // Abstract class. 36 | class BPlusTreePage { 37 | public: 38 | bool IsLeafPage() const; 39 | bool IsRootPage() const; 40 | void SetPageType(IndexPageType page_type); 41 | 42 | int GetSize() const; 43 | void SetSize(int size); 44 | void IncreaseSize(int amount); 45 | 46 | int GetMaxSize() const; 47 | void SetMaxSize(int max_size); 48 | int GetMinSize() const; 49 | 50 | page_id_t GetParentPageId() const; 51 | void SetParentPageId(page_id_t parent_page_id); 52 | 53 | page_id_t GetPageId() const; 54 | void SetPageId(page_id_t page_id); 55 | 56 | private: 57 | // member variable, attributes that both internal and leaf page share 58 | IndexPageType page_type_; 59 | int size_; 60 | int max_size_; 61 | page_id_t parent_page_id_; 62 | page_id_t page_id_; 63 | }; 64 | 65 | } // namespace cmudb 66 | -------------------------------------------------------------------------------- /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 | private: 38 | // method used by buffer pool manager 39 | inline void ResetMemory() { memset(data_, 0, PAGE_SIZE); } 40 | // members 41 | char data_[PAGE_SIZE]; // actual data 42 | page_id_t page_id_ = INVALID_PAGE_ID; 43 | int pin_count_ = 0; 44 | bool is_dirty_ = false; 45 | RWMutex rwlatch_; 46 | }; 47 | 48 | } // namespace cmudb 49 | -------------------------------------------------------------------------------- /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) | 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 "page/page.h" 28 | #include "table/tuple.h" 29 | 30 | namespace cmudb { 31 | 32 | class TablePage : public Page { 33 | public: 34 | /** 35 | * Header related 36 | */ 37 | void Init(page_id_t page_id, size_t page_size, 38 | page_id_t prev_page_id = INVALID_PAGE_ID, 39 | page_id_t next_page_id = INVALID_PAGE_ID); 40 | page_id_t GetPageId(); 41 | 42 | page_id_t GetPrevPageId(); 43 | page_id_t GetNextPageId(); 44 | void SetPrevPageId(page_id_t prev_page_id); 45 | void SetNextPageId(page_id_t next_page_id); 46 | 47 | /** 48 | * Tuple related 49 | */ 50 | bool InsertTuple(const Tuple &tuple, RID &rid, Transaction *txn, 51 | LockManager *lock_manager); // return rid if success 52 | bool MarkDelete(const RID &rid, Transaction *txn, 53 | LockManager *lock_manager); // delete 54 | bool UpdateTuple(const Tuple &new_tuple, Tuple &old_tuple, const RID &rid, 55 | Transaction *txn, LockManager *lock_manager); 56 | 57 | // commit time 58 | void ApplyDelete(const RID &rid, Transaction *txn); // when commit success 59 | void RollbackDelete(const RID &rid, Transaction *txn); // when commit abort 60 | 61 | // return tuple (with data pointing to heap) if success 62 | bool GetTuple(const RID &rid, Tuple &tuple, Transaction *txn, 63 | LockManager *lock_manager); 64 | 65 | /** 66 | * Tuple iterator 67 | */ 68 | bool GetFirstTupleRid(RID &first_rid); 69 | bool GetNextTupleRid(const RID &cur_rid, RID &next_rid); 70 | 71 | private: 72 | /** 73 | * helper functions 74 | */ 75 | int32_t GetTupleOffset(int slot_num); 76 | int32_t GetTupleSize(int slot_num); 77 | void SetTupleOffset(int slot_num, int32_t offset); 78 | void SetTupleSize(int slot_num, int32_t offset); 79 | int32_t GetFreeSpacePointer(); // offset of the beginning of free space 80 | void SetFreeSpacePointer(int32_t free_space_pointer); 81 | int32_t GetTupleCount(); // Note that this tuple count may be larger than # of 82 | // actual tuples because some slots may be empty 83 | void SetTupleCount(int32_t tuple_count); 84 | int32_t GetFreeSpaceSize(); 85 | }; 86 | } // namespace cmudb 87 | -------------------------------------------------------------------------------- /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 "page/table_page.h" 11 | #include "table/table_iterator.h" 12 | #include "table/tuple.h" 13 | 14 | namespace cmudb { 15 | 16 | class TableHeap { 17 | friend class TableIterator; 18 | 19 | public: 20 | ~TableHeap() { 21 | // when destruct table heap, flush all pages within buffer pool 22 | buffer_pool_manager_->FlushAllPages(); 23 | } 24 | 25 | // open/create a table heap, create table if first_page_id is not passed 26 | TableHeap(BufferPoolManager *buffer_pool_manager, LockManager *lock_manager, 27 | page_id_t first_page_id = INVALID_PAGE_ID); 28 | 29 | // for insert, if tuple is too large (>~page_size), return false 30 | bool InsertTuple(const Tuple &tuple, RID &rid, Transaction *txn); 31 | 32 | bool MarkDelete(const RID &rid, Transaction *txn); // for delete 33 | 34 | // if the new tuple is too large to fit in the old page, return false (will 35 | // delete and insert) 36 | bool UpdateTuple(const Tuple &tuple, const RID &rid, Transaction *txn); 37 | 38 | // commit/abort time 39 | void ApplyDelete(const RID &rid, 40 | Transaction *txn); // when commit delete or rollback insert 41 | void RollbackDelete(const RID &rid, Transaction *txn); // when rollback delete 42 | 43 | bool GetTuple(const RID &rid, Tuple &tuple, Transaction *txn); 44 | 45 | bool DeleteTableHeap(); 46 | 47 | TableIterator begin(Transaction *txn); 48 | 49 | TableIterator end(); 50 | 51 | inline page_id_t GetFirstPageId() const { return first_page_id_; } 52 | 53 | private: 54 | /** 55 | * Members 56 | */ 57 | BufferPoolManager *buffer_pool_manager_; 58 | LockManager *lock_manager_; 59 | page_id_t first_page_id_; 60 | }; 61 | 62 | } // namespace cmudb 63 | -------------------------------------------------------------------------------- /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 | // constructor for table heap tuple 27 | Tuple(RID rid) : allocated_(false), rid_(rid) {} 28 | 29 | // constructor for creating a new tuple based on input value 30 | Tuple(std::vector values, Schema *schema); 31 | 32 | // copy constructor, deep copy 33 | Tuple(const Tuple &other); 34 | 35 | ~Tuple() { 36 | if (allocated_) 37 | delete[] data_; 38 | } 39 | 40 | // return RID of current tuple 41 | inline RID GetRid() const { return rid_; } 42 | 43 | // Get the address of this tuple in the table's backing store 44 | inline char *GetData() const { return data_; } 45 | 46 | // Get length of the tuple, including varchar legth 47 | inline int32_t GetLength() const { return size_; } 48 | 49 | // Get the value of a specified column (const) 50 | // checks the schema to see how to return the Value. 51 | Value GetValue(Schema *schema, const int column_id) const; 52 | 53 | // Is the column value null ? 54 | inline bool IsNull(Schema *schema, const int column_id) const { 55 | Value value = GetValue(schema, column_id); 56 | return value.IsNull(); 57 | } 58 | inline bool IsAllocated() { return allocated_; } 59 | 60 | std::string ToString(Schema *schema) const; 61 | 62 | private: 63 | // Get the starting storage address of specific column 64 | const char *GetDataPtr(Schema *schema, const int column_id) const; 65 | 66 | bool allocated_; // is allocated? 67 | RID rid_; // if pointing to the table heap, the rid is valid 68 | int32_t size_; 69 | char *data_; 70 | }; 71 | 72 | } // namespace cmudb 73 | -------------------------------------------------------------------------------- /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/include/vtable/virtual_table.h: -------------------------------------------------------------------------------- 1 | /** 2 | * virtual_table.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "buffer/lru_replacer.h" 8 | #include "catalog/schema.h" 9 | #include "concurrency/transaction_manager.h" 10 | #include "index/b_plus_tree_index.h" 11 | #include "sqlite/sqlite3ext.h" 12 | #include "table/table_heap.h" 13 | #include "table/tuple.h" 14 | #include "type/value.h" 15 | 16 | namespace cmudb { 17 | /* Helpers */ 18 | Schema *ParseCreateStatement(const std::string &sql); 19 | 20 | IndexMetadata *ParseIndexStatement(std::string &sql, 21 | const std::string &table_name, 22 | Schema *schema); 23 | 24 | Tuple ConstructTuple(Schema *schema, sqlite3_value **argv); 25 | 26 | Index *ConstructIndex(IndexMetadata *metadata, 27 | BufferPoolManager *buffer_pool_manager, 28 | page_id_t root_id = INVALID_PAGE_ID); 29 | Transaction *GetTransaction(); 30 | 31 | /* API declaration */ 32 | int VtabCreate(sqlite3 *db, void *pAux, int argc, const char *const *argv, 33 | sqlite3_vtab **ppVtab, char **pzErr); 34 | 35 | int VtabConnect(sqlite3 *db, void *pAux, int argc, const char *const *argv, 36 | sqlite3_vtab **ppVtab, char **pzErr); 37 | 38 | int VtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo); 39 | 40 | int VtabDisconnect(sqlite3_vtab *pVtab); 41 | 42 | int VtabOpen(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor); 43 | 44 | int VtabClose(sqlite3_vtab_cursor *cur); 45 | 46 | int VtabFilter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, 47 | int argc, sqlite3_value **argv); 48 | 49 | int VtabNext(sqlite3_vtab_cursor *cur); 50 | 51 | int VtabEof(sqlite3_vtab_cursor *cur); 52 | 53 | int VtabColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i); 54 | 55 | int VtabRowid(sqlite3_vtab_cursor *cur, sqlite3_int64 *pRowid); 56 | 57 | int VtabCommit(sqlite3_vtab *pVTab); 58 | 59 | int VtabBegin(sqlite3_vtab *pVTab); 60 | 61 | // global parameters 62 | struct GlobalParameters { 63 | BufferPoolManager *buffer_pool_manager_; 64 | LockManager *lock_manager_; 65 | TransactionManager *transaction_manager_; 66 | // global transaction, sqlite does not support concurrent transaction 67 | Transaction *transaction_; 68 | }; 69 | 70 | GlobalParameters *global_parameters; 71 | 72 | class VirtualTable { 73 | friend class Cursor; 74 | 75 | public: 76 | VirtualTable(Schema *schema, BufferPoolManager *buffer_pool_manager, 77 | LockManager *lock_manager, Index *index, 78 | page_id_t first_page_id = INVALID_PAGE_ID) 79 | : schema_(schema), index_(index) { 80 | table_heap_ = 81 | new TableHeap(buffer_pool_manager, lock_manager, first_page_id); 82 | } 83 | 84 | ~VirtualTable() { 85 | delete schema_; 86 | delete table_heap_; 87 | delete index_; 88 | } 89 | 90 | // insert into table heap 91 | inline bool InsertTuple(const Tuple &tuple, RID &rid) { 92 | return table_heap_->InsertTuple(tuple, rid, GetTransaction()); 93 | } 94 | 95 | // insert into index 96 | inline void InsertEntry(const Tuple &tuple, const RID &rid) { 97 | if (index_ == nullptr) 98 | return; 99 | // construct indexed key tuple 100 | std::vector key_values; 101 | 102 | for (auto &i : index_->GetKeyAttrs()) 103 | key_values.push_back(tuple.GetValue(schema_, i)); 104 | Tuple key(key_values, index_->GetKeySchema()); 105 | index_->InsertEntry(key, rid, GetTransaction()); 106 | } 107 | 108 | // delete from table heap 109 | // TODO: call makrdelete method from heaptable 110 | inline bool DeleteTuple(const RID &rid) { 111 | return table_heap_->MarkDelete(rid, GetTransaction()); 112 | } 113 | 114 | // delete from index 115 | inline void DeleteEntry(const RID &rid) { 116 | if (index_ == nullptr) 117 | return; 118 | Tuple deleted_tuple(rid); 119 | table_heap_->GetTuple(rid, deleted_tuple, GetTransaction()); 120 | // construct indexed key tuple 121 | std::vector key_values; 122 | 123 | for (auto &i : index_->GetKeyAttrs()) 124 | key_values.push_back(deleted_tuple.GetValue(schema_, i)); 125 | Tuple key(key_values, index_->GetKeySchema()); 126 | index_->DeleteEntry(key, GetTransaction()); 127 | } 128 | 129 | // update table heap tuple 130 | inline bool UpdateTuple(const Tuple &tuple, const RID &rid) { 131 | // if failed try to delete and insert 132 | return table_heap_->UpdateTuple(tuple, rid, GetTransaction()); 133 | } 134 | 135 | inline TableIterator begin() { return table_heap_->begin(GetTransaction()); } 136 | 137 | inline TableIterator end() { return table_heap_->end(); } 138 | 139 | inline Schema *GetSchema() { return schema_; } 140 | 141 | inline Index *GetIndex() { return index_; } 142 | 143 | inline TableHeap *GetTableHeap() { return table_heap_; } 144 | 145 | inline page_id_t GetFirstPageId() { return table_heap_->GetFirstPageId(); } 146 | 147 | private: 148 | sqlite3_vtab base_; 149 | // virtual table schema 150 | Schema *schema_; 151 | // to read/write actual data in table 152 | TableHeap *table_heap_; 153 | // to insert/delete index entry 154 | Index *index_ = nullptr; 155 | }; 156 | 157 | class Cursor { 158 | public: 159 | Cursor(VirtualTable *virtual_table) 160 | : table_iterator_(virtual_table->begin()), virtual_table_(virtual_table) { 161 | } 162 | 163 | inline void SetScanFlag(bool is_index_scan) { 164 | is_index_scan_ = is_index_scan; 165 | } 166 | 167 | inline bool IsIndexScan() { return is_index_scan_; } 168 | 169 | inline VirtualTable *GetVirtualTable() { return virtual_table_; } 170 | 171 | inline Schema *GetKeySchema() { 172 | return virtual_table_->index_->GetKeySchema(); 173 | } 174 | // return rid at which cursor is currently pointed 175 | inline int64_t GetCurrentRid() { 176 | if (is_index_scan_) 177 | return results[offset_].Get(); 178 | else 179 | return (*table_iterator_).GetRid().Get(); 180 | } 181 | 182 | // return tuple at which cursor is currently pointed 183 | inline Value GetCurrentValue(Schema *schema, int column) { 184 | if (is_index_scan_) { 185 | RID rid = results[offset_]; 186 | Tuple tuple(rid); 187 | virtual_table_->table_heap_->GetTuple(rid, tuple, GetTransaction()); 188 | return tuple.GetValue(schema, column); 189 | } else { 190 | return table_iterator_->GetValue(schema, column); 191 | } 192 | } 193 | 194 | // move cursor up to next 195 | Cursor &operator++() { 196 | if (is_index_scan_) 197 | ++offset_; 198 | else 199 | ++table_iterator_; 200 | return *this; 201 | } 202 | // is end of cursor(no more tuple) 203 | inline bool isEof() { 204 | if (is_index_scan_) 205 | return offset_ == static_cast(results.size()); 206 | else 207 | return table_iterator_ == virtual_table_->end(); 208 | } 209 | 210 | // wrapper around poit scan methods 211 | inline void ScanKey(const Tuple &key) { 212 | virtual_table_->index_->ScanKey(key, results); 213 | } 214 | 215 | private: 216 | sqlite3_vtab_cursor base_; /* Base class - must be first */ 217 | // for index scan 218 | std::vector results; 219 | int offset_ = 0; 220 | // for sequential scan 221 | TableIterator table_iterator_; 222 | // flag to indicate which scan method is currently used 223 | bool is_index_scan_ = false; 224 | VirtualTable *virtual_table_; 225 | }; // namespace cmudb 226 | 227 | } // namespace cmudb 228 | -------------------------------------------------------------------------------- /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() {} 16 | 17 | INDEX_TEMPLATE_ARGUMENTS 18 | INDEXITERATOR_TYPE::~IndexIterator() {} 19 | 20 | template class IndexIterator, RID, GenericComparator<4>>; 21 | template class IndexIterator, RID, GenericComparator<8>>; 22 | template class IndexIterator, RID, GenericComparator<16>>; 23 | template class IndexIterator, RID, GenericComparator<32>>; 24 | template class IndexIterator, RID, GenericComparator<64>>; 25 | 26 | } // namespace cmudb 27 | -------------------------------------------------------------------------------- /src/page/b_plus_tree_internal_page.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_internal_page.cpp 3 | */ 4 | #include 5 | #include 6 | 7 | #include "common/exception.h" 8 | #include "page/b_plus_tree_internal_page.h" 9 | 10 | namespace cmudb { 11 | /***************************************************************************** 12 | * HELPER METHODS AND UTILITIES 13 | *****************************************************************************/ 14 | /* 15 | * Init method after creating a new internal page 16 | * Including set page type, set current size, set page id, set parent id and set 17 | * max page size 18 | */ 19 | INDEX_TEMPLATE_ARGUMENTS 20 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::Init(page_id_t page_id, 21 | page_id_t parent_id) {} 22 | /* 23 | * Helper method to get/set the key associated with input "index"(a.k.a 24 | * array offset) 25 | */ 26 | INDEX_TEMPLATE_ARGUMENTS 27 | KeyType B_PLUS_TREE_INTERNAL_PAGE_TYPE::KeyAt(int index) const { 28 | // replace with your own code 29 | KeyType key; 30 | return key; 31 | } 32 | 33 | INDEX_TEMPLATE_ARGUMENTS 34 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::SetKeyAt(int index, const KeyType &key) {} 35 | 36 | /* 37 | * Helper method to find and return array index(or offset), so that its value 38 | * equals to input "value" 39 | */ 40 | INDEX_TEMPLATE_ARGUMENTS 41 | int B_PLUS_TREE_INTERNAL_PAGE_TYPE::ValueIndex(const ValueType &value) const { 42 | return 0; 43 | } 44 | 45 | /* 46 | * Helper method to get the value associated with input "index"(a.k.a array 47 | * offset) 48 | */ 49 | INDEX_TEMPLATE_ARGUMENTS 50 | ValueType B_PLUS_TREE_INTERNAL_PAGE_TYPE::ValueAt(int index) const { return 0; } 51 | 52 | /***************************************************************************** 53 | * LOOKUP 54 | *****************************************************************************/ 55 | /* 56 | * Find and return the child pointer(page_id) which points to the child page 57 | * that contains input "key" 58 | * Start the search from the second key(the first key should always be invalid) 59 | */ 60 | INDEX_TEMPLATE_ARGUMENTS 61 | ValueType 62 | B_PLUS_TREE_INTERNAL_PAGE_TYPE::Lookup(const KeyType &key, 63 | const KeyComparator &comparator) const { 64 | return INVALID_PAGE_ID; 65 | } 66 | 67 | /***************************************************************************** 68 | * INSERTION 69 | *****************************************************************************/ 70 | /* 71 | * Populate new root page with old_value + new_key & new_value 72 | * When the insertion cause overflow from leaf page all the way upto the root 73 | * page, you should create a new root page and populate its elements. 74 | * NOTE: This method is only called within InsertIntoParent()(b_plus_tree.cpp) 75 | */ 76 | INDEX_TEMPLATE_ARGUMENTS 77 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::PopulateNewRoot( 78 | const ValueType &old_value, const KeyType &new_key, 79 | const ValueType &new_value) {} 80 | /* 81 | * Insert new_key & new_value pair right after the pair with its value == 82 | * old_value 83 | * @return: new size after insertion 84 | */ 85 | INDEX_TEMPLATE_ARGUMENTS 86 | int B_PLUS_TREE_INTERNAL_PAGE_TYPE::InsertNodeAfter( 87 | const ValueType &old_value, const KeyType &new_key, 88 | const ValueType &new_value) { 89 | return 0; 90 | } 91 | 92 | /***************************************************************************** 93 | * SPLIT 94 | *****************************************************************************/ 95 | /* 96 | * Remove half of key & value pairs from this page to "recipient" page 97 | */ 98 | INDEX_TEMPLATE_ARGUMENTS 99 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::MoveHalfTo( 100 | BPlusTreeInternalPage *recipient, 101 | BufferPoolManager *buffer_pool_manager) {} 102 | 103 | INDEX_TEMPLATE_ARGUMENTS 104 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::CopyHalfFrom( 105 | MappingType *items, int size, BufferPoolManager *buffer_pool_manager) {} 106 | 107 | /***************************************************************************** 108 | * REMOVE 109 | *****************************************************************************/ 110 | /* 111 | * Remove the key & value pair in internal page according to input index(a.k.a 112 | * array offset) 113 | * NOTE: store key&value pair continuously after deletion 114 | */ 115 | INDEX_TEMPLATE_ARGUMENTS 116 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::Remove(int index) {} 117 | 118 | /* 119 | * Remove the only key & value pair in internal page and return the value 120 | * NOTE: only call this method within AdjustRoot()(in b_plus_tree.cpp) 121 | */ 122 | INDEX_TEMPLATE_ARGUMENTS 123 | ValueType B_PLUS_TREE_INTERNAL_PAGE_TYPE::RemoveAndReturnOnlyChild() { 124 | return INVALID_PAGE_ID; 125 | } 126 | /***************************************************************************** 127 | * MERGE 128 | *****************************************************************************/ 129 | /* 130 | * Remove all of key & value pairs from this page to "recipient" page, then 131 | * update relavent key & value pair in its parent page. 132 | */ 133 | INDEX_TEMPLATE_ARGUMENTS 134 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::MoveAllTo( 135 | BPlusTreeInternalPage *recipient, int index_in_parent, 136 | BufferPoolManager *buffer_pool_manager) {} 137 | 138 | INDEX_TEMPLATE_ARGUMENTS 139 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::CopyAllFrom( 140 | MappingType *items, int size, BufferPoolManager *buffer_pool_manager) {} 141 | 142 | /***************************************************************************** 143 | * REDISTRIBUTE 144 | *****************************************************************************/ 145 | /* 146 | * Remove the first key & value pair from this page to tail of "recipient" 147 | * page, then update relavent key & value pair in its parent page. 148 | */ 149 | INDEX_TEMPLATE_ARGUMENTS 150 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::MoveFirstToEndOf( 151 | BPlusTreeInternalPage *recipient, 152 | BufferPoolManager *buffer_pool_manager) {} 153 | 154 | INDEX_TEMPLATE_ARGUMENTS 155 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::CopyLastFrom( 156 | const MappingType &pair, BufferPoolManager *buffer_pool_manager) {} 157 | 158 | /* 159 | * Remove the last key & value pair from this page to head of "recipient" 160 | * page, then update relavent key & value pair in its parent page. 161 | */ 162 | INDEX_TEMPLATE_ARGUMENTS 163 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::MoveLastToFrontOf( 164 | BPlusTreeInternalPage *recipient, int parent_index, 165 | BufferPoolManager *buffer_pool_manager) {} 166 | 167 | INDEX_TEMPLATE_ARGUMENTS 168 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::CopyFirstFrom( 169 | const MappingType &pair, int parent_index, 170 | BufferPoolManager *buffer_pool_manager) {} 171 | 172 | /***************************************************************************** 173 | * DEBUG 174 | *****************************************************************************/ 175 | INDEX_TEMPLATE_ARGUMENTS 176 | void B_PLUS_TREE_INTERNAL_PAGE_TYPE::QueueUpChildren( 177 | std::queue *queue, 178 | BufferPoolManager *buffer_pool_manager) { 179 | for (int i = 0; i < GetSize(); i++) { 180 | auto *page = buffer_pool_manager->FetchPage(array[i].second); 181 | if (page == nullptr) 182 | throw Exception(EXCEPTION_TYPE_INDEX, 183 | "all page are pinned while printing"); 184 | BPlusTreePage *node = 185 | reinterpret_cast(page->GetData()); 186 | queue->push(node); 187 | } 188 | } 189 | 190 | INDEX_TEMPLATE_ARGUMENTS 191 | std::string B_PLUS_TREE_INTERNAL_PAGE_TYPE::ToString(bool verbose) const { 192 | if (GetSize() == 0) { 193 | return ""; 194 | } 195 | std::ostringstream os; 196 | if (verbose) { 197 | os << "[pageId: " << GetPageId() << " parentId: " << GetParentPageId() 198 | << "]<" << GetSize() << "> "; 199 | } 200 | 201 | int entry = verbose ? 0 : 1; 202 | int end = GetSize(); 203 | bool first = true; 204 | while (entry < end) { 205 | if (first) { 206 | first = false; 207 | } else { 208 | os << " "; 209 | } 210 | os << std::dec << array[entry].first.ToString(); 211 | if (verbose) { 212 | os << "(" << array[entry].second << ")"; 213 | } 214 | ++entry; 215 | } 216 | return os.str(); 217 | } 218 | 219 | // valuetype for internalNode should be page id_t 220 | template class BPlusTreeInternalPage, page_id_t, 221 | GenericComparator<4>>; 222 | template class BPlusTreeInternalPage, page_id_t, 223 | GenericComparator<8>>; 224 | template class BPlusTreeInternalPage, page_id_t, 225 | GenericComparator<16>>; 226 | template class BPlusTreeInternalPage, page_id_t, 227 | GenericComparator<32>>; 228 | template class BPlusTreeInternalPage, page_id_t, 229 | GenericComparator<64>>; 230 | } // namespace cmudb 231 | -------------------------------------------------------------------------------- /src/page/b_plus_tree_leaf_page.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * b_plus_tree_leaf_page.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "common/exception.h" 8 | #include "common/rid.h" 9 | #include "page/b_plus_tree_leaf_page.h" 10 | 11 | namespace cmudb { 12 | 13 | /***************************************************************************** 14 | * HELPER METHODS AND UTILITIES 15 | *****************************************************************************/ 16 | 17 | /** 18 | * Init method after creating a new leaf page 19 | * Including set page type, set current size to zero, set page id/parent id, set 20 | * next page id and set max size 21 | */ 22 | INDEX_TEMPLATE_ARGUMENTS 23 | void B_PLUS_TREE_LEAF_PAGE_TYPE::Init(page_id_t page_id, page_id_t parent_id) {} 24 | 25 | /** 26 | * Helper methods to set/get next page id 27 | */ 28 | INDEX_TEMPLATE_ARGUMENTS 29 | page_id_t B_PLUS_TREE_LEAF_PAGE_TYPE::GetNextPageId() const { 30 | return INVALID_PAGE_ID; 31 | } 32 | 33 | INDEX_TEMPLATE_ARGUMENTS 34 | void B_PLUS_TREE_LEAF_PAGE_TYPE::SetNextPageId(page_id_t next_page_id) {} 35 | 36 | /** 37 | * Helper method to find the first index i so that array[i].first >= key 38 | * NOTE: This method is only used when generating index iterator 39 | */ 40 | INDEX_TEMPLATE_ARGUMENTS 41 | int B_PLUS_TREE_LEAF_PAGE_TYPE::KeyIndex( 42 | const KeyType &key, const KeyComparator &comparator) const { 43 | return 0; 44 | } 45 | 46 | /* 47 | * Helper method to find and return the key associated with input "index"(a.k.a 48 | * array offset) 49 | */ 50 | INDEX_TEMPLATE_ARGUMENTS 51 | KeyType B_PLUS_TREE_LEAF_PAGE_TYPE::KeyAt(int index) const { 52 | // replace with your own code 53 | KeyType key; 54 | return key; 55 | } 56 | 57 | /* 58 | * Helper method to find and return the key & value pair associated with input 59 | * "index"(a.k.a array offset) 60 | */ 61 | INDEX_TEMPLATE_ARGUMENTS 62 | const MappingType &B_PLUS_TREE_LEAF_PAGE_TYPE::GetItem(int index) { 63 | // replace with your own code 64 | return array[0]; 65 | } 66 | 67 | /***************************************************************************** 68 | * INSERTION 69 | *****************************************************************************/ 70 | /* 71 | * Insert key & value pair into leaf page ordered by key 72 | * @return page size after insertion 73 | */ 74 | INDEX_TEMPLATE_ARGUMENTS 75 | int B_PLUS_TREE_LEAF_PAGE_TYPE::Insert(const KeyType &key, 76 | const ValueType &value, 77 | const KeyComparator &comparator) { 78 | return 0; 79 | } 80 | 81 | /***************************************************************************** 82 | * SPLIT 83 | *****************************************************************************/ 84 | /* 85 | * Remove half of key & value pairs from this page to "recipient" page 86 | */ 87 | INDEX_TEMPLATE_ARGUMENTS 88 | void B_PLUS_TREE_LEAF_PAGE_TYPE::MoveHalfTo( 89 | BPlusTreeLeafPage *recipient, 90 | __attribute__((unused)) BufferPoolManager *buffer_pool_manager) {} 91 | 92 | INDEX_TEMPLATE_ARGUMENTS 93 | void B_PLUS_TREE_LEAF_PAGE_TYPE::CopyHalfFrom(MappingType *items, int size) {} 94 | 95 | /***************************************************************************** 96 | * LOOKUP 97 | *****************************************************************************/ 98 | /* 99 | * For the given key, check to see whether it exists in the leaf page. If it 100 | * does, then store its corresponding value in input "value" and return true. 101 | * If the key does not exist, then return false 102 | */ 103 | INDEX_TEMPLATE_ARGUMENTS 104 | bool B_PLUS_TREE_LEAF_PAGE_TYPE::Lookup(const KeyType &key, ValueType &value, 105 | const KeyComparator &comparator) const { 106 | return false; 107 | } 108 | 109 | /***************************************************************************** 110 | * REMOVE 111 | *****************************************************************************/ 112 | /* 113 | * First look through leaf page to see whether delete key exist or not. If 114 | * exist, perform deletion, otherwise return immdiately. 115 | * NOTE: store key&value pair continuously after deletion 116 | * @return page size after deletion 117 | */ 118 | INDEX_TEMPLATE_ARGUMENTS 119 | int B_PLUS_TREE_LEAF_PAGE_TYPE::RemoveAndDeleteRecord( 120 | const KeyType &key, const KeyComparator &comparator) { 121 | return 0; 122 | } 123 | 124 | /***************************************************************************** 125 | * MERGE 126 | *****************************************************************************/ 127 | /* 128 | * Remove all of key & value pairs from this page to "recipient" page, then 129 | * update next page id 130 | */ 131 | INDEX_TEMPLATE_ARGUMENTS 132 | void B_PLUS_TREE_LEAF_PAGE_TYPE::MoveAllTo(BPlusTreeLeafPage *recipient, 133 | int, BufferPoolManager *) {} 134 | INDEX_TEMPLATE_ARGUMENTS 135 | void B_PLUS_TREE_LEAF_PAGE_TYPE::CopyAllFrom(MappingType *items, int size) {} 136 | 137 | /***************************************************************************** 138 | * REDISTRIBUTE 139 | *****************************************************************************/ 140 | /* 141 | * Remove the first key & value pair from this page to "recipient" page, then 142 | * update relavent key & value pair in its parent page. 143 | */ 144 | INDEX_TEMPLATE_ARGUMENTS 145 | void B_PLUS_TREE_LEAF_PAGE_TYPE::MoveFirstToEndOf( 146 | BPlusTreeLeafPage *recipient, 147 | BufferPoolManager *buffer_pool_manager) {} 148 | 149 | INDEX_TEMPLATE_ARGUMENTS 150 | void B_PLUS_TREE_LEAF_PAGE_TYPE::CopyLastFrom(const MappingType &item) {} 151 | /* 152 | * Remove the last key & value pair from this page to "recipient" page, then 153 | * update relavent key & value pair in its parent page. 154 | */ 155 | INDEX_TEMPLATE_ARGUMENTS 156 | void B_PLUS_TREE_LEAF_PAGE_TYPE::MoveLastToFrontOf( 157 | BPlusTreeLeafPage *recipient, int parentIndex, 158 | BufferPoolManager *buffer_pool_manager) {} 159 | 160 | INDEX_TEMPLATE_ARGUMENTS 161 | void B_PLUS_TREE_LEAF_PAGE_TYPE::CopyFirstFrom( 162 | const MappingType &item, int parentIndex, 163 | BufferPoolManager *buffer_pool_manager) {} 164 | 165 | /***************************************************************************** 166 | * DEBUG 167 | *****************************************************************************/ 168 | INDEX_TEMPLATE_ARGUMENTS 169 | std::string B_PLUS_TREE_LEAF_PAGE_TYPE::ToString(bool verbose) const { 170 | if (GetSize() == 0) { 171 | return ""; 172 | } 173 | std::ostringstream stream; 174 | if (verbose) { 175 | stream << "[pageId: " << GetPageId() << " parentId: " << GetParentPageId() 176 | << "]<" << GetSize() << "> "; 177 | } 178 | int entry = 0; 179 | int end = GetSize(); 180 | bool first = true; 181 | 182 | while (entry < end) { 183 | if (first) { 184 | first = false; 185 | } else { 186 | stream << " "; 187 | } 188 | stream << std::dec << array[entry].first; 189 | if (verbose) { 190 | stream << "(" << array[entry].second << ")"; 191 | } 192 | ++entry; 193 | } 194 | return stream.str(); 195 | } 196 | 197 | template class BPlusTreeLeafPage, RID, 198 | GenericComparator<4>>; 199 | template class BPlusTreeLeafPage, RID, 200 | GenericComparator<8>>; 201 | template class BPlusTreeLeafPage, RID, 202 | GenericComparator<16>>; 203 | template class BPlusTreeLeafPage, RID, 204 | GenericComparator<32>>; 205 | template class BPlusTreeLeafPage, RID, 206 | GenericComparator<64>>; 207 | } // namespace cmudb 208 | -------------------------------------------------------------------------------- /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 { return false; } 13 | bool BPlusTreePage::IsRootPage() const { return false; } 14 | void BPlusTreePage::SetPageType(IndexPageType page_type) {} 15 | 16 | /* 17 | * Helper methods to get/set size (number of key/value pairs stored in that 18 | * page) 19 | */ 20 | int BPlusTreePage::GetSize() const { return 0; } 21 | void BPlusTreePage::SetSize(int size) {} 22 | void BPlusTreePage::IncreaseSize(int amount) {} 23 | 24 | /* 25 | * Helper methods to get/set max size (capacity) of the page 26 | */ 27 | int BPlusTreePage::GetMaxSize() const { return 0; } 28 | void BPlusTreePage::SetMaxSize(int size) {} 29 | 30 | /* 31 | * Helper method to get min page size 32 | * Generally, min page size == max page size / 2 33 | */ 34 | int BPlusTreePage::GetMinSize() const { return 0; } 35 | 36 | /* 37 | * Helper methods to get/set parent page id 38 | */ 39 | page_id_t BPlusTreePage::GetParentPageId() const { return 0; } 40 | void BPlusTreePage::SetParentPageId(page_id_t parent_page_id) {} 41 | 42 | /* 43 | * Helper methods to get/set self page id 44 | */ 45 | page_id_t BPlusTreePage::GetPageId() const { return 0; } 46 | void BPlusTreePage::SetPageId(page_id_t page_id) {} 47 | 48 | } // namespace cmudb 49 | -------------------------------------------------------------------------------- /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 | TableHeap::TableHeap(BufferPoolManager *buffer_pool_manager, 13 | LockManager *lock_manager, page_id_t first_page_id) 14 | : buffer_pool_manager_(buffer_pool_manager), lock_manager_(lock_manager), 15 | first_page_id_(first_page_id) { 16 | if (first_page_id_ == INVALID_PAGE_ID) { 17 | auto first_page = 18 | static_cast(buffer_pool_manager_->NewPage(first_page_id_)); 19 | assert(first_page != nullptr); // todo: abort table creation? 20 | first_page->WLatch(); 21 | LOG_DEBUG("new table page created %d", first_page_id_); 22 | 23 | first_page->Init(first_page_id_, PAGE_SIZE); 24 | first_page->WUnlatch(); 25 | buffer_pool_manager_->UnpinPage(first_page_id_, true); 26 | } 27 | } 28 | 29 | bool TableHeap::InsertTuple(const Tuple &tuple, RID &rid, Transaction *txn) { 30 | if (tuple.size_ + 28 > PAGE_SIZE) { // larger than one page size 31 | txn->SetState(TransactionState::ABORTED); 32 | return false; 33 | } 34 | 35 | auto cur_page = 36 | static_cast(buffer_pool_manager_->FetchPage(first_page_id_)); 37 | if (cur_page == nullptr) { 38 | txn->SetState(TransactionState::ABORTED); 39 | return false; 40 | } 41 | 42 | cur_page->WLatch(); 43 | while (!cur_page->InsertTuple( 44 | tuple, rid, txn, 45 | lock_manager_)) { // fail to insert due to not enough space 46 | auto next_page_id = cur_page->GetNextPageId(); 47 | if (next_page_id != INVALID_PAGE_ID) { // valid next page 48 | cur_page->WUnlatch(); 49 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), false); 50 | cur_page = static_cast( 51 | buffer_pool_manager_->FetchPage(next_page_id)); 52 | cur_page->WLatch(); 53 | } else { // create new page 54 | auto new_page = 55 | static_cast(buffer_pool_manager_->NewPage(next_page_id)); 56 | if (new_page == nullptr) { 57 | cur_page->WUnlatch(); 58 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), false); 59 | txn->SetState(TransactionState::ABORTED); 60 | return false; 61 | } 62 | new_page->WLatch(); 63 | // std::cout << "new table page " << next_page_id << " created" << 64 | // std::endl; 65 | cur_page->SetNextPageId(next_page_id); 66 | new_page->Init(next_page_id, PAGE_SIZE, cur_page->GetPageId(), 67 | INVALID_PAGE_ID); 68 | cur_page->WUnlatch(); 69 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), true); 70 | cur_page = new_page; 71 | } 72 | } 73 | cur_page->WUnlatch(); 74 | buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), true); 75 | txn->GetWriteSet()->emplace_back(rid, WType::INSERT, Tuple{RID()}, this); 76 | return true; 77 | } 78 | 79 | bool TableHeap::MarkDelete(const RID &rid, Transaction *txn) { 80 | // todo: remove empty page 81 | auto page = reinterpret_cast( 82 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 83 | if (page == nullptr) { 84 | txn->SetState(TransactionState::ABORTED); 85 | return false; 86 | } 87 | page->WLatch(); 88 | page->MarkDelete(rid, txn, lock_manager_); 89 | page->WUnlatch(); 90 | buffer_pool_manager_->UnpinPage(page->GetPageId(), true); 91 | txn->GetWriteSet()->emplace_back(rid, WType::DELETE, Tuple{RID()}, this); 92 | return true; 93 | } 94 | 95 | bool TableHeap::UpdateTuple(const Tuple &tuple, const RID &rid, 96 | Transaction *txn) { 97 | auto page = reinterpret_cast( 98 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 99 | if (page == nullptr) { 100 | txn->SetState(TransactionState::ABORTED); 101 | return false; 102 | } 103 | Tuple old_tuple{RID()}; 104 | page->WLatch(); 105 | bool is_updated = 106 | page->UpdateTuple(tuple, old_tuple, rid, txn, lock_manager_); 107 | page->WUnlatch(); 108 | buffer_pool_manager_->UnpinPage(page->GetPageId(), is_updated); 109 | if (is_updated) 110 | txn->GetWriteSet()->emplace_back(rid, WType::UPDATE, old_tuple, this); 111 | return is_updated; 112 | } 113 | 114 | void TableHeap::ApplyDelete(const RID &rid, Transaction *txn) { 115 | auto page = reinterpret_cast( 116 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 117 | assert(page != nullptr); 118 | page->WLatch(); 119 | page->ApplyDelete(rid, txn); 120 | lock_manager_->Unlock(txn, rid); 121 | page->WUnlatch(); 122 | buffer_pool_manager_->UnpinPage(page->GetPageId(), true); 123 | } 124 | 125 | void TableHeap::RollbackDelete(const RID &rid, Transaction *txn) { 126 | auto page = reinterpret_cast( 127 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 128 | assert(page != nullptr); 129 | page->WLatch(); 130 | page->RollbackDelete(rid, txn); 131 | page->WUnlatch(); 132 | buffer_pool_manager_->UnpinPage(page->GetPageId(), true); 133 | } 134 | 135 | // called by tuple iterator 136 | bool TableHeap::GetTuple(const RID &rid, Tuple &tuple, Transaction *txn) { 137 | auto page = static_cast( 138 | buffer_pool_manager_->FetchPage(rid.GetPageId())); 139 | if (page == nullptr) { 140 | txn->SetState(TransactionState::ABORTED); 141 | return false; 142 | } 143 | page->RLatch(); 144 | bool res = page->GetTuple(rid, tuple, txn, lock_manager_); 145 | page->RUnlatch(); 146 | buffer_pool_manager_->UnpinPage(rid.GetPageId(), false); 147 | return res; 148 | } 149 | 150 | bool TableHeap::DeleteTableHeap() { 151 | // todo: real delete 152 | return true; 153 | } 154 | 155 | TableIterator TableHeap::begin(Transaction *txn) { 156 | auto page = 157 | static_cast(buffer_pool_manager_->FetchPage(first_page_id_)); 158 | page->RLatch(); 159 | RID rid; 160 | // if failed (no tuple), rid will be the result of default 161 | // constructor, which means eof 162 | page->GetFirstTupleRid(rid); 163 | page->RUnlatch(); 164 | buffer_pool_manager_->UnpinPage(first_page_id_, false); 165 | return TableIterator(this, rid, txn); 166 | } 167 | 168 | TableIterator TableHeap::end() { 169 | return TableIterator(this, RID(INVALID_PAGE_ID, -1), nullptr); 170 | } 171 | 172 | } // namespace cmudb 173 | -------------------------------------------------------------------------------- /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 | // Get the value of a specified column (const) 56 | Value Tuple::GetValue(Schema *schema, const int column_id) const { 57 | assert(schema); 58 | assert(data_); 59 | const TypeId column_type = schema->GetType(column_id); 60 | const char *data_ptr = GetDataPtr(schema, column_id); 61 | // the third parameter "is_inlined" is unused 62 | return Value::DeserializeFrom(data_ptr, column_type); 63 | } 64 | 65 | const char *Tuple::GetDataPtr(Schema *schema, const int column_id) const { 66 | assert(schema); 67 | assert(data_); 68 | bool is_inlined = schema->IsInlined(column_id); 69 | // for inline type, data are stored where they are 70 | if (is_inlined) 71 | return (data_ + schema->GetOffset(column_id)); 72 | else { 73 | // step1: read relative offset from tuple data 74 | int32_t offset = 75 | *reinterpret_cast(data_ + schema->GetOffset(column_id)); 76 | // step 2: return beginning address of the real data for VARCHAR type 77 | return (data_ + offset); 78 | } 79 | } 80 | 81 | std::string Tuple::ToString(Schema *schema) const { 82 | std::stringstream os; 83 | 84 | int column_count = schema->GetColumnCount(); 85 | bool first = true; 86 | os << "("; 87 | for (int column_itr = 0; column_itr < column_count; column_itr++) { 88 | if (first) { 89 | first = false; 90 | } else { 91 | os << ", "; 92 | } 93 | if (IsNull(schema, column_itr)) { 94 | os << ""; 95 | } else { 96 | Value val = (GetValue(schema, column_itr)); 97 | os << val.ToString(); 98 | } 99 | } 100 | os << ")"; 101 | os << " Tuple size is " << size_; 102 | 103 | return os.str(); 104 | } 105 | 106 | } // namespace cmudb 107 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/type/varlen_type.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * varlen_type.cpp 3 | */ 4 | #include 5 | 6 | #include "common/exception.h" 7 | #include "type/type_util.h" 8 | #include "type/varlen_type.h" 9 | 10 | namespace cmudb { 11 | #define VARLEN_COMPARE_FUNC(OP) \ 12 | const char *str1 = left.GetData(); \ 13 | uint32_t len1 = GetLength(left) - 1; \ 14 | const char *str2; \ 15 | uint32_t len2; \ 16 | if (right.GetTypeId() == TypeId::VARCHAR) { \ 17 | str2 = right.GetData(); \ 18 | len2 = GetLength(right) - 1; \ 19 | return GetCmpBool(TypeUtil::CompareStrings(str1, len1, str2, len2) OP 0); \ 20 | } else { \ 21 | auto r_value = right.CastAs(TypeId::VARCHAR); \ 22 | str2 = r_value.GetData(); \ 23 | len2 = GetLength(r_value) - 1; \ 24 | return GetCmpBool(TypeUtil::CompareStrings(str1, len1, str2, len2) OP 0); \ 25 | } 26 | 27 | VarlenType::VarlenType(TypeId type) : Type(type) {} 28 | 29 | VarlenType::~VarlenType() {} 30 | 31 | // Access the raw variable length data 32 | const char *VarlenType::GetData(const Value &val) const { 33 | return val.value_.varlen; 34 | } 35 | 36 | // Get the length of the variable length data (including the length field) 37 | uint32_t VarlenType::GetLength(const Value &val) const { return val.size_.len; } 38 | 39 | CmpBool VarlenType::CompareEquals(const Value &left, const Value &right) const { 40 | assert(left.CheckComparable(right)); 41 | if (left.IsNull() || right.IsNull()) 42 | return CMP_NULL; 43 | if (GetLength(left) == PELOTON_VARCHAR_MAX_LEN || 44 | GetLength(right) == PELOTON_VARCHAR_MAX_LEN) { 45 | return GetCmpBool(GetLength(left) == GetLength(right)); 46 | } 47 | 48 | VARLEN_COMPARE_FUNC(==); 49 | } 50 | 51 | CmpBool VarlenType::CompareNotEquals(const Value &left, 52 | const Value &right) const { 53 | assert(left.CheckComparable(right)); 54 | if (left.IsNull() || right.IsNull()) 55 | return CMP_NULL; 56 | if (GetLength(left) == PELOTON_VARCHAR_MAX_LEN || 57 | GetLength(right) == PELOTON_VARCHAR_MAX_LEN) { 58 | return GetCmpBool(GetLength(left) != GetLength(right)); 59 | } 60 | 61 | VARLEN_COMPARE_FUNC(!=); 62 | } 63 | 64 | CmpBool VarlenType::CompareLessThan(const Value &left, 65 | const Value &right) const { 66 | assert(left.CheckComparable(right)); 67 | if (left.IsNull() || right.IsNull()) 68 | return CMP_NULL; 69 | if (GetLength(left) == PELOTON_VARCHAR_MAX_LEN || 70 | GetLength(right) == PELOTON_VARCHAR_MAX_LEN) { 71 | return GetCmpBool(GetLength(left) < GetLength(right)); 72 | } 73 | 74 | VARLEN_COMPARE_FUNC(<); 75 | } 76 | 77 | CmpBool VarlenType::CompareLessThanEquals(const Value &left, 78 | const Value &right) const { 79 | assert(left.CheckComparable(right)); 80 | if (left.IsNull() || right.IsNull()) 81 | return CMP_NULL; 82 | if (GetLength(left) == PELOTON_VARCHAR_MAX_LEN || 83 | GetLength(right) == PELOTON_VARCHAR_MAX_LEN) { 84 | return GetCmpBool(GetLength(left) <= GetLength(right)); 85 | } 86 | 87 | VARLEN_COMPARE_FUNC(<=); 88 | } 89 | 90 | CmpBool VarlenType::CompareGreaterThan(const Value &left, 91 | const Value &right) const { 92 | assert(left.CheckComparable(right)); 93 | if (left.IsNull() || right.IsNull()) 94 | return CMP_NULL; 95 | if (GetLength(left) == PELOTON_VARCHAR_MAX_LEN || 96 | GetLength(right) == PELOTON_VARCHAR_MAX_LEN) { 97 | return GetCmpBool(GetLength(left) > GetLength(right)); 98 | } 99 | 100 | VARLEN_COMPARE_FUNC(>); 101 | } 102 | 103 | CmpBool VarlenType::CompareGreaterThanEquals(const Value &left, 104 | const Value &right) const { 105 | assert(left.CheckComparable(right)); 106 | if (left.IsNull() || right.IsNull()) 107 | return CMP_NULL; 108 | if (GetLength(left) == PELOTON_VARCHAR_MAX_LEN || 109 | GetLength(right) == PELOTON_VARCHAR_MAX_LEN) { 110 | return GetCmpBool(GetLength(left) >= GetLength(right)); 111 | } 112 | 113 | VARLEN_COMPARE_FUNC(>=); 114 | } 115 | 116 | Value VarlenType::Min(const Value &left, const Value &right) const { 117 | assert(left.CheckComparable(right)); 118 | if (left.IsNull() || right.IsNull()) 119 | return left.OperateNull(right); 120 | if (left.CompareLessThan(right) == CMP_TRUE) 121 | return left.Copy(); 122 | return right.Copy(); 123 | } 124 | 125 | Value VarlenType::Max(const Value &left, const Value &right) const { 126 | assert(left.CheckComparable(right)); 127 | if (left.IsNull() || right.IsNull()) 128 | return left.OperateNull(right); 129 | if (left.CompareGreaterThan(right) == CMP_TRUE) 130 | return left.Copy(); 131 | return right.Copy(); 132 | } 133 | 134 | std::string VarlenType::ToString(const Value &val) const { 135 | uint32_t len = GetLength(val); 136 | 137 | if (val.IsNull()) 138 | return "varlen_null"; 139 | if (len == PELOTON_VARCHAR_MAX_LEN) 140 | return "varlen_max"; 141 | if (len == 0) { 142 | return ""; 143 | } 144 | return std::string(GetData(val), len - 1); 145 | } 146 | 147 | void VarlenType::SerializeTo(const Value &val, char *storage) const { 148 | uint32_t len = GetLength(val); 149 | if (len == PELOTON_VALUE_NULL) { 150 | memcpy(storage, &len, sizeof(uint32_t)); 151 | return; 152 | } else { 153 | memcpy(storage, &len, sizeof(uint32_t)); 154 | memcpy(storage + sizeof(uint32_t), val.value_.varlen, len); 155 | } 156 | } 157 | 158 | // Deserialize a value of the given type from the given storage space. 159 | Value VarlenType::DeserializeFrom(const char *storage) const { 160 | uint32_t len = *reinterpret_cast(storage); 161 | if (len == PELOTON_VALUE_NULL) { 162 | return Value(type_id_, nullptr, len, false); 163 | } 164 | // set manage_data as true 165 | return Value(type_id_, storage + sizeof(uint32_t), len, true); 166 | } 167 | 168 | Value VarlenType::Copy(const Value &val) const { return Value(val); } 169 | 170 | Value VarlenType::CastAs(const Value &value, const TypeId type_id) const { 171 | std::string str; 172 | // switch begins 173 | switch (type_id) { 174 | case TypeId::BOOLEAN: { 175 | str = value.ToString(); 176 | std::transform(str.begin(), str.end(), str.begin(), ::tolower); 177 | if (str == "true" || str == "1" || str == "t") 178 | return Value(type_id, 1); 179 | else if (str == "false" || str == "0" || str == "f") 180 | return Value(type_id, 0); 181 | else 182 | throw Exception("Boolean value format error."); 183 | } 184 | case TypeId::TINYINT: { 185 | str = value.ToString(); 186 | int8_t tinyint = 0; 187 | try { 188 | tinyint = stoi(str); 189 | } catch (std::out_of_range &e) { 190 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 191 | "Numeric value out of range."); 192 | } 193 | if (tinyint < PELOTON_INT8_MIN) 194 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 195 | "Numeric value out of range."); 196 | return Value(type_id, tinyint); 197 | } 198 | case TypeId::SMALLINT: { 199 | str = value.ToString(); 200 | int16_t smallint = 0; 201 | try { 202 | smallint = stoi(str); 203 | } catch (std::out_of_range &e) { 204 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 205 | "Numeric value out of range."); 206 | } 207 | if (smallint < PELOTON_INT16_MIN) 208 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 209 | "Numeric value out of range."); 210 | return Value(type_id, smallint); 211 | } 212 | case TypeId::INTEGER: { 213 | str = value.ToString(); 214 | int32_t integer = 0; 215 | try { 216 | integer = stoi(str); 217 | } catch (std::out_of_range &e) { 218 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 219 | "Numeric value out of range."); 220 | } 221 | if (integer > PELOTON_INT32_MAX || integer < PELOTON_INT32_MIN) 222 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 223 | "Numeric value out of range."); 224 | return Value(type_id, integer); 225 | } 226 | case TypeId::BIGINT: { 227 | str = value.ToString(); 228 | int64_t bigint = 0; 229 | try { 230 | bigint = stoll(str); 231 | } catch (std::out_of_range &e) { 232 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 233 | "Numeric value out of range."); 234 | } 235 | if (bigint > PELOTON_INT64_MAX || bigint < PELOTON_INT64_MIN) 236 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 237 | "Numeric value out of range."); 238 | return Value(type_id, bigint); 239 | } 240 | case TypeId::DECIMAL: { 241 | str = value.ToString(); 242 | double res = 0; 243 | try { 244 | res = stod(str); 245 | } catch (std::out_of_range &e) { 246 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 247 | "Numeric value out of range."); 248 | } 249 | if (res > PELOTON_DECIMAL_MAX || res < PELOTON_DECIMAL_MIN) 250 | throw Exception(EXCEPTION_TYPE_OUT_OF_RANGE, 251 | "Numeric value out of range."); 252 | return Value(type_id, res); 253 | } 254 | case TypeId::VARCHAR: 255 | return value.Copy(); 256 | default: 257 | break; 258 | } 259 | throw Exception("VARCHAR is not coercable to " + TypeIdToString(type_id)); 260 | } 261 | } // namespace cmudb 262 | -------------------------------------------------------------------------------- /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 | BufferPoolManager bpm(10, "test.db"); 15 | 16 | auto page_zero = bpm.NewPage(temp_page_id); 17 | EXPECT_NE(nullptr, page_zero); 18 | EXPECT_EQ(0, temp_page_id); 19 | 20 | // The test will fail here if the page is null 21 | ASSERT_NE(nullptr, page_zero); 22 | 23 | // change content in page one 24 | strcpy(page_zero->GetData(), "Hello"); 25 | 26 | for (int i = 1; i < 10; ++i) { 27 | EXPECT_NE(nullptr, bpm.NewPage(temp_page_id)); 28 | } 29 | // all the pages are pinned, the buffer pool is full 30 | for (int i = 10; i < 15; ++i) { 31 | EXPECT_EQ(nullptr, bpm.NewPage(temp_page_id)); 32 | } 33 | // upin the first five pages, add them to LRU list, set as dirty 34 | for (int i = 0; i < 5; ++i) { 35 | EXPECT_EQ(true, bpm.UnpinPage(i, true)); 36 | } 37 | // we have 5 empty slots in LRU list, evict page zero out of buffer pool 38 | for (int i = 10; i < 14; ++i) { 39 | EXPECT_NE(nullptr, bpm.NewPage(temp_page_id)); 40 | } 41 | // fetch page one again 42 | page_zero = bpm.FetchPage(0); 43 | // check read content 44 | EXPECT_EQ(0, strcmp(page_zero->GetData(), "Hello")); 45 | 46 | remove("test.db"); 47 | } 48 | 49 | } // namespace cmudb 50 | -------------------------------------------------------------------------------- /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/concurrency/lock_manager_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * lock_manager_test.cpp 3 | */ 4 | 5 | #include 6 | 7 | #include "concurrency/transaction_manager.h" 8 | #include "gtest/gtest.h" 9 | 10 | namespace cmudb { 11 | 12 | 13 | /* 14 | * This test is only a sanity check. Please do not rely on this test 15 | * to check the correctness. 16 | */ 17 | TEST(LockManagerTest, BasicTest) { 18 | LockManager lock_mgr{false}; 19 | TransactionManager txn_mgr{&lock_mgr}; 20 | RID rid{0, 0}; 21 | 22 | std::thread t0([&] { 23 | Transaction txn(0); 24 | bool res = lock_mgr.LockShared(&txn, rid); 25 | EXPECT_EQ(res, true); 26 | EXPECT_EQ(txn.GetState(), TransactionState::GROWING); 27 | txn_mgr.Commit(&txn); 28 | EXPECT_EQ(txn.GetState(), TransactionState::COMMITTED); 29 | }); 30 | 31 | std::thread t1([&] { 32 | Transaction txn(1); 33 | bool res = lock_mgr.LockShared(&txn, rid); 34 | EXPECT_EQ(res, true); 35 | EXPECT_EQ(txn.GetState(), TransactionState::GROWING); 36 | txn_mgr.Commit(&txn); 37 | EXPECT_EQ(txn.GetState(), TransactionState::COMMITTED); 38 | }); 39 | 40 | t0.join(); 41 | t1.join(); 42 | } 43 | } // namespace cmudb 44 | -------------------------------------------------------------------------------- /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/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 | BufferPoolManager *bpm = new BufferPoolManager(100, "test.db"); 49 | // create and fetch header_page 50 | page_id_t page_id; 51 | auto header_page = bpm->NewPage(page_id); 52 | (void)header_page; 53 | // create b+ tree 54 | BPlusTree, RID, GenericComparator<8>> tree("foo_pk", bpm, 55 | comparator); 56 | // create transaction 57 | Transaction *transaction = new Transaction(0); 58 | while (!quit) { 59 | std::cout << "> "; 60 | std::cin >> instruction; 61 | switch (instruction) { 62 | case 'd': 63 | std::cin >> filename; 64 | tree.RemoveFromFile(filename, transaction); 65 | std::cout << tree.ToString(verbose) << '\n'; 66 | break; 67 | case 'a': 68 | std::cin >> key; 69 | index_key.SetFromInteger(key); 70 | tree.Remove(index_key, transaction); 71 | std::cout << tree.ToString(verbose) << '\n'; 72 | break; 73 | case 'i': 74 | std::cin >> key; 75 | rid.Set((int32_t)(key >> 32), (int)(key & 0xFFFFFFFF)); 76 | index_key.SetFromInteger(key); 77 | tree.Insert(index_key, rid, transaction); 78 | std::cout << tree.ToString(verbose) << '\n'; 79 | break; 80 | case 'f': 81 | std::cin >> filename; 82 | tree.InsertFromFile(filename, transaction); 83 | std::cout << tree.ToString(verbose) << '\n'; 84 | break; 85 | case 'q': 86 | quit = true; 87 | break; 88 | case 'r': 89 | std::cin >> key; 90 | index_key.SetFromInteger(key); 91 | for (auto iterator = tree.Begin(index_key); iterator.isEnd() == false; 92 | ++iterator) 93 | std::cout << "key is " << (*iterator).first << " value is " 94 | << (*iterator).second << '\n'; 95 | 96 | break; 97 | case 'v': 98 | verbose = !verbose; 99 | tree.ToString(verbose); 100 | break; 101 | // case 'x': 102 | // tree.destroyTree(); 103 | // tree.print(); 104 | // break; 105 | case '?': 106 | std::cout << usageMessage(); 107 | break; 108 | default: 109 | std::cin.ignore(256, '\n'); 110 | std::cout << usageMessage(); 111 | break; 112 | } 113 | } 114 | bpm->UnpinPage(HEADER_PAGE_ID, true); 115 | delete bpm; 116 | delete transaction; 117 | remove("test.db"); 118 | } 119 | } // namespace cmudb 120 | -------------------------------------------------------------------------------- /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 | BufferPoolManager *buffer_pool_manager = new BufferPoolManager(20, "test.db"); 14 | page_id_t header_page_id; 15 | HeaderPage *page = 16 | static_cast(buffer_pool_manager->NewPage(header_page_id)); 17 | ASSERT_NE(nullptr, page); 18 | page->Init(); 19 | 20 | for (int i = 1; i < 28; i++) { 21 | std::string name = std::to_string(i); 22 | EXPECT_EQ(page->InsertRecord(name, i), true); 23 | } 24 | 25 | for (int i = 27; i >= 1; i--) { 26 | std::string name = std::to_string(i); 27 | page_id_t root_id; 28 | EXPECT_EQ(page->GetRootId(name, root_id), true); 29 | // std::cout << "root page id is " << root_id << '\n'; 30 | } 31 | 32 | for (int i = 1; i < 28; i++) { 33 | std::string name = std::to_string(i); 34 | EXPECT_EQ(page->UpdateRecord(name, i + 10), true); 35 | } 36 | 37 | for (int i = 27; i >= 1; i--) { 38 | std::string name = std::to_string(i); 39 | page_id_t root_id; 40 | EXPECT_EQ(page->GetRootId(name, root_id), true); 41 | // std::cout << "root page id is " << root_id << '\n'; 42 | } 43 | 44 | for (int i = 1; i < 28; i++) { 45 | std::string name = std::to_string(i); 46 | EXPECT_EQ(page->DeleteRecord(name), true); 47 | } 48 | 49 | EXPECT_EQ(page->GetRecordCount(), 0); 50 | 51 | delete buffer_pool_manager; 52 | } 53 | } // namespace cmudb 54 | -------------------------------------------------------------------------------- /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 "table/table_heap.h" 13 | #include "table/tuple.h" 14 | #include "vtable/virtual_table.h" 15 | #include "gtest/gtest.h" 16 | 17 | namespace cmudb { 18 | 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 | // test2: create one tuple 25 | std::vector values; 26 | Value v(TypeId::INVALID); 27 | for (int i = 0; i < schema->GetColumnCount(); i++) { 28 | // get type 29 | TypeId type = schema->GetType(i); 30 | switch (type) { 31 | case TypeId::BOOLEAN: 32 | v = Value(type, 0); 33 | break; 34 | case TypeId::TINYINT: 35 | v = Value(type, (int8_t)30); 36 | break; 37 | case TypeId::SMALLINT: 38 | case TypeId::INTEGER: 39 | v = Value(type, (int32_t)200); 40 | break; 41 | case TypeId::BIGINT: 42 | v = Value(type, (int64_t)1000); 43 | break; 44 | case TypeId::VARCHAR: 45 | v = Value(type, "Hello World", 12, true); 46 | break; 47 | default: 48 | break; 49 | } 50 | values.emplace_back(v); 51 | } 52 | 53 | Tuple tuple(values, schema); 54 | std::cout << tuple.ToString(schema) << '\n'; 55 | 56 | BufferPoolManager *buffer_pool_manager = new BufferPoolManager(50, "test.db"); 57 | LockManager *lock_manager = new LockManager(true); 58 | TableHeap *table = new TableHeap(buffer_pool_manager, lock_manager); 59 | // create transaction 60 | Transaction *transaction = new Transaction(0); 61 | 62 | RID rid; 63 | std::vector rid_v; 64 | for (int i = 0; i < 2000; ++i) { 65 | table->InsertTuple(tuple, rid, transaction); 66 | // std::cout << rid << '\n'; 67 | rid_v.push_back(rid); 68 | } 69 | 70 | TableIterator itr = table->begin(transaction); 71 | while (itr != table->end()) { 72 | // std::cout << itr->ToString(schema) << std::endl; 73 | ++itr; 74 | } 75 | 76 | // int i = 0; 77 | std::random_shuffle(rid_v.begin(), rid_v.end()); 78 | for (auto rid : rid_v) { 79 | // std::cout << i++ << std::endl; 80 | assert(table->MarkDelete(rid, transaction) == 1); 81 | } 82 | remove("test.db"); // remove db file 83 | delete schema; 84 | delete table; 85 | delete buffer_pool_manager; 86 | } 87 | 88 | } // namespace cmudb 89 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------