├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── deps └── CMakeLists.txt ├── docs ├── Rucbase-Lab1[存储管理实验文档].md ├── Rucbase-Lab2[索引管理实验文档].md ├── Rucbase-Lab3[查询执行实验指导].md ├── Rucbase-Lab3[查询执行实验文档].md ├── Rucbase-Lab4[并发控制实验文档].md ├── Rucbase使用文档.md ├── Rucbase学生实验操作说明示例.md ├── Rucbase开发文档.md ├── Rucbase环境配置文档.md └── Rucbase项目结构.md ├── ownbase ├── .gitignore ├── CMakeLists.txt ├── README.md ├── lib │ └── README.md ├── main.cpp └── ownbase.h ├── pics ├── B+树删除流程.png ├── B+树插入流程.png ├── B+树的结构.png ├── architecture_fixed.jpg ├── ast.png ├── 存储层-类之间的关系.png ├── 执行模块流程图.jpg └── 锁表结构.png ├── report └── report.md ├── rucbase_client ├── CMakeLists.txt └── main.cpp └── src ├── CMakeLists.txt ├── common ├── CMakeLists.txt ├── config.h ├── context.h ├── logger.h ├── macros.h ├── rwlatch.cpp └── rwlatch.h ├── defs.h ├── errors.h ├── execution ├── CMakeLists.txt ├── ExecutorTest_db_task2and3 │ ├── db.meta │ ├── grade │ └── student ├── README.md ├── exec_sql.cpp ├── execution.h ├── execution_defs.h ├── execution_manager.cpp ├── execution_manager.h ├── executor_abstract.h ├── executor_delete.h ├── executor_index_scan.h ├── executor_insert.h ├── executor_nestedloop_join.h ├── executor_projection.h ├── executor_seq_scan.h ├── executor_update.h ├── input_all_task.sql ├── input_task1.sql ├── input_task2.sql ├── input_task3.sql ├── interp_test.h ├── output.txt ├── res_task1_output.txt ├── res_task2_output.txt ├── res_task3_output.txt ├── res_taskall_output.txt ├── task1_test.sh ├── task2_test.sh ├── task3_test.sh └── taskall_test.sh ├── index ├── CMakeLists.txt ├── b_plus_tree_concurrent_test.cpp ├── b_plus_tree_delete_test.cpp ├── b_plus_tree_insert_test.cpp ├── ix.h ├── ix_defs.h ├── ix_index_handle.cpp ├── ix_index_handle.h ├── ix_manager.h ├── ix_node_handle.cpp ├── ix_node_handle.h ├── ix_scan.cpp └── ix_scan.h ├── interp.h ├── parser ├── CMakeLists.txt ├── ast.cpp ├── ast.h ├── ast_printer.h ├── lex.l ├── lex.yy.c ├── lex.yy.cpp ├── lex.yy.hpp ├── parser.h ├── parser_defs.h ├── yacc.tab.c ├── yacc.tab.cpp ├── yacc.tab.h └── yacc.y ├── record ├── CMakeLists.txt ├── bitmap.h ├── rm.h ├── rm_defs.h ├── rm_file_handle.cpp ├── rm_file_handle.h ├── rm_gtest.cpp ├── rm_manager.h ├── rm_scan.cpp └── rm_scan.h ├── record_printer.h ├── recovery ├── CMakeLists.txt ├── checkpoint.cpp ├── checkpoint.h ├── log_defs.h ├── log_manager.cpp ├── log_manager.h ├── log_record.h ├── log_recovery.cpp └── log_recovery.h ├── replacer ├── CMakeLists.txt ├── clock_replacer.cpp ├── clock_replacer.h ├── clock_replacer_test.cpp ├── lru_replacer.cpp ├── lru_replacer.h ├── lru_replacer_test.cpp └── replacer.h ├── rucbase.cpp ├── storage ├── CMakeLists.txt ├── buffer_pool_manager.cpp ├── buffer_pool_manager.h ├── buffer_pool_manager_test.cpp ├── disk_manager.cpp ├── disk_manager.h ├── disk_manager_test.cpp └── page.h ├── system ├── CMakeLists.txt ├── sm.h ├── sm_defs.h ├── sm_gtest.cpp ├── sm_manager.cpp ├── sm_manager.h └── sm_meta.h └── transaction ├── CMakeLists.txt ├── concurrency ├── lock_manager.cpp └── lock_manager.h ├── concurrency_test.cpp ├── lock_manager_test.cpp ├── transaction.h ├── transaction_manager.cpp ├── transaction_manager.h ├── txn_defs.h └── txn_manager_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 4 3 | TabWidth: 4 4 | ColumnLimit: 120 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ide 2 | .idea/ 3 | .vscode/ 4 | 5 | # macos 6 | .DS_Store 7 | 8 | # build 9 | /build/ 10 | /rucbase_client/build/ 11 | /cmake-build-debug/ 12 | /cmake-build-wsl_profile_debug 13 | *.out 14 | 15 | # bison & flex 16 | #lex.yy.cpp 17 | #yacc.tab.h 18 | #yacc.tab.cpp 19 | 20 | # python 21 | __pycache__/ 22 | 23 | # backup 24 | *_backup/ 25 | 26 | # tmp 27 | /tmp/ 28 | /src/parser/build/ 29 | *.zip 30 | 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | 2 | [submodule "deps/googletest"] 3 | path = deps/googletest 4 | url = https://github.com/google/googletest 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(RucBase) 3 | 4 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 5 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 7 | 8 | set(CMAKE_CXX_STANDARD 17) 9 | set(CMAKE_CXX_FLAGS "-Wall") 10 | 11 | 12 | 13 | enable_testing() 14 | add_subdirectory(src) 15 | add_subdirectory(deps) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ruc-deke 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 | # RucBase 2 | # 2020201591 罗文扬 3 | ``` 4 | ____ _ _ ____ ____ _ ____ _____ 5 | | _ \| | | |/ ___| __ ) / \ / ___|| ____| 6 | | |_) | | | | | | _ \ / _ \ \___ \| _| 7 | | _ <| |_| | |___| |_) / ___ \ ___) | |___ 8 | |_| \_ \___/ \____|____/_/ \_\____/|_____| 9 | ``` 10 | Rucbase是一个精简的RDBMS原型系统,用于《数据库系统实现》课程的实验教学。本实验框架源码参考和借鉴了CMU15-445课程的[BusTub](https://github.com/cmu-db/bustub) 和Standford CS346课程的[Redbase](https://web.stanford.edu/class/cs346/2015/redbase.html) 。 11 | 12 | ## 实验环境: 13 | - 操作系统:Ubuntu 18.04 及以上(64位) 14 | - 编译器:GCC 15 | - 编程语言:C++17 16 | - 管理工具:cmake 17 | - 推荐编辑器:VScode 18 | 19 | ### 依赖环境库配置: 20 | - gcc 7.1及以上版本(要求完全支持C++17) 21 | - cmake 3.16及以上版本 22 | - flex 23 | - bison 24 | - readline 25 | 26 | 欲查看有关依赖运行库和编译工具的更多信息,以及如何运行的说明,请查阅[Rucbase使用文档](docs/Rucbase使用文档.md) 27 | 28 | 欲了解如何在非Linux系统PC上部署实验环境的指导,请查阅[Rucbase环境配置文档](docs/Rucbase环境配置文档.md) 29 | 30 | ## 实验文档索引 31 | 32 | > 这里给出目前公开的文档分类索引 33 | 34 | ### 开发规范文档 35 | 36 | - [Rucbase开发文档](docs/Rucbase开发文档.md) 37 | 38 | ### 项目说明文档 39 | 40 | - [Rucbase环境配置文档](docs/Rucbase环境配置文档.md) 41 | - [Rucbase使用文档](docs/Rucbase使用文档.md) 42 | - [Rucbase项目结构](docs/Rucbase项目结构.md) 43 | 44 | ### 学生实验文档(2022-10-26日更新) 45 | 46 | > 请使用命令git pull来拉取最新的实验文档 47 | 48 | - [Rucbase学生实验操作说明示例](docs/Rucbase学生实验操作说明示例.md) 49 | - [Rucbase-Lab1存储管理实验文档](docs/Rucbase-Lab1[存储管理实验文档].md) 50 | - [Rucbase-Lab2索引管理实验文档](docs/Rucbase-Lab2[索引管理实验文档].md) 51 | - [Rucbase-Lab3查询执行实验文档](docs/Rucbase-Lab3[查询执行实验文档].md) 52 | - [Rucbase-Lab3查询执行实验指导](docs/Rucbase-Lab3[查询执行实验指导].md) 53 | - [Rucbase-Lab4并发控制实验文档](docs/Rucbase-Lab4[并发控制实验文档].md) 54 | 55 | ### 时间安排和工作量估计 56 | 57 | | **实验** | **发布时间** | **截止时间** | **工作量估计** | **难度系数** | 58 | | ------------ | ----------------- | ----------------- | -------------- | ------------ | 59 | | 存储管理实验 | 10.12(第六周) | 11.2(第九周) | 15h | 简单 | 60 | | 索引管理实验 | 10.26(第八周) | 11.23(第十二周) | 35h | 中等 | 61 | | 查询执行实验 | 11.16(第十一周) | 12.28(十七周) | 30-40h | 困难 | 62 | | 并发控制实验 | 12.1(第十三周) | 1.18(第二十周) | 25-30h | 困难 | 63 | -------------------------------------------------------------------------------- /deps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_SUBDIRECTORY(googletest) -------------------------------------------------------------------------------- /docs/Rucbase使用文档.md: -------------------------------------------------------------------------------- 1 | # Rucbase使用指南 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - [环境配置](#%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE) 10 | - [项目下载](#%E9%A1%B9%E7%9B%AE%E4%B8%8B%E8%BD%BD) 11 | - [GoogleTest子模块安装](#googletest%E5%AD%90%E6%A8%A1%E5%9D%97%E5%AE%89%E8%A3%85) 12 | - [编译](#%E7%BC%96%E8%AF%91) 13 | - [运行 (S/C)](#%E8%BF%90%E8%A1%8C-sc) 14 | - [测试单元](#%E6%B5%8B%E8%AF%95%E5%8D%95%E5%85%83) 15 | - [系统简介](#%E7%B3%BB%E7%BB%9F%E7%AE%80%E4%BB%8B) 16 | - [SQL Example](#sql-example) 17 | - [基本结构](#%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84) 18 | 19 | 20 | 21 | ## 环境配置 22 | 23 | Rucbase需要以下依赖环境库配置: 24 | 25 | - gcc 7.1及以上版本(要求完全支持C++17) 26 | - cmake 3.16及以上版本 27 | - flex 28 | - bison 29 | - readline 30 | 31 | 可以通过命令完成环境配置(以Debian/Ubuntu-apt为例) 32 | 33 | ```bash 34 | sudo apt-get install build-essential # build-essential packages, including gcc, g++, make and so on 35 | sudo apt-get install cmake # cmake package 36 | sudo apt-get install flex bison # flex & bison packages 37 | sudo apt-get install libreadline-dev # readline package 38 | ``` 39 | 40 | 可以通过`cmake --version`命令来查看cmake版本,如果低于3.16,需要在官网下载3.16以上的版本并解压,手动进行安装。 41 | 42 | 注意,在CentOS下,编译时可能存在头文件冲突的问题,我们不建议你使用Ubuntu以外的操作系统,你可以向助教获取帮助 43 | 44 | ## 项目下载 45 | 46 | 你可以通过以下两种方式来完成Rucbase及其子模块的下载 47 | 48 | ```bash 49 | git clone --recursive https://github.com/ruc-deke/rucbase-lab.git 50 | ``` 51 | 52 | or 53 | 54 | ```bash 55 | git clone https://github.com/ruc-deke/rucbase-lab.git 56 | cd rucbase-lab 57 | git submodule init 58 | git submodule update //clone submodules 59 | ``` 60 | 61 | **注意,当新lab放出时,你需要先使用git pull命令拉取最新的实验文档** 62 | 63 | ### GoogleTest子模块安装 64 | 65 | 当本项目及子模块下载成功之后,你需要进行GoogleTest子模块的安装,具体命令如下: 66 | 67 | ```bash 68 | cd deps 69 | cd googletest 70 | mkdir build 71 | cd build 72 | cmake .. 73 | make 74 | sudo make install 75 | ``` 76 | 77 | ## 编译 78 | 79 | 整个系统分为服务端和客户端,你可以使用以下命令来进行服务端的编译: 80 | 81 | ```bash 82 | mkdir build 83 | cd build 84 | cmake .. [-DCMAKE_BUILD_TYPE=Debug]|[-DCMAKE_BUILD_TYPE=Release] 85 | make rucbase <-j4>|<-j8> # 选择4 or 8线程编译,如果你不想选择,那么make -j也是可以的 86 | ``` 87 | 88 | 可以使用以下命令来进行客户端的编译: 89 | 90 | ```bash 91 | cd rucbase_client 92 | mkdir build 93 | cd build 94 | cmake .. [-DCMAKE_BUILD_TYPE=Debug]|[-DCMAKE_BUILD_TYPE=Release] 95 | make <-j4>|<-j8> # 选择4 or 8线程编译 96 | ``` 97 | 98 | ## 运行 (S/C) 99 | 100 | 首先运行服务端: 101 | 102 | ```bash 103 | cd build 104 | ./bin/rucbase # 如果存在该数据库,直接加载;若不存在该数据库,自动创建 105 | ``` 106 | 107 | 然后开启客户端,用户可以同时开启多个客户端: 108 | 109 | ```bash 110 | cd rucbase_client/build 111 | ./rucbase_client 112 | ``` 113 | 114 | 用户可以通过在客户端界面使用exit命令来进行客户端的关闭: 115 | 116 | ```bash 117 | Rucbase> exit; 118 | ``` 119 | 120 | 服务端的关闭需要在服务端运行界面使用ctrl+c来进行关闭,关闭服务端时,系统会把数据页刷新到磁盘中。 121 | 122 | + 如果需要删除数据库,则需要在build文件夹下删除与数据库同名的目录 123 | + 如果需要删除某个数据库中的表文件,则需要在build文件夹下找到数据库同名目录,进入该目录,然后删除表文件 124 | 125 | ## 测试单元 126 | 127 | GoogleTest框架测试 128 | 129 | 包含以下模块测试: 130 | 131 | - 存储模块: 132 | 133 | - disk_manager_test 134 | 135 | - lru_replacer_test 136 | 137 | - clock_replacer_test (选做) 138 | 139 | - buffer_pool_manager_test 140 | 141 | - rm_gtest 142 | 143 | - 索引模块: 144 | 145 | - b_plus_tree_insert_test 146 | - b_plus_tree_delete_test 147 | - b_plus_tree_concurrent_test 148 | 149 | - 执行模块: 150 | 151 | - task1_test.sh 152 | - task2_test.sh 153 | - task3_test.sh 154 | - taskall_test.sh 155 | 156 | - 事务模块: 157 | 158 | - txn_test 159 | - lock_test 160 | - concurrency_test 161 | 162 | 以lru_replacer_test为例,可以通过以下命令进行测试: 163 | 164 | ```bash 165 | cd build 166 | make lru_replacer_test 167 | ./bin/lru_replacer_test 168 | ``` 169 | 170 | ## 系统简介 171 | 172 | ### SQL Example 173 | 174 | 目前系统支持基础DML和DDL语句,包括以下语句: 175 | 176 | - create/drop table; 177 | - create/drop index; 178 | - insert; 179 | - delete; 180 | - update; 181 | - begin; 182 | - commit/abort; 183 | 184 | 目前事务的并发控制暂时支持可重复读隔离级别,事务暂时只支持基础insert、delete、update和select操作。 185 | 186 | 以下为SQL操作demo,具体下SQL语法可以在系统中使用help语句查询: 187 | 188 | ```sql 189 | create table student (id int, name char(32), major char(32)); 190 | create index student (id); 191 | create table grade (course char(32), student_id int, score float); 192 | create index grade (student_id); 193 | 194 | show tables; 195 | desc student; 196 | 197 | begin; 198 | insert into student values (1, 'Tom', 'Computer Science'); 199 | insert into student values (2, 'Jerry', 'Computer Science'); 200 | insert into student values (3, 'Jack', 'Electrical Engineering'); 201 | commit; 202 | 203 | begin; 204 | select * from student where id>=1; 205 | update student set major = 'Electrical Engineering' where id = 2; 206 | select * from student where id>=1; 207 | delete from student where name = 'Jack'; 208 | select * from student where id>=1; 209 | commit; 210 | 211 | begin; 212 | insert into grade values ('Data Structure', 1, 90.0); 213 | insert into grade values ('Data Structure', 2, 95.0); 214 | insert into grade values ('Calculus', 2, 82.0); 215 | insert into grade values ('Calculus', 1, 88.5); 216 | abort; 217 | 218 | begin; 219 | insert into grade values ('Data Structure', 1, 90.0); 220 | insert into grade values ('Data Structure', 2, 95.0); 221 | insert into grade values ('Calculus', 2, 82.0); 222 | insert into grade values ('Calculus', 1, 88.5); 223 | commit; 224 | 225 | select * from student, grade; 226 | select id, name, major, course, score from student, grade where student.id = grade.student_id; 227 | select id, name, major, course, score from student join grade where student.id = grade.student_id; 228 | 229 | drop index student (id); 230 | desc student; 231 | 232 | drop table student; 233 | drop table grade; 234 | show tables; 235 | 236 | exit; 237 | ``` 238 | 239 | ## 基本结构 240 | 241 | + rucbase_client: 客户端源代码,无需修改 242 | 243 | + src: 服务端源代码 244 | 245 | + Parser: 将原始SQL语句转换为抽象语法树AST, 由ExecutionManager进一步解释和执行。 246 | 247 | + storage: 存储层, 由自己实现的BufferPoolManager和*unix提供接口的磁盘文件系统组成, 暴露给上层的单位为Page 248 | 249 | + replacer: 缓冲区替换算法 250 | 251 | + record : 管理存储了无序记录的Page, 对Page进行Tuple级操作 252 | 253 | + index : 管理存储在记录文件中的无序数据记录的持久索引 254 | 255 | + system : 处理DDL语句, 负责跟踪 创建/删除 表/索引 256 | 257 | + execution : 执行模块, 负责DML语句 查询计划树的生成与执行 258 | 259 | + transaction : 事务模块,提供事务的Begin/Commit/Abort接口, LockManager提供事务相关的锁 260 | 261 | + recovery : 日志模块,WAL算法,负责日志管理和系统恢复,目前只支持DML语句恢复 262 | 263 | ![Architecture](../pics/architecture_fixed.jpg) 264 | -------------------------------------------------------------------------------- /docs/Rucbase学生实验操作说明示例.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | - [实验操作指南](#%E5%AE%9E%E9%AA%8C%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97) 5 | - [补全代码](#%E8%A1%A5%E5%85%A8%E4%BB%A3%E7%A0%81) 6 | - [自我测试](#%E8%87%AA%E6%88%91%E6%B5%8B%E8%AF%95) 7 | - [扩展](#%E6%89%A9%E5%B1%95) 8 | - [想运行数据库?](#%E6%83%B3%E8%BF%90%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%BA%93) 9 | - [如何提交](#%E5%A6%82%E4%BD%95%E6%8F%90%E4%BA%A4) 10 | 11 | 12 | 13 | # 实验操作指南 14 | 15 | > 本文档将以实验一(存储管理实验)部分任务为例示范你应该如何完成一个实验任务,并进行自我测试和提交 16 | 17 | 根据[Rucbase-Lab1存储管理实验文档](Rucbase-Lab1[存储管理实验文档].md)查看相应任务,进入`rucbase`项目对应的代码目录,你将看到实验文档中说明的需要你本人完成的项目源码`.cpp/.h`文件,除此之外,你还可能看到类似`*_test.cpp/*_test.sh`的文件,其中`*_test.cpp`文件是`GoogleTest`测试源码文件,而`.sh`文件则是在其他实验中使用的非`GoogleTest`测试脚本,你会在对应实验发布时获得相应的说明文件。 18 | 19 | 在阅读本指南前,请确保自己已经按照[Rucbase环境配置文档](Rucbase环境配置文档.md)配置好了环境,并根据[Rucbase使用文档](Rucbase使用文档.md)创建好了`build`目录,测试了`cmake`工具 20 | 21 | ## 补全代码 22 | 23 | 我们以`任务1.2 缓冲池替换策略`为例,进入`replacer`目录,其中源码文件如下: 24 | 25 | ```bash 26 | Mode LastWriteTime Length Name 27 | ---- ------------- ------ ---- 28 | -a---- 2022/9/18 10:07 1352 clock_replacer.cpp 29 | -a---- 2022/9/18 10:07 1252 clock_replacer.h 30 | -a---- 2022/9/18 10:07 2039 clock_replacer_test.cpp 31 | -a---- 2022/9/18 10:07 434 CMakeLists.txt 32 | -a---- 2022/9/18 10:07 1590 lru_replacer.cpp 33 | -a---- 2022/9/18 10:07 2351 lru_replacer.h 34 | -a---- 2022/9/18 10:07 9615 lru_replacer_test.cpp 35 | -a---- 2022/9/18 10:07 1021 replacer.h 36 | ``` 37 | 38 | 为完成本任务,你需要完成[Rucbase-Lab1存储管理实验文档](Rucbase-Lab1[存储管理实验文档].md)中指出的`Replacer`类相应接口,其中必做任务是LRU策略,你需要打开`lru_replacer.cpp`,在源码文件中,对于每个你需要实现的接口方法进行了功能描述和实现提示,示例如下: 39 | 40 | ```cpp 41 | /** 42 | * @brief 使用LRU策略删除一个victim frame,这个函数能得到frame_id 43 | * @param[out] frame_id id of frame that was removed, nullptr if no victim was found 44 | * @return true if a victim frame was found, false otherwise 45 | */ 46 | bool LRUReplacer::Victim(frame_id_t *frame_id) { 47 | // C++17 std::scoped_lock 48 | // 它能够避免死锁发生,其构造函数能够自动进行上锁操作,析构函数会对互斥量进行解锁操作,保证线程安全。 49 | std::scoped_lock lock{latch_}; 50 | 51 | // Todo: 52 | // 利用lru_replacer中的LRUlist_,LRUHash_实现LRU策略 53 | // 选择合适的frame指定为淘汰页面,赋值给*frame_id 54 | 55 | return true; 56 | } 57 | ``` 58 | 59 | 请注意,返回值代码语句只是为了保证初始代码不会有语法检查错误,事实上整个接口内部的实现逻辑你可以完全修改。 60 | 61 | 如果你需要额外的数据结构,你可以在`.h`文件中对类进行添加,在`LRUReplacer`中,进入`lru_replacer.h`,你可以看到已经给出的部分数据结构。 62 | 63 | ```cpp 64 | std::mutex latch_; // 互斥锁 65 | std::list LRUlist_; // 按加入的时间顺序存放unpinned pages的frame id,首部表示最近被访问 66 | std::unordered_map::iterator> LRUhash_; // frame_id_t -> unpinned pages的frame id 67 | size_t max_size_; // 最大容量(与缓冲池的容量相同) 68 | ``` 69 | 70 | 你也可以自行修改或添加,只要最后正确实现功能即可。**但是,强烈建议你不要擅自修改每个接口的声明** 71 | 72 | 当你完成LRU策略后,你也可以自行考虑是否实现CLOCK策略,整个实现步骤与LRU任务类似。 73 | 74 | 75 | 76 | ## 自我测试 77 | 78 | 当你完成实验任务后,你可以对自己的实验进行功能测试。请首先回到整个项目的根目录 79 | 80 | ```bash 81 | cd rucbase-lab 82 | cd build 83 | make [target_test] 84 | ./bin/[target_test] 85 | ``` 86 | 87 | 如你想测试`lru_replacer_test.cpp`提供的测试,你的编译执行命令就是 88 | 89 | ```bash 90 | make lru_replacer_test 91 | ./bin/lru_replacer_test 92 | ``` 93 | 94 | 如果测试通过,你得到的输出应该类似于 95 | 96 | ``` 97 | aaron@DESKTOP-U1TJ26P:~/rucdeke/rucbase-lab/build$ ./bin/lru_replacer_test 98 | Running main() from /home/aaron/rucdeke/rucbase-lab/deps/googletest/googletest/src/gtest_main.cc 99 | [==========] Running 6 tests from 1 test suite. 100 | [----------] Global test environment set-up. 101 | [----------] 6 tests from LRUReplacerTest 102 | [ RUN ] LRUReplacerTest.SampleTest 103 | [ OK ] LRUReplacerTest.SampleTest (0 ms) 104 | [ RUN ] LRUReplacerTest.Victim 105 | [ OK ] LRUReplacerTest.Victim (1 ms) 106 | [ RUN ] LRUReplacerTest.Pin 107 | [ OK ] LRUReplacerTest.Pin (1 ms) 108 | [ RUN ] LRUReplacerTest.Size 109 | [ OK ] LRUReplacerTest.Size (8 ms) 110 | [ RUN ] LRUReplacerTest.ConcurrencyTest 111 | [ OK ] LRUReplacerTest.ConcurrencyTest (122 ms) 112 | [ RUN ] LRUReplacerTest.IntegratedTest 113 | [ OK ] LRUReplacerTest.IntegratedTest (12 ms) 114 | [----------] 6 tests from LRUReplacerTest (146 ms total) 115 | 116 | [----------] Global test environment tear-down 117 | [==========] 6 tests from 1 test suite ran. (146 ms total) 118 | [ PASSED ] 6 tests. 119 | ``` 120 | 121 | 测试输出会给出各个测试点是否通过的信息以及每个测试点花费的时间。 122 | 123 | 如果没有通过,会在对应测试点打印不匹配的信息。你可以进行比对分析为什么自己的逻辑没能给出正确的输出。 124 | 125 | ### 扩展 126 | 127 | 整个项目是是由`cmake`管理的,如果你想知道编译命令为什么这么写,请打开每个模块下的`CMakeLists.txt` 128 | 129 | ```cmake 130 | set(SOURCES lru_replacer.cpp clock_replacer.cpp) 131 | add_library(lru_replacer STATIC ${SOURCES}) 132 | add_library(clock_replacer STATIC ${SOURCES}) 133 | 134 | add_executable(lru_replacer_test lru_replacer_test.cpp) 135 | target_link_libraries(lru_replacer_test lru_replacer gtest_main) # add gtest 136 | 137 | 138 | 139 | add_executable(clock_replacer_test clock_replacer_test.cpp) 140 | target_link_libraries(clock_replacer_test clock_replacer gtest_main) # add gtest 141 | ``` 142 | 143 | 这里将`lru_replacer_test.cpp`生成可执行文件`lru_replacer_test`,因此你需要`make lru_replacer_test`来生成测试可执行文件,其他实验测试命名以依据此规范。 144 | 145 | ## 想运行数据库? 146 | 147 | 当然可以!同样是以上文的`CMakeLists.txt`为例,你可以编译自己的库文件,如你自己实现了`lru_replacer`,你可以进行如下编译操作 148 | 149 | ```bash 150 | make lru_replacer 151 | -- Configuring done 152 | -- Generating done 153 | -- Build files have been written to: /home/aaron/rucdeke/rucbase-lab/build 154 | Scanning dependencies of target lru_replacer 155 | [ 33%] Building CXX object src/replacer/CMakeFiles/lru_replacer.dir/lru_replacer.cpp.o 156 | [ 66%] Building CXX object src/replacer/CMakeFiles/lru_replacer.dir/clock_replacer.cpp.o 157 | [100%] Linking CXX static library ../../lib/liblru_replacer.a 158 | [100%] Built target lru_replacer 159 | ``` 160 | 161 | 在`rucbase-lab/build/lib`下你可以看到你生成的`liblru_replacer.a`静态库文件,你可以将它替换掉我们提供的静态库文件,这样就可以编译一个以你自己实现的`lru_replacer`作为相应功能模块的数据库可执行文件。 162 | 163 | ## 如何提交 164 | 165 | 我们鼓励你使用`Github`或者`Gitee`的私有仓库功能,建立自己的远程代码库进行代码与报告提交,并将助教账户添加为你们仓库的协作者或者赋予访问权限。助教会定期拉取你们的仓库代码进行测试,并将测试结果贴在仓库的`issues`中。 166 | 167 | 你们也可以使用压缩包的方式将自己对应实验更改的代码文件以及相关报告发送到助教邮箱或者其他系统中,请根据个人喜好选择提交方式。 168 | 169 | -------------------------------------------------------------------------------- /docs/Rucbase开发文档.md: -------------------------------------------------------------------------------- 1 | # Rucbase开发文档 2 | 3 | 4 | 5 | 6 | - [flex && bison文件的修改](#flex--bison%E6%96%87%E4%BB%B6%E7%9A%84%E4%BF%AE%E6%94%B9) 7 | - [代码规范](#%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83) 8 | - [注释规范](#%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83) 9 | - [example](#example) 10 | - [相关资料](#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99) 11 | 12 | 13 | 14 | ## flex && bison文件的修改 15 | 在parser子文件夹下涉及flex和bison文件的修改,开发者修改lex.l和yacc.y文件之后,需要通过以下命令重新生成对应文件: 16 | ```bash 17 | flex --header-file=lex.yy.hpp -o lex.yy.cpp lex.l 18 | bison --defines=yacc.tab.hpp -o yacc.tab.cpp yacc.y 19 | ``` 20 | 21 | ## 代码规范 22 | > 以VScode format配置为例 23 | 24 | ``` 25 | { 26 | BasedOnStyle: Google, 27 | IndentWidth: 4, 28 | TabWidth: 4, 29 | ColumnLimit: 120 30 | } 31 | ``` 32 | 33 | ## 注释规范 34 | 使用`/** */`写注释块 35 | 36 | 使用`//` 写行内注释 37 | 38 | 使用`//*/`作为代码注释开关 39 | 40 | 尽量不要使用中文标点符号 41 | 注释块符号 42 | ``` 43 | @brief: 简要描述 44 | @param: 参数描述 45 | @return: 用它来制定一个 method 或 function的返回值 46 | @note: 注意点 47 | @see: 用它来指明其他相关的 method 或 function . 你可以使用多个这种标签. 48 | ``` 49 | ### example 50 | ```cpp 51 | 52 | /** 53 | * @brief: 初始化系统. 54 | * @note: 动态参数不可为0 55 | * @param: dynamParam 动态参数 56 | * @param: controlPrarm 控制参数 57 | * @return: true初始化成功, false初始化失败 58 | */ 59 | bool init(int dynamParam,int controlPrarm){ 60 | //*/ 61 | codeSegement1; 62 | /*/ 63 | codeSegement2; 64 | //*/ 65 | } 66 | 67 | ``` 68 | 69 | ## 相关资料 70 | [GoogleTest框架简介](https://www.cnblogs.com/jycboy/p/6057677.html) -------------------------------------------------------------------------------- /docs/Rucbase环境配置文档.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 4 | 5 | - [Rucbase环境配置文档](#rucbase%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E6%96%87%E6%A1%A3) 6 | - [虚拟机软件VMware/VirtualBox](#%E8%99%9A%E6%8B%9F%E6%9C%BA%E8%BD%AF%E4%BB%B6vmwarevirtualbox) 7 | - [WSL 2.0 (Windows Subsystem for Linux)](#wsl-20-windows-subsystem-for-linux) 8 | - [WSL2.0相关注意事项](#wsl20%E7%9B%B8%E5%85%B3%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9) 9 | - [Ubuntu操作系统](#ubuntu%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F) 10 | - [代码编辑器VS Code](#%E4%BB%A3%E7%A0%81%E7%BC%96%E8%BE%91%E5%99%A8vs-code) 11 | - [安装VS Code](#%E5%AE%89%E8%A3%85vs-code) 12 | - [在VS Code中使用SSH连接到虚拟机](#%E5%9C%A8vs-code%E4%B8%AD%E4%BD%BF%E7%94%A8ssh%E8%BF%9E%E6%8E%A5%E5%88%B0%E8%99%9A%E6%8B%9F%E6%9C%BA) 13 | - [代码托管平台GitHub](#%E4%BB%A3%E7%A0%81%E6%89%98%E7%AE%A1%E5%B9%B3%E5%8F%B0github) 14 | - [Git使用](#git%E4%BD%BF%E7%94%A8) 15 | 16 | 17 | 18 | # Rucbase环境配置文档 19 | 20 | 巧妇难为无米之炊,我们需要先对实验进行一些准备工作。配置好开发环境后,才能在Linux系统上运行Rucbase。 21 | 22 | 在本实验中,我们统一使用Ubuntu操作系统(Linux的一个主流发行版)。推荐在你的主机(Windows/macOS)中使用VS Code作为代码编辑器。使用虚拟机的同学,推荐使用SSH连接到虚拟机进行实验。 23 | 24 | 请同学们按照我们的要求进行环境配置。如果你没有按照文档要求进行实验(比如使用了其他的Linux发行版),可能会出现一些奇怪的bug,进而影响实验进度和分数。 25 | 26 | 如果配置环境的过程中遇到了文档中没提及的问题,你可以向助教求助。 27 | 28 | 29 | 30 | ## 虚拟机软件VMware/VirtualBox 31 | 32 | 我们使用虚拟机软件安装Linux操作系统。虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。 33 | 34 | VMware是目前流行的虚拟机运行软件,在Windows/macOS主机上均可以安装使用。它在Windows和macOS中有不同的名字,但使用方法基本一致,请根据你主机的操作系统选择对应版本。 35 | 36 | [下载Windows版VMware Workstation 16 Pro](https://www.vmware.com/go/getworkstation-win) 37 | 38 | [下载macOS版本VMware Fusion 12 Pro](https://www.vmware.com/go/getfusion) 39 | 40 | VMware是收费软件,你可以选择试用一段时间,在激活过程中需要小心防范欺诈链接。 41 | 42 | 如果你不希望使用收费软件,我们推荐[Oracle VM VirtualBox](https://www.virtualbox.org/),它是一款优秀的开源跨平台虚拟化软件。 43 | 44 | ## WSL 2.0 (Windows Subsystem for Linux) 45 | 46 | 对于使用Windows10,11操作系统的同学,你也可以使用WSL2.0作为自己的Linux开发环境。请注意,WSL开发环境默认安装为1.0版本,1.0并不能提供完整的Linux内核功能,**你必须升级为WSL2.0**,对此有疑问的同学,可在实验课上联系助教获得支持。 47 | 48 | 你也可以阅读Microsoft提供的官方文档:[适用于 Linux 的 Windows 子系统文档 | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/wsl/) 49 | 50 | ### WSL2.0相关注意事项 51 | 52 | 1. 开启WSL2.0后,会开启hyper-v虚拟层,这样可能会导致你的pc性能略有下降(10%以内,包括磁盘IO和CPU/GPU处理速度)。 53 | 2. 部分使用低版本VMware,VirtualBox等虚拟化软件的同学可能无法正常启动虚拟化软件,需要进行升级 54 | 3. 对**国内手游/安卓模拟器有需求的同学**请注意,开启WSL2.0后可能你安装的模拟器将无法使用,你需要改用`BlueStack`等模拟器 55 | 4. PC机性能较为孱弱且对于日常使用性能有较高要求的同学不建议部署WSL2.0 56 | 57 | ## Ubuntu操作系统 58 | 59 | 安装并激活好虚拟机软件后,我们就要在VMware上安装Linux操作系统。 60 | 61 | Linux是开源的操作系统,拥有数百个发行版,其中Ubuntu是一个非常流行的桌面发行版。我们本次实验中就采用Ubuntu操作系统作为Rucbase的运行环境。 62 | 63 | **我们强烈建议你使用Ubuntu系统,虽然在内部测试中,项目在原生Debian和CentOS上也可以通过编译,但是仍然可能遇到一些始料未及的问题** 64 | 65 | [下载Ubuntu 20.04.5 LTS](https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/20.04.5/ubuntu-20.04.5-desktop-amd64.iso) 66 | 67 | 下载好Ubuntu后,在VMware中选择文件-新建虚拟机,选择“安装程序光盘映像文件”,即刚才下载的iso文件,然后按提示安装即可。 68 | 69 | 安装好系统之后,建议进行换源,以提高下载速度。Ubuntu官方的服务器在国外,速度较慢且时常断连。为了提高软件安装/更新速度,Ubuntu提供了选择服务器的功能,可以帮助我们使用位于国内的快速稳定的镜像服务器。先进入系统设置,打开软件和更新,在服务器列表中选择较近的服务器,我们建议使用清华或者阿里源。 70 | 71 | 我们还需要安装Rucbase的依赖环境,如gcc、cmake等。具体请参阅[Rucbase使用文档](Rucbase使用文档.md) 72 | 73 | 关于Linux的学习,还可以参考以下资料: 74 | 75 | [Linux教程](https://www.runoob.com/linux) 76 | 77 | [学习Linux命令行](https://nju-projectn.github.io/ics-pa-gitbook/ics2021/linux.html) 78 | 79 | 80 | 81 | ## 代码编辑器VS Code 82 | 83 | ### 安装VS Code 84 | 85 | VS Code(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器。该软件支持语法高亮、代码自动补全、代码重构、查看定义功能,并且内置了命令行工具和 Git 版本控制系统。用户可以更改主题和键盘快捷方式实现个性化设置,也可以通过内置的扩展程序商店安装扩展以拓展软件功能。 86 | 87 | [下载VS Code](https://code.visualstudio.com/Download) 88 | 89 | 在主机上下载并安装VS Code后,可以进行少量的配置,如安装C++扩展包、SSH相关扩展等。 90 | 91 | **我们推荐你使用VScode,你也可以使用其他IDE或代码编辑工具,如Clion, Vim, Emacs等,但是请注意,使用其他工具请确保自己对其有一定的熟练度,在遇到部分配置问题时助教可能无法提供充足的支持** 92 | 93 | ### 在VS Code中使用SSH连接到虚拟机 94 | 95 | 需要在VS Code中安装SSH的相关扩展。 96 | 97 | [官方教程](https://code.visualstudio.com/docs/remote/ssh-tutorial) 98 | 99 | 在VS Code扩展商店中搜索并安装Remote SSH,安装成功后进入配置页。在配置文件中填入虚拟机的用户名等相关信息,就可以连接虚拟机。 100 | 101 | 102 | 103 | ## 代码托管平台GitHub 104 | 105 | Git 是一个版本控制系统,可以让你跟踪你对文件所做的修改。GitHub是世界上最大的软件远程仓库,是一个面向开源和私有软件项目的托管平台,使用Git做分布式版本控制。 106 | 107 | 你需要在Ubuntu中安装Git来下载实验代码。具体请参阅[Rucbase使用文档](Rucbase使用文档.md)。 108 | 109 | [下载Git](https://git-scm.com/download) 110 | 111 | ## Git使用 112 | 113 | 对于不熟悉Git使用的同学,我们推荐你阅读该博客教程[Git教程 - 廖雪峰的官方网站 (liaoxuefeng.com)](https://www.liaoxuefeng.com/wiki/896043488029600) 114 | 115 | 同时,我们也推荐你尽早申请Github教育优惠,获取相关福利软件。其中,我们强烈推荐使用`GitKraken`作为你的`git`可视化管理工具。 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /ownbase/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | /lib/*.a -------------------------------------------------------------------------------- /ownbase/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(YourBase) 3 | 4 | link_directories(./lib) 5 | 6 | add_executable(yourbase main.cpp) 7 | target_link_libraries(yourbase ownbase parser execution system index record transaction recovery storage rwlatch readline pthread) 8 | -------------------------------------------------------------------------------- /ownbase/README.md: -------------------------------------------------------------------------------- 1 | ## OwnBase 2 | OwnBase是rucbase项目中参与者构建自己的db程序的子项目。 3 | 4 | ### 使用方法 5 | 1. 完成对应lab,通过编译和测试 6 | 2. 在rucbase主目录`/build/lib`下找到自己实现的模块的对应`.lib`文件,替换`/ownbase/lib`下对应文件 7 | 3. 8 | ```bash 9 | cd ownbase 10 | mkdir build 11 | cd build 12 | cmake .. 13 | make 14 | ``` 15 | 4. 你将获得对应模块功能由自己实现的`yourbase`可执行文件,可以用`rucbase_client`进行连接测试 -------------------------------------------------------------------------------- /ownbase/lib/README.md: -------------------------------------------------------------------------------- 1 | ## ownbase 静态库目录 2 | 3 | > 这里存放各个模块的静态库,你可以用自己实现的库替换 4 | 5 | 6 | 7 | 8 | 9 | ```bash 10 | find ./lib/*.a -type f -print0 |xargs -0 md5sum>libmd5.txt 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /ownbase/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ownbase.h" 2 | 3 | int main(int argc, char *argv[]) { return ownbase_start(argc, argv); } -------------------------------------------------------------------------------- /ownbase/ownbase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int ownbase_start(int argc, char **argv); -------------------------------------------------------------------------------- /pics/B+树删除流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/B+树删除流程.png -------------------------------------------------------------------------------- /pics/B+树插入流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/B+树插入流程.png -------------------------------------------------------------------------------- /pics/B+树的结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/B+树的结构.png -------------------------------------------------------------------------------- /pics/architecture_fixed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/architecture_fixed.jpg -------------------------------------------------------------------------------- /pics/ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/ast.png -------------------------------------------------------------------------------- /pics/存储层-类之间的关系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/存储层-类之间的关系.png -------------------------------------------------------------------------------- /pics/执行模块流程图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/执行模块流程图.jpg -------------------------------------------------------------------------------- /pics/锁表结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/pics/锁表结构.png -------------------------------------------------------------------------------- /rucbase_client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(rucbase_client) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | find_package(Threads REQUIRED) 6 | 7 | 8 | 9 | add_executable(${PROJECT_NAME} main.cpp) 10 | 11 | 12 | target_link_libraries(rucbase_client 13 | pthread readline 14 | ) -------------------------------------------------------------------------------- /rucbase_client/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MAX_MEM_BUFFER_SIZE 8192 18 | #define PORT_DEFAULT 8765 19 | 20 | bool is_exit_command(std::string &cmd) { return cmd == "exit" || cmd == "exit;" || cmd == "bye" || cmd == "bye;"; } 21 | 22 | int init_unix_sock(const char *unix_sock_path) { 23 | int sockfd = socket(PF_UNIX, SOCK_STREAM, 0); 24 | if (sockfd < 0) { 25 | fprintf(stderr, "failed to create unix socket. %s", strerror(errno)); 26 | return -1; 27 | } 28 | 29 | struct sockaddr_un sockaddr; 30 | memset(&sockaddr, 0, sizeof(sockaddr)); 31 | sockaddr.sun_family = PF_UNIX; 32 | snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", unix_sock_path); 33 | 34 | if (connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { 35 | fprintf(stderr, "failed to connect to server. unix socket path '%s'. error %s", sockaddr.sun_path, 36 | strerror(errno)); 37 | close(sockfd); 38 | return -1; 39 | } 40 | return sockfd; 41 | } 42 | 43 | int init_tcp_sock(const char *server_host, int server_port) { 44 | struct hostent *host; 45 | struct sockaddr_in serv_addr; 46 | 47 | if ((host = gethostbyname(server_host)) == NULL) { 48 | fprintf(stderr, "gethostbyname failed. errmsg=%d:%s\n", errno, strerror(errno)); 49 | return -1; 50 | } 51 | 52 | int sockfd; 53 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 54 | fprintf(stderr, "create socket error. errmsg=%d:%s\n", errno, strerror(errno)); 55 | return -1; 56 | } 57 | 58 | serv_addr.sin_family = AF_INET; 59 | serv_addr.sin_port = htons(server_port); 60 | serv_addr.sin_addr = *((struct in_addr *)host->h_addr); 61 | bzero(&(serv_addr.sin_zero), 8); 62 | 63 | if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1) { 64 | fprintf(stderr, "Failed to connect. errmsg=%d:%s\n", errno, strerror(errno)); 65 | close(sockfd); 66 | return -1; 67 | } 68 | return sockfd; 69 | } 70 | 71 | int main(int argc, char *argv[]) { 72 | int ret = 0; // set_terminal_noncanonical(); 73 | // if (ret < 0) { 74 | // printf("Warning: failed to set terminal non canonical. Long command may be " 75 | // "handled incorrect\n"); 76 | // } 77 | 78 | const char *unix_socket_path = nullptr; 79 | const char *server_host = "127.0.0.1"; // 127.0.0.1 192.168.31.25 80 | int server_port = PORT_DEFAULT; 81 | int opt; 82 | 83 | while ((opt = getopt(argc, argv, "s:h:p:")) > 0) { 84 | switch (opt) { 85 | case 's': 86 | unix_socket_path = optarg; 87 | break; 88 | case 'p': 89 | char *ptr; 90 | server_port = (int)strtol(optarg, &ptr, 10); 91 | break; 92 | case 'h': 93 | server_host = optarg; 94 | break; 95 | default: 96 | break; 97 | } 98 | } 99 | 100 | // const char *prompt_str = "RucBase > "; 101 | 102 | int sockfd, send_bytes; 103 | // char send[MAXLINE]; 104 | 105 | if (unix_socket_path != nullptr) { 106 | sockfd = init_unix_sock(unix_socket_path); 107 | } else { 108 | sockfd = init_tcp_sock(server_host, server_port); 109 | } 110 | if (sockfd < 0) { 111 | return 1; 112 | } 113 | 114 | char recv_buf[MAX_MEM_BUFFER_SIZE]; 115 | 116 | while (1) { 117 | char *line_read = readline("Rucbase> "); 118 | if (line_read == nullptr) { 119 | // EOF encountered 120 | break; 121 | } 122 | std::string command = line_read; 123 | free(line_read); 124 | 125 | if (!command.empty()) { 126 | add_history(command.c_str()); 127 | if (is_exit_command(command)) { 128 | printf("The client will be closed.\n"); 129 | break; 130 | } 131 | 132 | if ((send_bytes = write(sockfd, command.c_str(), command.length() + 1)) == -1) { 133 | // fprintf(stderr, "send error: %d:%s \n", errno, strerror(errno)); 134 | std::cerr << "send error: " << errno << ":" << strerror(errno) << " \n" << std::endl; 135 | exit(1); 136 | } 137 | int len = recv(sockfd, recv_buf, MAX_MEM_BUFFER_SIZE, 0); 138 | if (len < 0) { 139 | fprintf(stderr, "Connection was broken: %s\n", strerror(errno)); 140 | break; 141 | } else if (len == 0) { 142 | printf("Connection has been closed\n"); 143 | break; 144 | } else { 145 | for (int i = 0; i <= len; i++) { 146 | if (recv_buf[i] == '\0') { 147 | break; 148 | } else { 149 | printf("%c", recv_buf[i]); 150 | } 151 | } 152 | memset(recv_buf, 0, MAX_MEM_BUFFER_SIZE); 153 | } 154 | } 155 | } 156 | close(sockfd); 157 | printf("Bye.\n"); 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | 3 | add_subdirectory(record) 4 | add_subdirectory(index) 5 | add_subdirectory(system) 6 | add_subdirectory(execution) 7 | add_subdirectory(parser) 8 | add_subdirectory(storage) 9 | add_subdirectory(common) 10 | add_subdirectory(replacer) 11 | add_subdirectory(transaction) 12 | add_subdirectory(recovery) 13 | 14 | # 后续lab开放 15 | # add_executable(rawcli rawcli.cpp) 16 | # target_link_libraries(rawcli parser execution pthread) 17 | 18 | # add_executable(rucbase rucbase.cpp) 19 | # target_link_libraries(rucbase parser execution readline pthread) 20 | 21 | # add_library(ownbase STATIC ownbase.cpp) -------------------------------------------------------------------------------- /src/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES rwlatch.cpp) 2 | add_library(rwlatch STATIC ${SOURCES}) 3 | -------------------------------------------------------------------------------- /src/common/config.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // config.h 6 | // 7 | // Identification: src/include/common/config.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #pragma once 14 | 15 | #include 16 | #include // NOLINT 17 | #include 18 | 19 | /** Cycle detection is performed every CYCLE_DETECTION_INTERVAL milliseconds. */ 20 | extern std::chrono::milliseconds cycle_detection_interval; 21 | 22 | /** True if logging should be enabled, false otherwise. */ 23 | extern std::atomic enable_logging; 24 | 25 | /** If ENABLE_LOGGING is true, the log should be flushed to disk every LOG_TIMEOUT. */ 26 | extern std::chrono::duration log_timeout; 27 | 28 | static constexpr int INVALID_FRAME_ID = -1; // invalid frame id 29 | static constexpr int INVALID_PAGE_ID = -1; // invalid page id 30 | static constexpr int INVALID_TXN_ID = -1; // invalid transaction id 31 | static constexpr int INVALID_TIMESTAMP = -1; // invalid transaction timestamp 32 | static constexpr int INVALID_LSN = -1; // invalid log sequence number 33 | static constexpr int HEADER_PAGE_ID = 0; // the header page id 34 | static constexpr int PAGE_SIZE = 4096; // size of a data page in byte 35 | static constexpr int BUFFER_POOL_SIZE = 65536; // size of buffer pool 36 | static constexpr int LOG_BUFFER_SIZE = ((BUFFER_POOL_SIZE + 1) * PAGE_SIZE); // size of a log buffer in byte 37 | static constexpr int BUCKET_SIZE = 50; // size of extendible hash bucket 38 | 39 | using frame_id_t = int32_t; // frame id type, 帧页ID, 页在BufferPool中的存储单元称为帧,一帧对应一页 40 | using page_id_t = int32_t; // page id type , 页ID 41 | using txn_id_t = int32_t; // transaction id type 42 | using lsn_t = int32_t; // log sequence number type 43 | using slot_offset_t = size_t; // slot offset type 44 | using oid_t = uint16_t; 45 | using timestamp_t = int32_t; // timestamp type, used for transaction concurrency 46 | 47 | // log file 48 | static const std::string LOG_FILE_NAME = "db.log"; 49 | 50 | // replacer 51 | static const std::string REPLACER_TYPE = "LRU"; 52 | -------------------------------------------------------------------------------- /src/common/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "transaction/concurrency/lock_manager.h" 4 | #include "recovery/log_manager.h" 5 | 6 | // used for data_send 7 | static int const_offset = -1; 8 | 9 | class Context { 10 | public: 11 | Context (LockManager *lock_mgr, LogManager *log_mgr, 12 | Transaction *txn, char *data_send = nullptr, int *offset = &const_offset) 13 | : lock_mgr_(lock_mgr), log_mgr_(log_mgr), txn_(txn), 14 | data_send_(data_send), offset_(offset) {} 15 | 16 | LockManager *lock_mgr_; 17 | LogManager *log_mgr_; 18 | Transaction *txn_; 19 | char *data_send_; 20 | int *offset_; 21 | }; -------------------------------------------------------------------------------- /src/common/macros.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // macros.h 6 | // 7 | // Identification: src/include/common/macros.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #pragma once 14 | 15 | #include 16 | #include 17 | 18 | #define BUSTUB_ASSERT(expr, message) assert((expr) && (message)) 19 | 20 | #define UNREACHABLE(message) throw std::logic_error(message) 21 | 22 | // Macros to disable copying and moving 23 | #define DISALLOW_COPY(cname) \ 24 | cname(const cname &) = delete; /* NOLINT */ \ 25 | cname &operator=(const cname &) = delete; /* NOLINT */ 26 | 27 | #define DISALLOW_MOVE(cname) \ 28 | cname(cname &&) = delete; /* NOLINT */ \ 29 | cname &operator=(cname &&) = delete; /* NOLINT */ 30 | 31 | #define DISALLOW_COPY_AND_MOVE(cname) \ 32 | DISALLOW_COPY(cname); \ 33 | DISALLOW_MOVE(cname); 34 | 35 | -------------------------------------------------------------------------------- /src/common/rwlatch.cpp: -------------------------------------------------------------------------------- 1 | #include "rwlatch.h" 2 | 3 | /** 4 | * Acquire a write latch. 5 | */ 6 | void ReaderWriterLatch::WLock() { 7 | std::unique_lock latch(mutex_); 8 | while (writer_entered_) { 9 | reader_.wait(latch); 10 | } 11 | writer_entered_ = true; 12 | while (reader_count_ > 0) { 13 | writer_.wait(latch); 14 | } 15 | } 16 | 17 | /** 18 | * Release a write latch. 19 | */ 20 | void ReaderWriterLatch::WUnlock() { 21 | std::lock_guard guard(mutex_); 22 | writer_entered_ = false; 23 | reader_.notify_all(); 24 | } 25 | 26 | /** 27 | * Acquire a read latch. 28 | */ 29 | void ReaderWriterLatch::RLock() { 30 | std::unique_lock latch(mutex_); 31 | while (writer_entered_ || reader_count_ == MAX_READERS) { 32 | reader_.wait(latch); 33 | } 34 | reader_count_++; 35 | } 36 | 37 | /** 38 | * Release a read latch. 39 | */ 40 | void ReaderWriterLatch::RUnlock() { 41 | std::lock_guard guard(mutex_); 42 | reader_count_--; 43 | if (writer_entered_) { 44 | if (reader_count_ == 0) { 45 | writer_.notify_one(); 46 | } 47 | } else { 48 | if (reader_count_ == MAX_READERS - 1) { 49 | reader_.notify_one(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/common/rwlatch.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // rwlatch.h 6 | // 7 | // Identification: src/include/common/rwlatch.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #pragma once 14 | 15 | #include 16 | #include // NOLINT 17 | #include // NOLINT 18 | 19 | #include "common/macros.h" 20 | 21 | /** 22 | * Reader-Writer latch backed by std::mutex. 23 | */ 24 | class ReaderWriterLatch { 25 | using mutex_t = std::mutex; 26 | using cond_t = std::condition_variable; 27 | static const uint32_t MAX_READERS = UINT_MAX; 28 | 29 | public: 30 | ReaderWriterLatch() = default; 31 | ~ReaderWriterLatch() { std::lock_guard guard(mutex_); } 32 | 33 | DISALLOW_COPY(ReaderWriterLatch); 34 | 35 | /** 36 | * Acquire a write latch. 37 | */ 38 | void WLock(); 39 | 40 | /** 41 | * Release a write latch. 42 | */ 43 | void WUnlock(); 44 | 45 | /** 46 | * Acquire a read latch. 47 | */ 48 | void RLock(); 49 | 50 | /** 51 | * Release a read latch. 52 | */ 53 | void RUnlock(); 54 | 55 | private: 56 | mutex_t mutex_; 57 | cond_t writer_; 58 | cond_t reader_; 59 | uint32_t reader_count_{0}; 60 | bool writer_entered_{false}; 61 | }; 62 | -------------------------------------------------------------------------------- /src/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // 此处重载了<<操作符,在ColMeta中进行了调用 7 | template::value, T>::type> 8 | std::ostream &operator<<(std::ostream &os, const T &enum_val) { 9 | os << static_cast(enum_val); 10 | return os; 11 | } 12 | 13 | template::value, T>::type> 14 | std::istream &operator>>(std::istream &is, T &enum_val) { 15 | int int_val; 16 | is >> int_val; 17 | enum_val = static_cast(int_val); 18 | return is; 19 | } 20 | 21 | struct Rid { 22 | int page_no; 23 | int slot_no; 24 | 25 | friend bool operator==(const Rid &x, const Rid &y) { 26 | return x.page_no == y.page_no && x.slot_no == y.slot_no; 27 | } 28 | 29 | friend bool operator!=(const Rid &x, const Rid &y) { return !(x == y); } 30 | }; 31 | 32 | enum ColType { 33 | TYPE_INT, TYPE_FLOAT, TYPE_STRING 34 | }; 35 | 36 | inline std::string coltype2str(ColType type) { 37 | std::map m = { 38 | {TYPE_INT, "INT"}, 39 | {TYPE_FLOAT, "FLOAT"}, 40 | {TYPE_STRING, "STRING"} 41 | }; 42 | return m.at(type); 43 | } 44 | 45 | class RecScan { 46 | public: 47 | virtual ~RecScan() = default; 48 | 49 | virtual void next() = 0; 50 | 51 | virtual bool is_end() const = 0; 52 | 53 | virtual Rid rid() const = 0; 54 | }; 55 | -------------------------------------------------------------------------------- /src/errors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class RedBaseError : public std::exception { 8 | std::string _msg; 9 | 10 | public: 11 | RedBaseError(const std::string &msg) : _msg("Error: " + msg) {} 12 | 13 | const char *what() const noexcept override { return _msg.c_str(); } 14 | }; 15 | 16 | class InternalError : public RedBaseError { 17 | public: 18 | InternalError(const std::string &msg) : RedBaseError(msg) {} 19 | }; 20 | 21 | // PF errors 22 | class UnixError : public RedBaseError { 23 | public: 24 | UnixError() : RedBaseError(strerror(errno)) {} 25 | }; 26 | 27 | class FileNotOpenError : public RedBaseError { 28 | public: 29 | FileNotOpenError(int fd) : RedBaseError("Invalid file descriptor: " + std::to_string(fd)) {} 30 | }; 31 | 32 | class FileNotClosedError : public RedBaseError { 33 | public: 34 | FileNotClosedError(const std::string &filename) : RedBaseError("File is opened: " + filename) {} 35 | }; 36 | 37 | class FileExistsError : public RedBaseError { 38 | public: 39 | FileExistsError(const std::string &filename) : RedBaseError("File already exists: " + filename) {} 40 | }; 41 | 42 | class FileNotFoundError : public RedBaseError { 43 | public: 44 | FileNotFoundError(const std::string &filename) : RedBaseError("File not found: " + filename) {} 45 | }; 46 | 47 | // RM errors 48 | class RecordNotFoundError : public RedBaseError { 49 | public: 50 | RecordNotFoundError(int page_no, int slot_no) 51 | : RedBaseError("Record not found: (" + std::to_string(page_no) + "," + std::to_string(slot_no) + ")") {} 52 | }; 53 | 54 | class InvalidRecordSizeError : public RedBaseError { 55 | public: 56 | InvalidRecordSizeError(int record_size) : RedBaseError("Invalid record size: " + std::to_string(record_size)) {} 57 | }; 58 | 59 | // IX errors 60 | class InvalidColLengthError : public RedBaseError { 61 | public: 62 | InvalidColLengthError(int col_len) : RedBaseError("Invalid column length: " + std::to_string(col_len)) {} 63 | }; 64 | 65 | class IndexEntryNotFoundError : public RedBaseError { 66 | public: 67 | IndexEntryNotFoundError() : RedBaseError("Index entry not found") {} 68 | }; 69 | 70 | // SM errors 71 | class DatabaseNotFoundError : public RedBaseError { 72 | public: 73 | DatabaseNotFoundError(const std::string &db_name) : RedBaseError("Database not found: " + db_name) {} 74 | }; 75 | 76 | class DatabaseExistsError : public RedBaseError { 77 | public: 78 | DatabaseExistsError(const std::string &db_name) : RedBaseError("Database already exists: " + db_name) {} 79 | }; 80 | 81 | class TableNotFoundError : public RedBaseError { 82 | public: 83 | TableNotFoundError(const std::string &tab_name) : RedBaseError("Table not found: " + tab_name) {} 84 | }; 85 | 86 | class TableExistsError : public RedBaseError { 87 | public: 88 | TableExistsError(const std::string &tab_name) : RedBaseError("Table already exists: " + tab_name) {} 89 | }; 90 | 91 | class ColumnNotFoundError : public RedBaseError { 92 | public: 93 | ColumnNotFoundError(const std::string &col_name) : RedBaseError("Column not found: " + col_name) {} 94 | }; 95 | 96 | class IndexNotFoundError : public RedBaseError { 97 | public: 98 | IndexNotFoundError(const std::string &tab_name, const std::string &col_name) 99 | : RedBaseError("Index not found: " + tab_name + '.' + col_name) {} 100 | }; 101 | 102 | class IndexExistsError : public RedBaseError { 103 | public: 104 | IndexExistsError(const std::string &tab_name, const std::string &col_name) 105 | : RedBaseError("Index already exists: " + tab_name + '.' + col_name) {} 106 | }; 107 | 108 | // QL errors 109 | class InvalidValueCountError : public RedBaseError { 110 | public: 111 | InvalidValueCountError() : RedBaseError("Invalid value count") {} 112 | }; 113 | 114 | class StringOverflowError : public RedBaseError { 115 | public: 116 | StringOverflowError() : RedBaseError("String is too long") {} 117 | }; 118 | 119 | class IncompatibleTypeError : public RedBaseError { 120 | public: 121 | IncompatibleTypeError(const std::string &lhs, const std::string &rhs) 122 | : RedBaseError("Incompatible type error: lhs " + lhs + ", rhs " + rhs) {} 123 | }; 124 | 125 | class AmbiguousColumnError : public RedBaseError { 126 | public: 127 | AmbiguousColumnError(const std::string &col_name) : RedBaseError("Ambiguous column: " + col_name) {} 128 | }; 129 | 130 | class PageNotExistError : public RedBaseError { 131 | public: 132 | PageNotExistError(const std::string &table_name, int page_no) 133 | : RedBaseError("Page " + std::to_string(page_no) + " in table " + table_name + "not exits") {} 134 | }; -------------------------------------------------------------------------------- /src/execution/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES execution_manager.cpp) 2 | add_library(execution STATIC ${SOURCES}) 3 | 4 | target_link_libraries(execution system record system transaction) 5 | 6 | 7 | 8 | ## exec_sql 9 | add_executable(exec_sql exec_sql.cpp) 10 | target_link_libraries(exec_sql execution parser gtest_main) 11 | -------------------------------------------------------------------------------- /src/execution/ExecutorTest_db_task2and3/db.meta: -------------------------------------------------------------------------------- 1 | ExecutorTest_db 2 | 2 3 | grade 4 | 3 5 | grade course 2 32 0 0 6 | grade student_id 0 4 32 0 7 | grade score 1 4 36 0 8 | 9 | student 10 | 3 11 | student id 0 4 0 0 12 | student name 2 32 4 0 13 | student major 2 32 36 0 14 | 15 | -------------------------------------------------------------------------------- /src/execution/ExecutorTest_db_task2and3/grade: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/src/execution/ExecutorTest_db_task2and3/grade -------------------------------------------------------------------------------- /src/execution/ExecutorTest_db_task2and3/student: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timturing/RUCbase/fae7c873017ddcfb1456f4b2365d47f8d1b9561a/src/execution/ExecutorTest_db_task2and3/student -------------------------------------------------------------------------------- /src/execution/README.md: -------------------------------------------------------------------------------- 1 | ## lab3测试说明 2 | 3 | 在lab3中,系统目前不使用`GoogleTest`框架进行测试,而是采用`Linux Bash`脚本对你完成的各个任务进行`SQL`语句执行并比对正确的结果,你能够在脚本执行完成时看到通过测试与否的提示,如果未能通过,系统会使用`diff`将你的输出和正确输出进行比对方便你定位执行错误的地方。 4 | 5 | - `output.txt` : 你的执行输出文件 6 | - `res_output.txt`:正确的执行输出文件 7 | - `input_*.sql`:各个任务点执行的SQL语句 8 | - `task_test.sh`:测试脚本 9 | 10 | 测试脚本依赖二进制程序`exec_sql`,你可以按照下列语句进行编译: 11 | 12 | ```bash 13 | cd rucbase; 14 | cd build 15 | cmake .. 16 | make exec_sql 17 | ``` 18 | 19 | 得到的`exec_sql`程序在`rucbase/build/bin/`目录下,请不要移动该程序。 20 | 21 | 22 | ### 其他事项 23 | 24 | 如果你无法执行`.sh`脚本,请执行以下命令,以`task1`为例: 25 | 26 | ```bash 27 | chmod +x task1_test.sh 28 | ./task1_test.sh 29 | ``` 30 | 31 | 注意:**不能**使用`sh task1_test.sh`运行脚本。 32 | 33 | 34 | 在不影响主要比对逻辑的情况下,你可以自行根据需求修改测试脚本。 35 | 36 | -------------------------------------------------------------------------------- /src/execution/exec_sql.cpp: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | 3 | #include "execution.h" 4 | #include "gtest/gtest.h" 5 | #include "interp_test.h" 6 | 7 | #define BUFFER_LENGTH 8192 8 | 9 | std::string db_name_ = "ExecutorTest_db"; 10 | std::unique_ptr disk_manager_ = std::make_unique(); 11 | std::unique_ptr buffer_pool_manager_ = 12 | std::make_unique(BUFFER_POOL_SIZE, disk_manager_.get()); 13 | std::unique_ptr rm_manager_ = std::make_unique(disk_manager_.get(), buffer_pool_manager_.get()); 14 | std::unique_ptr ix_manager_ = std::make_unique(disk_manager_.get(), buffer_pool_manager_.get()); 15 | std::unique_ptr sm_manager_ = 16 | std::make_unique(disk_manager_.get(), buffer_pool_manager_.get(), rm_manager_.get(), ix_manager_.get()); 17 | 18 | std::unique_ptr ql_manager_ = std::make_unique(sm_manager_.get()); 19 | 20 | std::unique_ptr interp_ = std::make_unique(sm_manager_.get(), ql_manager_.get()); 21 | 22 | char *result = new char[BUFFER_LENGTH]; 23 | int offset; 24 | 25 | char *exec_sql(const std::string &sql) { 26 | std::cout << "rucbase> " + sql << std::endl; 27 | YY_BUFFER_STATE yy_buffer = yy_scan_string(sql.c_str()); 28 | assert(yyparse() == 0 && ast::parse_tree != nullptr); 29 | yy_delete_buffer(yy_buffer); 30 | memset(result, 0, BUFFER_LENGTH); 31 | offset = 0; 32 | Context *context = new Context(nullptr, nullptr, new Transaction(0), result, &offset); 33 | interp_->interp_sql(ast::parse_tree, context); // 主要执行逻辑 34 | // std::cout << result << std::endl; 35 | return result; 36 | }; 37 | 38 | int main(int argc, char *argv[]) { 39 | if (argc == 2) { 40 | if (!sm_manager_->is_dir(db_name_)) { 41 | sm_manager_->create_db(db_name_); 42 | } 43 | sm_manager_->open_db(db_name_); 44 | std::cout << exec_sql(argv[1]) << std::endl; 45 | } 46 | sm_manager_->close_db(); 47 | } 48 | -------------------------------------------------------------------------------- /src/execution/execution.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "execution_defs.h" 4 | #include "execution_manager.h" 5 | -------------------------------------------------------------------------------- /src/execution/execution_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #include "errors.h" 5 | -------------------------------------------------------------------------------- /src/execution/execution_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "execution_defs.h" 10 | #include "record/rm.h" 11 | #include "system/sm.h" 12 | #include "common/context.h" 13 | 14 | struct TabCol { 15 | std::string tab_name; 16 | std::string col_name; 17 | 18 | friend bool operator<(const TabCol &x, const TabCol &y) { 19 | return std::make_pair(x.tab_name, x.col_name) < std::make_pair(y.tab_name, y.col_name); 20 | } 21 | }; 22 | 23 | struct Value { 24 | ColType type; // type of value 25 | union { 26 | int int_val; // int value 27 | float float_val; // float value 28 | }; 29 | std::string str_val; // string value 30 | 31 | std::shared_ptr raw; // raw record buffer 32 | 33 | void set_int(int int_val_) { 34 | type = TYPE_INT; 35 | int_val = int_val_; 36 | } 37 | 38 | void set_float(float float_val_) { 39 | type = TYPE_FLOAT; 40 | float_val = float_val_; 41 | } 42 | 43 | void set_str(std::string str_val_) { 44 | type = TYPE_STRING; 45 | str_val = std::move(str_val_); 46 | } 47 | 48 | void init_raw(int len) { 49 | assert(raw == nullptr); 50 | raw = std::make_shared(len); 51 | if (type == TYPE_INT) { 52 | assert(len == sizeof(int)); 53 | *(int *)(raw->data) = int_val; 54 | } else if (type == TYPE_FLOAT) { 55 | assert(len == sizeof(float)); 56 | *(float *)(raw->data) = float_val; 57 | } else if (type == TYPE_STRING) { 58 | if (len < (int)str_val.size()) { 59 | throw StringOverflowError(); 60 | } 61 | memset(raw->data, 0, len); 62 | memcpy(raw->data, str_val.c_str(), str_val.size()); 63 | } 64 | } 65 | }; 66 | 67 | enum CompOp { OP_EQ, OP_NE, OP_LT, OP_GT, OP_LE, OP_GE }; 68 | 69 | struct Condition { 70 | TabCol lhs_col; // left-hand side column 71 | CompOp op; // comparison operator 72 | bool is_rhs_val; // true if right-hand side is a value (not a column) 73 | TabCol rhs_col; // right-hand side column 74 | Value rhs_val; // right-hand side value 75 | }; 76 | 77 | struct SetClause { 78 | TabCol lhs; 79 | Value rhs; 80 | }; 81 | 82 | class QlManager { 83 | private: 84 | SmManager *sm_manager_; 85 | 86 | public: 87 | QlManager(SmManager *sm_manager) : sm_manager_(sm_manager) {} 88 | 89 | void insert_into(const std::string &tab_name, std::vector values, Context *context); 90 | 91 | void delete_from(const std::string &tab_name, std::vector conds, Context *context); 92 | 93 | void update_set(const std::string &tab_name, std::vector set_clauses, 94 | std::vector conds, Context *context); 95 | 96 | void select_from(std::vector sel_cols, const std::vector &tab_names, 97 | std::vector conds, Context *context); 98 | 99 | private: 100 | TabCol check_column(const std::vector &all_cols, TabCol target); 101 | std::vector get_all_cols(const std::vector &tab_names); 102 | std::vector check_where_clause(const std::vector &tab_names, 103 | const std::vector &conds); 104 | int get_indexNo(std::string tab_name, std::vector curr_conds); 105 | }; 106 | -------------------------------------------------------------------------------- /src/execution/executor_abstract.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "execution_defs.h" 4 | #include "execution_manager.h" 5 | #include "index/ix.h" 6 | #include "system/sm.h" 7 | 8 | class AbstractExecutor { 9 | public: 10 | Rid _abstract_rid; 11 | 12 | Context *context_; 13 | 14 | virtual ~AbstractExecutor() = default; 15 | 16 | virtual size_t tupleLen() const { return 0; }; 17 | 18 | virtual const std::vector &cols() const { 19 | std::vector *_cols = nullptr; 20 | return *_cols; 21 | }; 22 | 23 | virtual std::string getType() { return "AbstractExecutor"; }; 24 | 25 | virtual void beginTuple(){}; 26 | 27 | virtual void nextTuple(){}; 28 | 29 | virtual bool is_end() const { return true; }; 30 | 31 | virtual Rid &rid() = 0; 32 | 33 | virtual std::unique_ptr Next() = 0; 34 | 35 | virtual void feed(const std::map &feed_dict){}; 36 | 37 | std::vector::const_iterator get_col(const std::vector &rec_cols, const TabCol &target) { 38 | auto pos = std::find_if(rec_cols.begin(), rec_cols.end(), [&](const ColMeta &col) { 39 | return col.tab_name == target.tab_name && col.name == target.col_name; 40 | }); 41 | if (pos == rec_cols.end()) { 42 | throw ColumnNotFoundError(target.tab_name + '.' + target.col_name); 43 | } 44 | return pos; 45 | } 46 | 47 | std::map rec2dict(const std::vector &cols, const RmRecord *rec) { 48 | std::map rec_dict; 49 | for (auto &col : cols) { 50 | TabCol key = {.tab_name = col.tab_name, .col_name = col.name}; 51 | Value val; 52 | char *val_buf = rec->data + col.offset; 53 | if (col.type == TYPE_INT) { 54 | val.set_int(*(int *)val_buf); 55 | } else if (col.type == TYPE_FLOAT) { 56 | val.set_float(*(float *)val_buf); 57 | } else if (col.type == TYPE_STRING) { 58 | std::string str_val((char *)val_buf, col.len); 59 | str_val.resize(strlen(str_val.c_str())); 60 | val.set_str(str_val); 61 | } 62 | assert(rec_dict.count(key) == 0); 63 | val.init_raw(col.len); 64 | rec_dict[key] = val; 65 | } 66 | return rec_dict; 67 | } 68 | }; -------------------------------------------------------------------------------- /src/execution/executor_delete.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "execution_defs.h" 3 | #include "execution_manager.h" 4 | #include "executor_abstract.h" 5 | #include "index/ix.h" 6 | #include "system/sm.h" 7 | 8 | class DeleteExecutor : public AbstractExecutor { 9 | private: 10 | TabMeta tab_; 11 | std::vector conds_; 12 | RmFileHandle *fh_; 13 | std::vector rids_; 14 | std::string tab_name_; 15 | SmManager *sm_manager_; 16 | 17 | public: 18 | DeleteExecutor(SmManager *sm_manager, const std::string &tab_name, std::vector conds, 19 | std::vector rids, Context *context) { 20 | sm_manager_ = sm_manager; 21 | tab_name_ = tab_name; 22 | tab_ = sm_manager_->db_.get_table(tab_name); 23 | fh_ = sm_manager_->fhs_.at(tab_name).get(); 24 | conds_ = conds; 25 | rids_ = rids; 26 | context_ = context; 27 | } 28 | std::unique_ptr Next() override { 29 | // Get all index files 30 | std::vector ihs(tab_.cols.size(), nullptr); 31 | for (size_t col_i = 0; col_i < tab_.cols.size(); col_i++) { 32 | if (tab_.cols[col_i].index) { 33 | // lab3 task3 Todo 34 | // 获取需要的索引句柄,填充vector ihs 35 | // lab3 task3 Todo end 36 | ihs[col_i] = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, col_i)).get(); 37 | } 38 | else ihs[col_i]=nullptr; 39 | } 40 | // Delete each rid from record file and index file 41 | for (auto &rid : rids_) { 42 | auto rec = fh_->get_record(rid, context_); 43 | // lab3 task3 Todo 44 | // Delete from index file 45 | // Delete from record file 46 | // lab3 task3 Todo end 47 | 48 | // Delete from index file 49 | for (long unsigned int i = 0; i < tab_.cols.size(); i++) { 50 | if (tab_.cols[i].index) { 51 | auto ifh = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, i)).get();;//index file handle 52 | ifh->delete_entry(rec->data + tab_.cols[i].offset,context_->txn_); 53 | } 54 | } 55 | // Delete from record file 56 | fh_->delete_record(rid, context_); 57 | 58 | // record a delete operation into the transaction 59 | WriteRecord* wr= new WriteRecord(WType::DELETE_TUPLE, tab_name_,rid, *rec); 60 | context_->txn_->AppendWriteRecord(wr); 61 | } 62 | return nullptr; 63 | } 64 | Rid &rid() override { return _abstract_rid; } 65 | }; -------------------------------------------------------------------------------- /src/execution/executor_insert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "execution_defs.h" 3 | #include "execution_manager.h" 4 | #include "executor_abstract.h" 5 | #include "index/ix.h" 6 | #include "system/sm.h" 7 | 8 | class InsertExecutor : public AbstractExecutor { 9 | private: 10 | TabMeta tab_; 11 | std::vector values_; 12 | RmFileHandle *fh_; 13 | std::string tab_name_; 14 | Rid rid_; 15 | SmManager *sm_manager_; 16 | 17 | public: 18 | InsertExecutor(SmManager *sm_manager, const std::string &tab_name, std::vector values, Context *context) { 19 | sm_manager_ = sm_manager; 20 | tab_ = sm_manager_->db_.get_table(tab_name); 21 | values_ = values; 22 | tab_name_ = tab_name; 23 | if (values.size() != tab_.cols.size()) { 24 | throw InvalidValueCountError(); 25 | } 26 | // Get record file handle 27 | fh_ = sm_manager_->fhs_.at(tab_name).get(); 28 | context_ = context; 29 | }; 30 | 31 | std::unique_ptr Next() override { 32 | // lab3 task3 Todo 33 | // Make record buffer 34 | // Insert into record file 35 | // Insert into index 36 | // lab3 task3 Todo end 37 | 38 | 39 | // Make record buffer 40 | RmRecord rec(fh_->get_file_hdr().record_size); 41 | 42 | for (size_t i = 0; i < values_.size(); i++) { 43 | auto &col = tab_.cols[i]; 44 | auto &val = values_[i]; 45 | if (col.type != val.type) { 46 | throw IncompatibleTypeError(coltype2str(col.type), coltype2str(val.type)); 47 | } 48 | val.init_raw(col.len); 49 | memcpy(rec.data + col.offset, val.raw->data, col.len); 50 | } 51 | // Insert into record file 52 | rid_ = fh_->insert_record(rec.data, context_); 53 | // Transaction insert 54 | WriteRecord* wr= new WriteRecord(WType::INSERT_TUPLE, tab_name_, rid_); 55 | context_->txn_->AppendWriteRecord(wr); 56 | // Insert into index 57 | for (size_t i = 0; i < tab_.cols.size(); i++) { 58 | auto &col = tab_.cols[i]; 59 | if (col.index) { 60 | auto ih = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, i)).get(); 61 | ih->insert_entry(rec.data + col.offset, rid_,context_->txn_); 62 | } 63 | } 64 | return std::make_unique(rec); 65 | } 66 | Rid &rid() override { return rid_; } 67 | }; -------------------------------------------------------------------------------- /src/execution/executor_nestedloop_join.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "execution_defs.h" 3 | #include "execution_manager.h" 4 | #include "executor_abstract.h" 5 | #include "index/ix.h" 6 | #include "system/sm.h" 7 | 8 | class NestedLoopJoinExecutor : public AbstractExecutor { 9 | private: 10 | std::unique_ptr left_; 11 | std::unique_ptr right_; 12 | size_t len_; 13 | std::vector cols_; 14 | 15 | std::map prev_feed_dict_; 16 | 17 | public: 18 | NestedLoopJoinExecutor(std::unique_ptr left, std::unique_ptr right) { 19 | // 设置左右孩子 20 | left_ = std::move(left); 21 | right_ = std::move(right); 22 | // 得到连接(笛卡尔积)结果元组的长度 23 | len_ = left_->tupleLen() + right_->tupleLen(); 24 | // 默认以左孩子作为outer table 25 | cols_ = left_->cols(); 26 | // inner table col 27 | auto right_cols = right_->cols(); 28 | for (auto &col : right_cols) { 29 | col.offset += left_->tupleLen(); 30 | } 31 | cols_.insert(cols_.end(), right_cols.begin(), right_cols.end()); 32 | } 33 | 34 | std::string getType() override { return "Join"; } 35 | 36 | size_t tupleLen() const override { return len_; } 37 | 38 | const std::vector &cols() const override { return cols_; } 39 | 40 | void beginTuple() override { 41 | left_->beginTuple(); 42 | if (left_->is_end()) { 43 | return; 44 | } 45 | feed_right(); 46 | right_->beginTuple(); 47 | while (right_->is_end()) { 48 | // 此处任务点取消,不用做 49 | // lab3 task2 Todo 50 | // 如果当前innerTable(右表或算子)扫描完了 51 | // 你需要移动到outerTable(左表)下一个记录,然后把右表移动到第一个记录的位置 52 | left_->nextTuple(); 53 | if (left_->is_end()) { 54 | break; 55 | } 56 | feed_right(); 57 | right_->beginTuple(); 58 | // lab3 task2 Todo end 59 | } 60 | } 61 | 62 | void nextTuple() override { 63 | assert(!is_end()); 64 | right_->nextTuple(); 65 | while (right_->is_end()) { 66 | // lab3 task2 Todo 67 | // 如果右节点扫描完了 68 | // 你需要把左表移动到下一个记录并把右节点回退到第一个记录 69 | // lab3 task2 Todo end 70 | left_->nextTuple(); 71 | if (left_->is_end()) { 72 | return; 73 | } 74 | feed_right(); 75 | right_->beginTuple(); 76 | } 77 | } 78 | 79 | bool is_end() const override { return left_->is_end(); } 80 | 81 | std::unique_ptr Next() override { 82 | assert(!is_end()); 83 | auto record = std::make_unique(len_); 84 | // lab3 task2 Todo 85 | // 你需要调用左右算子的Next()获取下一个记录进行拼接赋给返回的连接结果std::make_uniquerecord中 86 | // memecpy()可能对你有所帮助 87 | // lab3 task2 Todo End 88 | std::unique_ptr left_record = left_->Next(); 89 | std::unique_ptr right_record = right_->Next(); 90 | memcpy(record->data, left_record->data, left_->tupleLen()); 91 | memcpy(record->data + left_->tupleLen(), right_record->data, right_->tupleLen()); 92 | return record; 93 | } 94 | 95 | // 递归更新条件谓词 96 | void feed(const std::map &feed_dict) override { 97 | prev_feed_dict_ = feed_dict; 98 | left_->feed(feed_dict); 99 | } 100 | 101 | // 默认以right 作inner table 102 | void feed_right() { 103 | // 将左子算子的ColMeta数组和对应的下一个元组转换成的map 104 | // 每一个表列和其对应的Value相对应 105 | auto left_dict = rec2dict(left_->cols(), left_->Next().get()); 106 | auto feed_dict = prev_feed_dict_; 107 | // 将左子算子的<列,值>map中的KV对增加到prev_feed_dict_(feed_dict)中 108 | feed_dict.insert(left_dict.begin(), left_dict.end()); 109 | // 右子算子调用feed,对当前的feed_dict 110 | // 重置右子算子的连接条件 111 | right_->feed(feed_dict); 112 | } 113 | 114 | Rid &rid() override { return _abstract_rid; } 115 | }; -------------------------------------------------------------------------------- /src/execution/executor_projection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "execution_defs.h" 3 | #include "execution_manager.h" 4 | #include "executor_abstract.h" 5 | #include "index/ix.h" 6 | #include "system/sm.h" 7 | 8 | class ProjectionExecutor : public AbstractExecutor { 9 | private: 10 | std::unique_ptr prev_; 11 | std::vector cols_; 12 | size_t len_; 13 | std::vector sel_idxs_; 14 | 15 | public: 16 | ProjectionExecutor(std::unique_ptr prev, const std::vector &sel_cols) { 17 | prev_ = std::move(prev); 18 | 19 | size_t curr_offset = 0; 20 | auto &prev_cols = prev_->cols(); 21 | for (auto &sel_col : sel_cols) { 22 | auto pos = get_col(prev_cols, sel_col); 23 | sel_idxs_.push_back(pos - prev_cols.begin()); 24 | auto col = *pos; 25 | col.offset = curr_offset; 26 | curr_offset += col.len; 27 | cols_.push_back(col); 28 | } 29 | len_ = curr_offset; 30 | } 31 | 32 | std::string getType() override { return "Projection"; } 33 | 34 | size_t tupleLen() const override { return len_; } 35 | 36 | const std::vector &cols() const override { return cols_; } 37 | 38 | void beginTuple() override { prev_->beginTuple(); } 39 | 40 | void nextTuple() override { 41 | if(prev_->is_end()) 42 | { 43 | return; 44 | } 45 | prev_->nextTuple(); 46 | } 47 | 48 | bool is_end() const override { return prev_->is_end(); } 49 | 50 | std::unique_ptr Next() override { 51 | assert(!is_end()); 52 | auto &prev_cols = prev_->cols(); 53 | auto prev_rec = prev_->Next(); 54 | auto &proj_cols = cols_; 55 | auto proj_rec = std::make_unique(len_); 56 | for (size_t proj_idx = 0; proj_idx < proj_cols.size(); proj_idx++) { 57 | size_t prev_idx = sel_idxs_[proj_idx]; 58 | auto &prev_col = prev_cols[prev_idx]; 59 | auto &proj_col = proj_cols[proj_idx]; 60 | // lab3 task2 Todo 61 | // 利用memcpy生成proj_rec 62 | // lab3 task2 Todo End 63 | memcpy(proj_rec->data + proj_col.offset, prev_rec->data + prev_col.offset, proj_col.len); 64 | } 65 | return proj_rec; 66 | } 67 | 68 | void feed(const std::map &feed_dict) override { 69 | throw InternalError("Cannot feed a projection node"); 70 | } 71 | Rid &rid() override { return _abstract_rid; } 72 | }; -------------------------------------------------------------------------------- /src/execution/executor_update.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "execution_defs.h" 3 | #include "execution_manager.h" 4 | #include "executor_abstract.h" 5 | #include "index/ix.h" 6 | #include "system/sm.h" 7 | 8 | class UpdateExecutor : public AbstractExecutor { 9 | private: 10 | TabMeta tab_; 11 | std::vector conds_; 12 | RmFileHandle *fh_; 13 | std::vector rids_; 14 | std::string tab_name_; 15 | std::vector set_clauses_; 16 | SmManager *sm_manager_; 17 | 18 | public: 19 | UpdateExecutor(SmManager *sm_manager, const std::string &tab_name, std::vector set_clauses, 20 | std::vector conds, std::vector rids, Context *context) { 21 | sm_manager_ = sm_manager; 22 | tab_name_ = tab_name; 23 | set_clauses_ = set_clauses; 24 | tab_ = sm_manager_->db_.get_table(tab_name); 25 | fh_ = sm_manager_->fhs_.at(tab_name).get(); 26 | conds_ = conds; 27 | rids_ = rids; 28 | context_ = context; 29 | } 30 | std::unique_ptr Next() override { 31 | // Get all necessary index files 32 | std::vector ihs(tab_.cols.size(), nullptr); 33 | for (auto &set_clause : set_clauses_) { 34 | auto lhs_col = tab_.get_col(set_clause.lhs.col_name); 35 | if (lhs_col->index) { 36 | size_t lhs_col_idx = lhs_col - tab_.cols.begin(); 37 | // lab3 task3 Todo 38 | // 获取需要的索引句柄,填充vector ihs 39 | // lab3 task3 Todo end 40 | ihs[lhs_col_idx] = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, lhs_col_idx)).get();; 41 | } 42 | } 43 | // Update each rid from record file and index file 44 | for (auto &rid : rids_) { 45 | auto rec = fh_->get_record(rid, context_); 46 | // lab3 task3 Todo 47 | // Remove old entry from index 48 | // lab3 task3 Todo end 49 | // Remove old entry from index 50 | //before update 51 | WriteRecord* wr= new WriteRecord(WType::UPDATE_TUPLE, tab_name_, rid,*rec); 52 | context_->txn_->AppendWriteRecord(wr); 53 | 54 | for (long unsigned int i = 0; i < tab_.cols.size(); i++) { 55 | if (tab_.cols[i].index) { 56 | auto ifh = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, i)).get();//index file handle 57 | ifh->delete_entry(rec->data + tab_.cols[i].offset,context_->txn_); 58 | } 59 | } 60 | 61 | // record a update operation into the transaction 62 | // RmRecord update_record{rec->size}; 63 | // memcpy(update_record.data, rec->data, rec->size); 64 | 65 | // lab3 task3 Todo 66 | // Update record in record file 67 | // lab3 task3 Todo end 68 | // Update record in record file 69 | for (auto &set_clause : set_clauses_) { 70 | auto lhs_col = tab_.get_col(set_clause.lhs.col_name); 71 | // size_t lhs_col_idx = lhs_col - tab_.cols.begin(); 72 | // lab3 task3 Todo 73 | // Update record in record file 74 | // lab3 task3 Todo end 75 | // Update record in record file 76 | // printf("In update,update: %s\n", set_clause.rhs.raw->data); 77 | // printf("ori:%s\n", rec->data + lhs_col->offset); 78 | // printf("offset:%d\n", lhs_col->offset); 79 | // printf("len:%d\n", lhs_col->len); 80 | memcpy(rec->data + lhs_col->offset, set_clause.rhs.raw->data, lhs_col->len); 81 | // printf("after:%d\n", *(rec->data)); 82 | // printf("after:%s\n", rec->data + lhs_col->offset); 83 | } 84 | fh_->update_record(rid, rec->data, context_); 85 | 86 | // lab3 task3 Todo 87 | // Insert new entry into index 88 | // lab3 task3 Todo end 89 | // Insert new entry into index 90 | for (long unsigned int i = 0; i < tab_.cols.size(); i++) { 91 | if (tab_.cols[i].index) { 92 | auto ifh = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, i)).get();//index file handle 93 | ifh->insert_entry(rec->data + tab_.cols[i].offset,rid,context_->txn_); 94 | } 95 | } 96 | 97 | // return rec; 98 | } 99 | return nullptr; 100 | } 101 | Rid &rid() override { return _abstract_rid; } 102 | }; -------------------------------------------------------------------------------- /src/execution/input_all_task.sql: -------------------------------------------------------------------------------- 1 | create table student (id int, name char(32), major char(32)); 2 | create index student (id); 3 | create table grade (course char(32), student_id int, score float); 4 | create index grade (student_id); 5 | show tables; 6 | desc student; 7 | insert into student values (1, 'Tom', 'Computer Science'); 8 | insert into student values (2, 'Jerry', 'Computer Science'); 9 | insert into student values (3, 'Jack', 'Electrical Engineering'); 10 | select * from student where id>=1; 11 | update student set major = 'Electrical Engineering' where id = 2; 12 | select * from student where id>=1; 13 | delete from student where name = 'Jack'; 14 | select * from student where id>=1; 15 | insert into grade values ('Data Structure', 1, 90.0); 16 | insert into grade values ('Data Structure', 2, 95.0); 17 | insert into grade values ('Calculus', 2, 82.0); 18 | insert into grade values ('Calculus', 1, 88.5); 19 | select * from student, grade; 20 | select id, name, major, course, score from student, grade where student.id = grade.student_id; 21 | select id, name, major, course, score from student join grade where student.id = grade.student_id; 22 | drop index student (id); 23 | desc student; 24 | drop table student; 25 | drop table grade; 26 | show tables; 27 | # -------------------------------------------------------------------------------- /src/execution/input_task1.sql: -------------------------------------------------------------------------------- 1 | show tables; 2 | create table tb(s int, a int, b float, c char(16)); 3 | create table tb2(x int, y float, z char(16), s int); 4 | show tables; 5 | desc tb; 6 | drop table tb; 7 | show tables; 8 | drop table tb2; 9 | show tables; 10 | # -------------------------------------------------------------------------------- /src/execution/input_task2.sql: -------------------------------------------------------------------------------- 1 | select * from student; 2 | select * from grade; 3 | select * from student where id>=2; 4 | select * from student, grade; 5 | select id, name, major, course from student, grade where student.id = grade.student_id; 6 | # -------------------------------------------------------------------------------- /src/execution/input_task3.sql: -------------------------------------------------------------------------------- 1 | select * from student; 2 | update student set major = 'Electrical Engineering' where id = 2; 3 | select * from student; 4 | delete from student where name = 'Jack'; 5 | select * from student; 6 | update student set major = 'Computer Science' where id = 2; 7 | select * from student; 8 | insert into student values (3, 'Jack', 'Electrical Engineering'); 9 | select * from student; 10 | -------------------------------------------------------------------------------- /src/execution/res_task1_output.txt: -------------------------------------------------------------------------------- 1 | >> show tables; 2 | rucbase> show tables; 3 | +------------------+ 4 | | Tables | 5 | +------------------+ 6 | +------------------+ 7 | 8 | ------------------------------ 9 | >> create table tb(s int, a int, b float, c char(16)); 10 | rucbase> create table tb(s int, a int, b float, c char(16)); 11 | 12 | ------------------------------ 13 | >> create table tb2(x int, y float, z char(16), s int); 14 | rucbase> create table tb2(x int, y float, z char(16), s int); 15 | 16 | ------------------------------ 17 | >> show tables; 18 | rucbase> show tables; 19 | +------------------+ 20 | | Tables | 21 | +------------------+ 22 | | tb | 23 | | tb2 | 24 | +------------------+ 25 | 26 | ------------------------------ 27 | >> desc tb; 28 | rucbase> desc tb; 29 | +------------------+------------------+------------------+ 30 | | Field | Type | Index | 31 | +------------------+------------------+------------------+ 32 | | s | INT | NO | 33 | | a | INT | NO | 34 | | b | FLOAT | NO | 35 | | c | STRING | NO | 36 | +------------------+------------------+------------------+ 37 | 38 | ------------------------------ 39 | >> drop table tb; 40 | rucbase> drop table tb; 41 | 42 | ------------------------------ 43 | >> show tables; 44 | rucbase> show tables; 45 | +------------------+ 46 | | Tables | 47 | +------------------+ 48 | | tb2 | 49 | +------------------+ 50 | 51 | ------------------------------ 52 | >> drop table tb2; 53 | rucbase> drop table tb2; 54 | 55 | ------------------------------ 56 | >> show tables; 57 | rucbase> show tables; 58 | +------------------+ 59 | | Tables | 60 | +------------------+ 61 | +------------------+ 62 | 63 | ------------------------------ 64 | -------------------------------------------------------------------------------- /src/execution/res_task2_output.txt: -------------------------------------------------------------------------------- 1 | >> select * from student; 2 | rucbase> select * from student; 3 | +------------------+------------------+------------------+ 4 | | id | name | major | 5 | +------------------+------------------+------------------+ 6 | | 1 | Tom | Computer Science | 7 | | 2 | Jerry | Computer Science | 8 | | 3 | Jack | Electrical En... | 9 | +------------------+------------------+------------------+ 10 | Total record(s): 3 11 | 12 | ------------------------------ 13 | >> select * from grade; 14 | rucbase> select * from grade; 15 | +------------------+------------------+------------------+ 16 | | course | student_id | score | 17 | +------------------+------------------+------------------+ 18 | | Data Structure | 1 | 90.000000 | 19 | | Data Structure | 2 | 95.000000 | 20 | | Calculus | 2 | 82.000000 | 21 | | Calculus | 1 | 88.500000 | 22 | +------------------+------------------+------------------+ 23 | Total record(s): 4 24 | 25 | ------------------------------ 26 | >> select * from student where id>=2; 27 | rucbase> select * from student where id>=2; 28 | +------------------+------------------+------------------+ 29 | | id | name | major | 30 | +------------------+------------------+------------------+ 31 | | 2 | Jerry | Computer Science | 32 | | 3 | Jack | Electrical En... | 33 | +------------------+------------------+------------------+ 34 | Total record(s): 2 35 | 36 | ------------------------------ 37 | >> select * from student, grade; 38 | rucbase> select * from student, grade; 39 | +------------------+------------------+------------------+------------------+------------------+------------------+ 40 | | id | name | major | course | student_id | score | 41 | +------------------+------------------+------------------+------------------+------------------+------------------+ 42 | | 1 | Tom | Computer Science | Data Structure | 1 | 90.000000 | 43 | | 1 | Tom | Computer Science | Data Structure | 2 | 95.000000 | 44 | | 1 | Tom | Computer Science | Calculus | 2 | 82.000000 | 45 | | 1 | Tom | Computer Science | Calculus | 1 | 88.500000 | 46 | | 2 | Jerry | Computer Science | Data Structure | 1 | 90.000000 | 47 | | 2 | Jerry | Computer Science | Data Structure | 2 | 95.000000 | 48 | | 2 | Jerry | Computer Science | Calculus | 2 | 82.000000 | 49 | | 2 | Jerry | Computer Science | Calculus | 1 | 88.500000 | 50 | | 3 | Jack | Electrical En... | Data Structure | 1 | 90.000000 | 51 | | 3 | Jack | Electrical En... | Data Structure | 2 | 95.000000 | 52 | | 3 | Jack | Electrical En... | Calculus | 2 | 82.000000 | 53 | | 3 | Jack | Electrical En... | Calculus | 1 | 88.500000 | 54 | +------------------+------------------+------------------+------------------+------------------+------------------+ 55 | Total record(s): 12 56 | 57 | ------------------------------ 58 | >> select id, name, major, course from student, grade where student.id = grade.student_id; 59 | rucbase> select id, name, major, course from student, grade where student.id = grade.student_id; 60 | +------------------+------------------+------------------+------------------+ 61 | | id | name | major | course | 62 | +------------------+------------------+------------------+------------------+ 63 | | 1 | Tom | Computer Science | Data Structure | 64 | | 1 | Tom | Computer Science | Calculus | 65 | | 2 | Jerry | Computer Science | Data Structure | 66 | | 2 | Jerry | Computer Science | Calculus | 67 | +------------------+------------------+------------------+------------------+ 68 | Total record(s): 4 69 | 70 | ------------------------------ 71 | -------------------------------------------------------------------------------- /src/execution/res_task3_output.txt: -------------------------------------------------------------------------------- 1 | >> select * from student; 2 | rucbase> select * from student; 3 | +------------------+------------------+------------------+ 4 | | id | name | major | 5 | +------------------+------------------+------------------+ 6 | | 1 | Tom | Computer Science | 7 | | 2 | Jerry | Computer Science | 8 | | 3 | Jack | Electrical En... | 9 | +------------------+------------------+------------------+ 10 | Total record(s): 3 11 | 12 | ------------------------------ 13 | >> update student set major = 'Electrical Engineering' where id = 2; 14 | rucbase> update student set major = 'Electrical Engineering' where id = 2; 15 | 16 | ------------------------------ 17 | >> select * from student; 18 | rucbase> select * from student; 19 | +------------------+------------------+------------------+ 20 | | id | name | major | 21 | +------------------+------------------+------------------+ 22 | | 1 | Tom | Computer Science | 23 | | 2 | Jerry | Electrical En... | 24 | | 3 | Jack | Electrical En... | 25 | +------------------+------------------+------------------+ 26 | Total record(s): 3 27 | 28 | ------------------------------ 29 | >> delete from student where name = 'Jack'; 30 | rucbase> delete from student where name = 'Jack'; 31 | 32 | ------------------------------ 33 | >> select * from student; 34 | rucbase> select * from student; 35 | +------------------+------------------+------------------+ 36 | | id | name | major | 37 | +------------------+------------------+------------------+ 38 | | 1 | Tom | Computer Science | 39 | | 2 | Jerry | Electrical En... | 40 | +------------------+------------------+------------------+ 41 | Total record(s): 2 42 | 43 | ------------------------------ 44 | >> update student set major = 'Computer Science' where id = 2; 45 | rucbase> update student set major = 'Computer Science' where id = 2; 46 | 47 | ------------------------------ 48 | >> select * from student; 49 | rucbase> select * from student; 50 | +------------------+------------------+------------------+ 51 | | id | name | major | 52 | +------------------+------------------+------------------+ 53 | | 1 | Tom | Computer Science | 54 | | 2 | Jerry | Computer Science | 55 | +------------------+------------------+------------------+ 56 | Total record(s): 2 57 | 58 | ------------------------------ 59 | >> insert into student values (3, 'Jack', 'Electrical Engineering'); 60 | rucbase> insert into student values (3, 'Jack', 'Electrical Engineering'); 61 | 62 | ------------------------------ 63 | >> select * from student; 64 | rucbase> select * from student; 65 | +------------------+------------------+------------------+ 66 | | id | name | major | 67 | +------------------+------------------+------------------+ 68 | | 1 | Tom | Computer Science | 69 | | 2 | Jerry | Computer Science | 70 | | 3 | Jack | Electrical En... | 71 | +------------------+------------------+------------------+ 72 | Total record(s): 3 73 | 74 | ------------------------------ 75 | -------------------------------------------------------------------------------- /src/execution/task1_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -r ExecutorTest_db 3 | rm output.txt 4 | cat input_task1.sql | while read line 5 | do 6 | if [ ${#line} -eq 0 ] || [ ${line:0:1} == "#" ] 7 | then 8 | echo "$line" 9 | continue 10 | fi 11 | echo ">> $line" 12 | ../../build/bin/exec_sql "$line" 13 | echo "------------------------------" 14 | done | tee -a output.txt 15 | echo "check different" 16 | diff res_task1_output.txt output.txt 17 | if [ $? != 0 ] 18 | then 19 | echo "Pass Failed!" 20 | else 21 | echo "Pass Success!" 22 | fi -------------------------------------------------------------------------------- /src/execution/task2_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -r ExecutorTest_db 3 | rm output.txt 4 | cp -r ExecutorTest_db_task2and3 ExecutorTest_db 5 | cat input_task2.sql | while read line 6 | do 7 | if [ ${#line} -eq 0 ] || [ ${line:0:1} == "#" ] 8 | then 9 | echo "$line" 10 | continue 11 | fi 12 | echo ">> $line" 13 | ../../build/bin/exec_sql "$line" 14 | echo "------------------------------" 15 | done | tee -a output.txt 16 | echo "check different" 17 | diff res_task2_output.txt output.txt 18 | if [ $? != 0 ] 19 | then 20 | echo "Pass Failed!" 21 | else 22 | echo "Pass Success!" 23 | fi -------------------------------------------------------------------------------- /src/execution/task3_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -r ExecutorTest_db 3 | rm output.txt 4 | cp -r ExecutorTest_db_task2and3 ExecutorTest_db 5 | cat input_task3.sql | while read line 6 | do 7 | if [ ${#line} -eq 0 ] || [ ${line:0:1} == "#" ] 8 | then 9 | echo "$line" 10 | continue 11 | fi 12 | echo ">> $line" 13 | ../../build/bin/exec_sql "$line" 14 | echo "------------------------------" 15 | done | tee -a output.txt 16 | echo "check different" 17 | diff res_task3_output.txt output.txt 18 | if [ $? != 0 ] 19 | then 20 | echo "Pass Failed!" 21 | else 22 | echo "Pass Success!" 23 | fi -------------------------------------------------------------------------------- /src/execution/taskall_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -r ExecutorTest_db 3 | rm output.txt 4 | cat input_all_task.sql | while read line 5 | do 6 | if [ ${#line} -eq 0 ] || [ ${line:0:1} == "#" ] 7 | then 8 | echo "$line" 9 | continue 10 | fi 11 | echo ">> $line" 12 | ../../build/bin/exec_sql "$line" 13 | echo "------------------------------" 14 | done | tee -a output.txt 15 | echo "check different" 16 | diff res_taskall_output.txt output.txt 17 | if [ $? != 0 ] 18 | then 19 | echo "Pass Failed!" 20 | else 21 | echo "Pass Success!" 22 | fi 23 | rm -r ExecutorTest_db -------------------------------------------------------------------------------- /src/index/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES ix_node_handle.cpp ix_index_handle.cpp ix_scan.cpp ../common/rwlatch.cpp) 2 | add_library(index STATIC ${SOURCES}) 3 | target_link_libraries(index storage) 4 | 5 | # insert test 6 | add_executable(b_plus_tree_insert_test b_plus_tree_insert_test.cpp) 7 | target_link_libraries(b_plus_tree_insert_test index gtest_main) 8 | 9 | # delete test 10 | add_executable(b_plus_tree_delete_test b_plus_tree_delete_test.cpp) 11 | target_link_libraries(b_plus_tree_delete_test index gtest_main) 12 | 13 | # concurrent insert and delete test 14 | add_executable(b_plus_tree_concurrent_test b_plus_tree_concurrent_test.cpp) 15 | target_link_libraries(b_plus_tree_concurrent_test index gtest_main) -------------------------------------------------------------------------------- /src/index/ix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ix_scan.h" 4 | #include "ix_manager.h" 5 | -------------------------------------------------------------------------------- /src/index/ix_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #include "storage/buffer_pool_manager.h" 5 | 6 | struct IxFileHdr { 7 | page_id_t first_free_page_no; 8 | int num_pages; // disk pages 9 | page_id_t root_page; // root page no 10 | ColType col_type; 11 | int col_len; // ColMeta->len 12 | int btree_order; // children per page 每个结点最多可插入的键值对数量 13 | int keys_size; // keys_size = (btree_order + 1) * col_len 14 | // first_leaf初始化之后没有进行修改,只不过是在测试文件中遍历叶子结点的时候用了 15 | page_id_t first_leaf; // 在上层IxManager的open函数进行初始化,初始化为root page_no 16 | page_id_t last_leaf; 17 | }; 18 | 19 | struct IxPageHdr { 20 | page_id_t next_free_page_no; 21 | page_id_t parent; // its parent's page_no 22 | int num_key; // # current keys (always equals to #child - 1) 已插入的keys数量,key_idx∈[0,num_key) 23 | bool is_leaf; 24 | page_id_t prev_leaf; // previous leaf node's page_no, effective only when is_leaf is true 25 | page_id_t next_leaf; // next leaf node's page_no, effective only when is_leaf is true 26 | }; 27 | 28 | // 这个其实和Rid结构类似 29 | struct Iid { 30 | int page_no; 31 | int slot_no; 32 | 33 | friend bool operator==(const Iid &x, const Iid &y) { return x.page_no == y.page_no && x.slot_no == y.slot_no; } 34 | 35 | friend bool operator!=(const Iid &x, const Iid &y) { return !(x == y); } 36 | }; 37 | 38 | constexpr int IX_NO_PAGE = -1; 39 | constexpr int IX_FILE_HDR_PAGE = 0; 40 | constexpr int IX_LEAF_HEADER_PAGE = 1; 41 | constexpr int IX_INIT_ROOT_PAGE = 2; 42 | constexpr int IX_INIT_NUM_PAGES = 3; 43 | constexpr int IX_MAX_COL_LEN = 512; 44 | -------------------------------------------------------------------------------- /src/index/ix_index_handle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ix_defs.h" 4 | #include "ix_node_handle.h" 5 | #include "transaction/transaction.h" 6 | 7 | enum class Operation { FIND = 0, INSERT, DELETE }; // 三种操作:查找、插入、删除 8 | 9 | /** 10 | * @brief B+树索引 11 | */ 12 | class IxIndexHandle { 13 | friend class IxScan; 14 | friend class IxManager; 15 | 16 | private: 17 | DiskManager *disk_manager_; 18 | BufferPoolManager *buffer_pool_manager_; 19 | int fd_; 20 | IxFileHdr file_hdr_; // 存了root_page,但root_page初始化为2(第0页存FILE_HDR_PAGE,第1页存LEAF_HEADER_PAGE) 21 | std::mutex root_latch_; // 用于索引并发(请自行选择并发粒度在 Tree级 或 Page级 ) 22 | 23 | public: 24 | IxIndexHandle(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager, int fd); 25 | 26 | // for search 27 | bool GetValue(const char *key, std::vector *result, Transaction *transaction); 28 | 29 | IxNodeHandle *FindLeafPage(const char *key, Operation operation, Transaction *transaction); 30 | 31 | // for insert 32 | bool insert_entry(const char *key, const Rid &value, Transaction *transaction); 33 | 34 | IxNodeHandle *Split(IxNodeHandle *node); 35 | 36 | void InsertIntoParent(IxNodeHandle *old_node, const char *key, IxNodeHandle *new_node, Transaction *transaction); 37 | 38 | // for delete 39 | bool delete_entry(const char *key, Transaction *transaction); 40 | 41 | bool CoalesceOrRedistribute(IxNodeHandle *node, Transaction *transaction = nullptr); 42 | 43 | bool AdjustRoot(IxNodeHandle *old_root_node); 44 | 45 | void Redistribute(IxNodeHandle *neighbor_node, IxNodeHandle *node, IxNodeHandle *parent, int index); 46 | 47 | bool Coalesce(IxNodeHandle **neighbor_node, IxNodeHandle **node, IxNodeHandle **parent, int index, 48 | Transaction *transaction); 49 | 50 | // 辅助函数,lab3执行层将使用 51 | Iid lower_bound(const char *key); 52 | 53 | Iid upper_bound(const char *key); 54 | 55 | Iid leaf_end() const; 56 | 57 | Iid leaf_begin() const; 58 | 59 | private: 60 | // 辅助函数 61 | void UpdateRootPageNo(page_id_t root) { file_hdr_.root_page = root; } 62 | 63 | bool IsEmpty() const { return file_hdr_.root_page == IX_NO_PAGE; } 64 | 65 | // for get/create node 66 | IxNodeHandle *FetchNode(int page_no) const; 67 | 68 | IxNodeHandle *CreateNode(); 69 | 70 | // for maintain data structure 71 | void maintain_parent(IxNodeHandle *node); 72 | 73 | void erase_leaf(IxNodeHandle *leaf); 74 | 75 | void release_node_handle(IxNodeHandle &node); 76 | 77 | void maintain_child(IxNodeHandle *node, int child_idx); 78 | 79 | // for index test 80 | Rid get_rid(const Iid &iid) const; 81 | }; 82 | -------------------------------------------------------------------------------- /src/index/ix_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "ix_defs.h" 7 | #include "ix_index_handle.h" 8 | 9 | class IxManager { 10 | private: 11 | DiskManager *disk_manager_; 12 | BufferPoolManager *buffer_pool_manager_; 13 | 14 | public: 15 | IxManager(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager) 16 | : disk_manager_(disk_manager), buffer_pool_manager_(buffer_pool_manager) {} 17 | 18 | std::string get_index_name(const std::string &filename, int index_no) { 19 | return filename + '.' + std::to_string(index_no) + ".idx"; 20 | } 21 | 22 | bool exists(const std::string &filename, int index_no) { 23 | auto ix_name = get_index_name(filename, index_no); 24 | return disk_manager_->is_file(ix_name); 25 | } 26 | 27 | void create_index(const std::string &filename, int index_no, ColType col_type, int col_len) { 28 | std::string ix_name = get_index_name(filename, index_no); 29 | assert(index_no >= 0); 30 | // Create index file 31 | disk_manager_->create_file(ix_name); 32 | // Open index file 33 | int fd = disk_manager_->open_file(ix_name); 34 | // Create file header and write to file 35 | // Theoretically we have: |page_hdr| + (|attr| + |rid|) * n <= PAGE_SIZE 36 | // but we reserve one slot for convenient inserting and deleting, i.e. 37 | // |page_hdr| + (|attr| + |rid|) * (n + 1) <= PAGE_SIZE 38 | if (col_len > IX_MAX_COL_LEN) { 39 | throw InvalidColLengthError(col_len); 40 | } 41 | // 根据 |page_hdr| + (|attr| + |rid|) * (n + 1) <= PAGE_SIZE 求得n的最大值btree_order 42 | // 即 n <= btree_order,那么btree_order就是每个结点最多可插入的键值对数量(实际还多留了一个空位,但其不可插入) 43 | int btree_order = static_cast((PAGE_SIZE - sizeof(IxPageHdr)) / (col_len + sizeof(Rid)) - 1); 44 | assert(btree_order > 2); 45 | // int key_offset = sizeof(IxPageHdr); 46 | // int rid_offset = key_offset + (btree_order + 1) * col_len; 47 | 48 | // Create file header and write to file 49 | IxFileHdr fhdr = { 50 | .first_free_page_no = IX_NO_PAGE, 51 | .num_pages = IX_INIT_NUM_PAGES, 52 | .root_page = IX_INIT_ROOT_PAGE, 53 | .col_type = col_type, 54 | .col_len = col_len, 55 | .btree_order = btree_order, 56 | // .key_offset = key_offset, 57 | // .rid_offset = rid_offset, 58 | .keys_size = (btree_order + 1) * col_len, // 用于IxNodeHandle初始化rids首地址 59 | .first_leaf = IX_INIT_ROOT_PAGE, 60 | .last_leaf = IX_INIT_ROOT_PAGE, 61 | }; 62 | disk_manager_->write_page(fd, IX_FILE_HDR_PAGE, (const char *)&fhdr, sizeof(fhdr)); 63 | 64 | char page_buf[PAGE_SIZE]; // 在内存中初始化page_buf中的内容,然后将其写入磁盘 65 | // 注意leaf header页号为1,也标记为叶子结点,其前一个/后一个叶子均指向root node 66 | // Create leaf list header page and write to file 67 | { 68 | auto phdr = reinterpret_cast(page_buf); 69 | *phdr = { 70 | .next_free_page_no = IX_NO_PAGE, 71 | .parent = IX_NO_PAGE, 72 | .num_key = 0, 73 | .is_leaf = true, 74 | .prev_leaf = IX_INIT_ROOT_PAGE, 75 | .next_leaf = IX_INIT_ROOT_PAGE, 76 | }; 77 | disk_manager_->write_page(fd, IX_LEAF_HEADER_PAGE, page_buf, PAGE_SIZE); 78 | } 79 | // 注意root node页号为2,也标记为叶子结点,其前一个/后一个叶子均指向leaf header 80 | // Create root node and write to file 81 | { 82 | auto phdr = reinterpret_cast(page_buf); 83 | *phdr = { 84 | .next_free_page_no = IX_NO_PAGE, 85 | .parent = IX_NO_PAGE, 86 | .num_key = 0, 87 | .is_leaf = true, 88 | .prev_leaf = IX_LEAF_HEADER_PAGE, 89 | .next_leaf = IX_LEAF_HEADER_PAGE, 90 | }; 91 | // Must write PAGE_SIZE here in case of future fetch_node() 92 | disk_manager_->write_page(fd, IX_INIT_ROOT_PAGE, page_buf, PAGE_SIZE); 93 | } 94 | 95 | disk_manager_->set_fd2pageno(fd, IX_INIT_NUM_PAGES - 1); // DEBUG 96 | 97 | // Close index file 98 | disk_manager_->close_file(fd); 99 | } 100 | 101 | void destroy_index(const std::string &filename, int index_no) { 102 | std::string ix_name = get_index_name(filename, index_no); 103 | disk_manager_->destroy_file(ix_name); 104 | } 105 | 106 | // 注意这里打开文件,创建并返回了index file handle的指针 107 | std::unique_ptr open_index(const std::string &filename, int index_no) { 108 | std::string ix_name = get_index_name(filename, index_no); 109 | int fd = disk_manager_->open_file(ix_name); 110 | return std::make_unique(disk_manager_, buffer_pool_manager_, fd); 111 | } 112 | 113 | void close_index(const IxIndexHandle *ih) { 114 | disk_manager_->write_page(ih->fd_, IX_FILE_HDR_PAGE, (const char *)&ih->file_hdr_, sizeof(ih->file_hdr_)); 115 | // 缓冲区的所有页刷到磁盘,注意这句话必须写在close_file前面 116 | buffer_pool_manager_->FlushAllPages(ih->fd_); 117 | disk_manager_->close_file(ih->fd_); 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /src/index/ix_node_handle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ix_defs.h" 3 | 4 | static const bool binary_search = true; // 控制在lower_bound/uppper_bound函数中是否使用二分查找 5 | 6 | /** 7 | * @brief 用于比较两个指针指向的数组(类型支持int*、float*、char*) 8 | */ 9 | inline int ix_compare(const char *a, const char *b, ColType type, int col_len) { 10 | switch (type) { 11 | case TYPE_INT: { 12 | int ia = *(int *)a; 13 | int ib = *(int *)b; 14 | return (ia < ib) ? -1 : ((ia > ib) ? 1 : 0); 15 | } 16 | case TYPE_FLOAT: { 17 | float fa = *(float *)a; 18 | float fb = *(float *)b; 19 | return (fa < fb) ? -1 : ((fa > fb) ? 1 : 0); 20 | } 21 | case TYPE_STRING: 22 | return memcmp(a, b, col_len); 23 | default: 24 | throw InternalError("Unexpected data type"); 25 | } 26 | } 27 | 28 | /** 29 | * @brief 树中的结点 30 | * 记录了root page,max size等;以及实现结点内部的查找/插入/删除操作 31 | * 可类比RmPageHandle 32 | */ 33 | class IxNodeHandle { 34 | friend class IxIndexHandle; 35 | friend class IxScan; 36 | 37 | private: 38 | const IxFileHdr *file_hdr; // 用到了file_hdr的keys_size, col_len 39 | Page *page; 40 | 41 | /** page->data的第一部分,指针指向首地址,后续占用长度为sizeof(IxPageHdr) */ 42 | IxPageHdr *page_hdr; 43 | /** page->data的第二部分,指针指向首地址,后续占用长度为file_hdr->keys_size,每个key的长度为file_hdr->col_len */ 44 | char *keys; 45 | /** page->data的第三部分,指针指向首地址,每个rid的长度为sizeof(Rid) */ 46 | Rid *rids; 47 | 48 | public: 49 | IxNodeHandle(const IxFileHdr *file_hdr_, Page *page_) : file_hdr(file_hdr_), page(page_) { 50 | page_hdr = reinterpret_cast(page->GetData()); 51 | keys = page->GetData() + sizeof(IxPageHdr); 52 | rids = reinterpret_cast(keys + file_hdr->keys_size); 53 | } 54 | 55 | IxNodeHandle() = default; 56 | 57 | /** 58 | * @brief 在当前node中查找第一个>=target的key_idx 59 | * 60 | * @return key_idx,范围为[0,num_key),如果返回的key_idx=num_key,则表示target大于最后一个key 61 | * @note 返回key index(同时也是rid index),作为slot no 62 | */ 63 | int lower_bound(const char *target) const; 64 | 65 | /** 66 | * @brief 在当前node中查找第一个>target的key_idx 67 | * 68 | * @return key_idx,范围为[1,num_key),如果返回的key_idx=num_key,则表示target大于等于最后一个key 69 | * @note 注意此处的范围从1开始 70 | */ 71 | int upper_bound(const char *target) const; 72 | 73 | bool LeafLookup(const char *key, Rid **value); 74 | 75 | page_id_t InternalLookup(const char *key); 76 | 77 | /** 78 | * @brief used in leaf node to insert (key,value) 79 | * 80 | * @return the size after Insert 81 | */ 82 | int Insert(const char *key, const Rid &value); 83 | 84 | /** 85 | * @brief used in leaf node to remove (key,value) which contains the key 86 | * 87 | * @return the size after Remove 88 | */ 89 | int Remove(const char *key); 90 | 91 | /** 92 | * @brief 将key的前n位插入到原来keys中的pos位置;将rid的前n位插入到原来rids中的pos位置 93 | * 94 | * @note [0,pos) [pos,num_key) 95 | * key_slot 96 | * [0,pos) [pos,pos+n) [pos+n,num_key+n) 97 | * key key_slot 98 | */ 99 | void insert_pairs(int pos, const char *key, const Rid *rid, int n); 100 | 101 | void insert_pair(int pos, const char *key, const Rid &rid); 102 | 103 | void erase_pair(int pos); 104 | 105 | /** 106 | * @brief 此函数由parent调用,寻找child 107 | * 108 | * @return 返回child在parent中的rid_idx∈[0,page_hdr->num_key) 109 | */ 110 | int find_child(IxNodeHandle *child); 111 | 112 | /** 以下为已经实现了的辅助函数 **/ 113 | char *get_key(int key_idx) const { return keys + key_idx * file_hdr->col_len; } 114 | 115 | Rid *get_rid(int rid_idx) const { return &rids[rid_idx]; } 116 | 117 | void set_key(int key_idx, const char *key) { memcpy(keys + key_idx * file_hdr->col_len, key, file_hdr->col_len); } 118 | 119 | void set_rid(int rid_idx, const Rid &rid) { rids[rid_idx] = rid; } 120 | 121 | int GetSize() { return page_hdr->num_key; } 122 | 123 | void SetSize(int size) { page_hdr->num_key = size; } 124 | 125 | int GetMaxSize() { return file_hdr->btree_order + 1; } 126 | 127 | int GetMinSize() { return GetMaxSize() / 2; } 128 | 129 | int KeyAt(int i) { return *(int *)get_key(i); } 130 | 131 | /** 132 | * @brief 得到第i个孩子结点的page_no 133 | */ 134 | page_id_t ValueAt(int i) { return get_rid(i)->page_no; } 135 | 136 | page_id_t GetPageNo() { return page->GetPageId().page_no; } 137 | 138 | PageId GetPageId() { return page->GetPageId(); } 139 | 140 | page_id_t GetNextLeaf() { return page_hdr->next_leaf; } 141 | 142 | page_id_t GetPrevLeaf() { return page_hdr->prev_leaf; } 143 | 144 | page_id_t GetParentPageNo() { return page_hdr->parent; } 145 | 146 | bool IsLeafPage() { return page_hdr->is_leaf; } 147 | 148 | bool IsRootPage() { return GetParentPageNo() == INVALID_PAGE_ID; } 149 | 150 | void SetNextLeaf(page_id_t page_no) { page_hdr->next_leaf = page_no; } 151 | 152 | void SetPrevLeaf(page_id_t page_no) { page_hdr->prev_leaf = page_no; } 153 | 154 | void SetParentPageNo(page_id_t parent) { page_hdr->parent = parent; } 155 | 156 | /** 157 | * @brief used in internal node to remove the last key in root node, and return the last child 158 | * 159 | * @return the last child 160 | */ 161 | page_id_t RemoveAndReturnOnlyChild(); 162 | }; -------------------------------------------------------------------------------- /src/index/ix_scan.cpp: -------------------------------------------------------------------------------- 1 | #include "ix_scan.h" 2 | 3 | /** 4 | * @brief 找到leaf page的下一个slot_no 5 | */ 6 | void IxScan::next() { 7 | assert(!is_end()); 8 | IxNodeHandle *node = ih_->FetchNode(iid_.page_no); 9 | assert(node->IsLeafPage()); 10 | assert(iid_.slot_no < node->GetSize()); 11 | // increment slot no 12 | iid_.slot_no++; 13 | if (iid_.page_no != ih_->file_hdr_.last_leaf && iid_.slot_no == node->GetSize()) { 14 | // go to next leaf 15 | iid_.slot_no = 0; 16 | iid_.page_no = node->GetNextLeaf(); 17 | } 18 | } 19 | 20 | Rid IxScan::rid() const { 21 | return ih_->get_rid(iid_); 22 | } 23 | -------------------------------------------------------------------------------- /src/index/ix_scan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ix_defs.h" 4 | #include "ix_index_handle.h" 5 | 6 | /** 7 | * @brief 用于直接遍历叶子结点,而不用FindLeafPage()来得到叶子结点 8 | */ 9 | class IxScan : public RecScan { 10 | const IxIndexHandle *ih_; 11 | Iid iid_; // 初始为lower(用于遍历的指针) 12 | Iid end_; // 初始为upper 13 | BufferPoolManager *bpm_; 14 | 15 | public: 16 | IxScan(const IxIndexHandle *ih, const Iid &lower, const Iid &upper, BufferPoolManager *bpm) 17 | : ih_(ih), iid_(lower), end_(upper), bpm_(bpm) {} 18 | 19 | void next() override; 20 | 21 | bool is_end() const override { return iid_ == end_; } 22 | 23 | Rid rid() const override; 24 | 25 | const Iid &iid() const { return iid_; } 26 | }; 27 | -------------------------------------------------------------------------------- /src/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # parser module 2 | find_package(BISON REQUIRED) 3 | find_package(FLEX REQUIRED) 4 | 5 | bison_target(yacc yacc.y ${CMAKE_CURRENT_SOURCE_DIR}/yacc.tab.cpp 6 | DEFINES_FILE ${CMAKE_CURRENT_SOURCE_DIR}/yacc.tab.h) 7 | flex_target(lex lex.l ${CMAKE_CURRENT_SOURCE_DIR}/lex.yy.cpp) 8 | add_flex_bison_dependency(lex yacc) 9 | 10 | set(SOURCES ${BISON_yacc_OUTPUT_SOURCE} ${FLEX_lex_OUTPUTS} ast.cpp) 11 | add_library(parser STATIC ${SOURCES}) 12 | 13 | -------------------------------------------------------------------------------- /src/parser/ast.cpp: -------------------------------------------------------------------------------- 1 | #include "ast.h" 2 | 3 | namespace ast { 4 | 5 | std::shared_ptr parse_tree; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/parser/ast_printer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ast.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ast { 9 | 10 | class TreePrinter { 11 | public: 12 | static void print(const std::shared_ptr &node) { 13 | print_node(node, 0); 14 | } 15 | 16 | private: 17 | static std::string offset2string(int offset) { 18 | return std::string(offset, ' '); 19 | } 20 | 21 | template 22 | static void print_val(const T &val, int offset) { 23 | std::cout << offset2string(offset) << val << '\n'; 24 | } 25 | 26 | template 27 | static void print_val_list(const std::vector &vals, int offset) { 28 | std::cout << offset2string(offset) << "LIST\n"; 29 | offset += 2; 30 | for (auto &val : vals) { 31 | print_val(val, offset); 32 | } 33 | } 34 | 35 | static std::string type2str(SvType type) { 36 | static std::map m{ 37 | {SV_TYPE_INT, "INT"}, 38 | {SV_TYPE_FLOAT, "FLOAT"}, 39 | {SV_TYPE_STRING, "STRING"}, 40 | }; 41 | return m.at(type); 42 | } 43 | 44 | static std::string op2str(SvCompOp op) { 45 | static std::map m{ 46 | {SV_OP_EQ, "=="}, 47 | {SV_OP_NE, "!="}, 48 | {SV_OP_LT, "<"}, 49 | {SV_OP_GT, ">"}, 50 | {SV_OP_LE, "<="}, 51 | {SV_OP_GE, ">="}, 52 | }; 53 | return m.at(op); 54 | } 55 | 56 | template 57 | static void print_node_list(std::vector nodes, int offset) { 58 | std::cout << offset2string(offset); 59 | offset += 2; 60 | std::cout << "LIST\n"; 61 | for (auto &node : nodes) { 62 | print_node(node, offset); 63 | } 64 | } 65 | 66 | static void print_node(const std::shared_ptr &node, int offset) { 67 | std::cout << offset2string(offset); 68 | offset += 2; 69 | if (auto x = std::dynamic_pointer_cast(node)) { 70 | std::cout << "HELP\n"; 71 | } else if (auto x = std::dynamic_pointer_cast(node)) { 72 | std::cout << "SHOW_TABLES\n"; 73 | } else if (auto x = std::dynamic_pointer_cast(node)) { 74 | std::cout << "CREATE_TABLE\n"; 75 | print_val(x->tab_name, offset); 76 | print_node_list(x->fields, offset); 77 | } else if (auto x = std::dynamic_pointer_cast(node)) { 78 | std::cout << "DROP_TABLE\n"; 79 | print_val(x->tab_name, offset); 80 | } else if (auto x = std::dynamic_pointer_cast(node)) { 81 | std::cout << "DESC_TABLE\n"; 82 | print_val(x->tab_name, offset); 83 | } else if (auto x = std::dynamic_pointer_cast(node)) { 84 | std::cout << "CREATE_INDEX\n"; 85 | print_val(x->tab_name, offset); 86 | print_val(x->col_name, offset); 87 | } else if (auto x = std::dynamic_pointer_cast(node)) { 88 | std::cout << "DROP_INDEX\n"; 89 | print_val(x->tab_name, offset); 90 | print_val(x->col_name, offset); 91 | } else if (auto x = std::dynamic_pointer_cast(node)) { 92 | std::cout << "COL_DEF\n"; 93 | print_val(x->col_name, offset); 94 | print_node(x->type_len, offset); 95 | } else if (auto x = std::dynamic_pointer_cast(node)) { 96 | std::cout << "COL\n"; 97 | print_val(x->tab_name, offset); 98 | print_val(x->col_name, offset); 99 | } else if (auto x = std::dynamic_pointer_cast(node)) { 100 | std::cout << "TYPE_LEN\n"; 101 | print_val(type2str(x->type), offset); 102 | print_val(x->len, offset); 103 | } else if (auto x = std::dynamic_pointer_cast(node)) { 104 | std::cout << "INT_LIT\n"; 105 | print_val(x->val, offset); 106 | } else if (auto x = std::dynamic_pointer_cast(node)) { 107 | std::cout << "FLOAT_LIT\n"; 108 | print_val(x->val, offset); 109 | } else if (auto x = std::dynamic_pointer_cast(node)) { 110 | std::cout << "STRING_LIT\n"; 111 | print_val(x->val, offset); 112 | } else if (auto x = std::dynamic_pointer_cast(node)) { 113 | std::cout << "SET_CLAUSE\n"; 114 | print_val(x->col_name, offset); 115 | print_node(x->val, offset); 116 | } else if (auto x = std::dynamic_pointer_cast(node)) { 117 | std::cout << "BINARY_EXPR\n"; 118 | print_node(x->lhs, offset); 119 | print_val(op2str(x->op), offset); 120 | print_node(x->rhs, offset); 121 | } else if (auto x = std::dynamic_pointer_cast(node)) { 122 | std::cout << "INSERT\n"; 123 | print_val(x->tab_name, offset); 124 | print_node_list(x->vals, offset); 125 | } else if (auto x = std::dynamic_pointer_cast(node)) { 126 | std::cout << "DELETE\n"; 127 | print_val(x->tab_name, offset); 128 | print_node_list(x->conds, offset); 129 | } else if (auto x = std::dynamic_pointer_cast(node)) { 130 | std::cout << "UPDATE\n"; 131 | print_val(x->tab_name, offset); 132 | print_node_list(x->set_clauses, offset); 133 | print_node_list(x->conds, offset); 134 | } else if (auto x = std::dynamic_pointer_cast(node)) { 135 | std::cout << "SELECT\n"; 136 | print_node_list(x->cols, offset); 137 | print_val_list(x->tabs, offset); 138 | print_node_list(x->conds, offset); 139 | } else if (auto x = std::dynamic_pointer_cast(node)) { 140 | std::cout << "BEGIN\n"; 141 | } else if (auto x = std::dynamic_pointer_cast(node)) { 142 | std::cout << "COMMIT\n"; 143 | } else if (auto x = std::dynamic_pointer_cast(node)) { 144 | std::cout << "ABORT\n"; 145 | } else if (auto x = std::dynamic_pointer_cast(node)) { 146 | std::cout << "ROLLBACK\n"; 147 | } else { 148 | assert(0); 149 | } 150 | } 151 | }; 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/parser/lex.l: -------------------------------------------------------------------------------- 1 | /* keywords are case insensitive */ 2 | %option caseless 3 | /* we don't need yywrap() function */ 4 | %option noyywrap 5 | /* we don't need yyunput() function */ 6 | %option nounput 7 | /* we don't need input() function */ 8 | %option noinput 9 | /* enable location */ 10 | %option bison-bridge 11 | %option bison-locations 12 | 13 | %{ 14 | #include "ast.h" 15 | #include "yacc.tab.h" 16 | #include 17 | 18 | // automatically update location 19 | #define YY_USER_ACTION \ 20 | yylloc->first_line = yylloc->last_line; \ 21 | yylloc->first_column = yylloc->last_column; \ 22 | for (int i = 0; yytext[i] != '\0'; i++) { \ 23 | if(yytext[i] == '\n') { \ 24 | yylloc->last_line++; \ 25 | yylloc->last_column = 1; \ 26 | } else { \ 27 | yylloc->last_column++; \ 28 | } \ 29 | } 30 | 31 | %} 32 | 33 | alpha [a-zA-Z] 34 | digit [0-9] 35 | white_space [ \t]+ 36 | new_line "\r"|"\n"|"\r\n" 37 | sign "+"|"-" 38 | identifier {alpha}(_|{alpha}|{digit})* 39 | value_int {sign}?{digit}+ 40 | value_float {sign}?{digit}+\.({digit}+)? 41 | value_string '[^']*' 42 | single_op ";"|"("|")"|","|"*"|"="|">"|"<"|"." 43 | 44 | %x STATE_COMMENT 45 | 46 | %% 47 | /* block comment */ 48 | "/*" { BEGIN(STATE_COMMENT); } 49 | "*/" { BEGIN(INITIAL); } 50 | [^*] { /* ignore the text of the comment */ } 51 | \* { /* ignore *'s that aren't part of */ } 52 | /* single line comment */ 53 | "--".* { /* ignore single line comment */ } 54 | /* white space and new line */ 55 | {white_space} { /* ignore white space */ } 56 | {new_line} { /* ignore new line */ } 57 | /* keywords */ 58 | "SHOW" { return SHOW; } 59 | "BEGIN" { return TXN_BEGIN; } 60 | "COMMIT" { return TXN_COMMIT; } 61 | "ABORT" { return TXN_ABORT; } 62 | "ROLLBACK" { return TXN_ROLLBACK; } 63 | "TABLES" { return TABLES; } 64 | "CREATE" { return CREATE; } 65 | "TABLE" { return TABLE; } 66 | "DROP" { return DROP; } 67 | "ORDER" { return ORDER; } 68 | "BY" { return BY; } 69 | "DESC" { return DESC; } 70 | "ASC" { return ASC; } 71 | "LIMIT" { return LIMIT; } 72 | "INSERT" { return INSERT; } 73 | "INTO" { return INTO; } 74 | "VALUES" { return VALUES; } 75 | "DELETE" { return DELETE; } 76 | "FROM" { return FROM; } 77 | "WHERE" { return WHERE; } 78 | "UPDATE" { return UPDATE; } 79 | "SET" { return SET; } 80 | "SELECT" { return SELECT; } 81 | "INT" { return INT; } 82 | "CHAR" { return CHAR; } 83 | "FLOAT" { return FLOAT; } 84 | "INDEX" { return INDEX; } 85 | "AND" { return AND; } 86 | "JOIN" {return JOIN;} 87 | "EXIT" { return EXIT; } 88 | "HELP" { return HELP; } 89 | /* operators */ 90 | ">=" { return GEQ; } 91 | "<=" { return LEQ; } 92 | "<>" { return NEQ; } 93 | {single_op} { return yytext[0]; } 94 | /* id */ 95 | {identifier} { 96 | yylval->sv_str = yytext; 97 | return IDENTIFIER; 98 | } 99 | /* literals */ 100 | {value_int} { 101 | yylval->sv_int = atoi(yytext); 102 | return VALUE_INT; 103 | } 104 | {value_float} { 105 | yylval->sv_float = atof(yytext); 106 | return VALUE_FLOAT; 107 | } 108 | {value_string} { 109 | yylval->sv_str = std::string(yytext + 1, strlen(yytext) - 2); 110 | return VALUE_STRING; 111 | } 112 | /* EOF */ 113 | <> { return T_EOF; } 114 | /* unexpected char */ 115 | . { std::cerr << "Lexer Error: unexpected character " << yytext[0] << std::endl; } 116 | %% 117 | -------------------------------------------------------------------------------- /src/parser/parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ast_printer.h" 4 | #include "ast.h" 5 | #include "parser_defs.h" 6 | -------------------------------------------------------------------------------- /src/parser/parser_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | 5 | int yyparse(); 6 | 7 | typedef struct yy_buffer_state *YY_BUFFER_STATE; 8 | 9 | YY_BUFFER_STATE yy_scan_string(const char *str); 10 | 11 | void yy_delete_buffer(YY_BUFFER_STATE buffer); 12 | -------------------------------------------------------------------------------- /src/parser/yacc.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.5.1. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, 6 | Inc. 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . */ 20 | 21 | /* As a special exception, you may create a larger work that contains 22 | part or all of the Bison parser skeleton and distribute that work 23 | under terms of your choice, so long as that work isn't itself a 24 | parser generator using the skeleton or a modified version thereof 25 | as a parser skeleton. Alternatively, if you modify or redistribute 26 | the parser skeleton itself, you may (at your option) remove this 27 | special exception, which will cause the skeleton and the resulting 28 | Bison output files to be licensed under the GNU General Public 29 | License without this special exception. 30 | 31 | This special exception was added by the Free Software Foundation in 32 | version 2.2 of Bison. */ 33 | 34 | /* Undocumented macros, especially those whose name start with YY_, 35 | are private implementation details. Do not rely on them. */ 36 | 37 | #ifndef YY_YY_HOME_LUO_RUCBASE_RUCBASE_SRC_PARSER_YACC_TAB_H_INCLUDED 38 | # define YY_YY_HOME_LUO_RUCBASE_RUCBASE_SRC_PARSER_YACC_TAB_H_INCLUDED 39 | /* Debug traces. */ 40 | #ifndef YYDEBUG 41 | # define YYDEBUG 0 42 | #endif 43 | #if YYDEBUG 44 | extern int yydebug; 45 | #endif 46 | 47 | /* Token type. */ 48 | #ifndef YYTOKENTYPE 49 | # define YYTOKENTYPE 50 | enum yytokentype 51 | { 52 | SHOW = 258, 53 | TABLES = 259, 54 | CREATE = 260, 55 | TABLE = 261, 56 | DROP = 262, 57 | DESC = 263, 58 | INSERT = 264, 59 | INTO = 265, 60 | VALUES = 266, 61 | DELETE = 267, 62 | FROM = 268, 63 | WHERE = 269, 64 | UPDATE = 270, 65 | SET = 271, 66 | SELECT = 272, 67 | INT = 273, 68 | CHAR = 274, 69 | FLOAT = 275, 70 | INDEX = 276, 71 | AND = 277, 72 | JOIN = 278, 73 | EXIT = 279, 74 | HELP = 280, 75 | TXN_BEGIN = 281, 76 | TXN_COMMIT = 282, 77 | TXN_ABORT = 283, 78 | TXN_ROLLBACK = 284, 79 | ORDER = 285, 80 | BY = 286, 81 | ASC = 287, 82 | LIMIT = 288, 83 | LEQ = 289, 84 | NEQ = 290, 85 | GEQ = 291, 86 | T_EOF = 292, 87 | IDENTIFIER = 293, 88 | VALUE_STRING = 294, 89 | VALUE_INT = 295, 90 | VALUE_FLOAT = 296 91 | }; 92 | #endif 93 | 94 | /* Value type. */ 95 | 96 | /* Location type. */ 97 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 98 | typedef struct YYLTYPE YYLTYPE; 99 | struct YYLTYPE 100 | { 101 | int first_line; 102 | int first_column; 103 | int last_line; 104 | int last_column; 105 | }; 106 | # define YYLTYPE_IS_DECLARED 1 107 | # define YYLTYPE_IS_TRIVIAL 1 108 | #endif 109 | 110 | 111 | 112 | int yyparse (void); 113 | 114 | #endif /* !YY_YY_HOME_LUO_RUCBASE_RUCBASE_SRC_PARSER_YACC_TAB_H_INCLUDED */ 115 | -------------------------------------------------------------------------------- /src/record/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # record module 2 | set(SOURCES rm_file_handle.cpp rm_scan.cpp) 3 | add_library(record STATIC ${SOURCES}) 4 | add_library(records SHARED ${SOURCES}) 5 | target_link_libraries(record storage system transaction) 6 | 7 | # rm_gtest 8 | add_executable(rm_gtest rm_gtest.cpp) 9 | target_link_libraries(rm_gtest record gtest_main) -------------------------------------------------------------------------------- /src/record/bitmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | static constexpr int BITMAP_WIDTH = 8; 7 | static constexpr unsigned BITMAP_HIGHEST_BIT = 0x80u; // 128 (2^7) 8 | 9 | class Bitmap { 10 | public: 11 | // 从地址bm开始的size个字节全部置0 12 | static void init(char *bm, int size) { memset(bm, 0, size); } 13 | 14 | // pos位 置1 15 | static void set(char *bm, int pos) { bm[get_bucket(pos)] |= get_bit(pos); } 16 | 17 | // pos位 置0 18 | static void reset(char *bm, int pos) { bm[get_bucket(pos)] &= static_cast(~get_bit(pos)); } 19 | 20 | // 如果pos位是1,则返回true 21 | static bool is_set(const char *bm, int pos) { return (bm[get_bucket(pos)] & get_bit(pos)) != 0; } 22 | 23 | /** 24 | * @brief 找下一个为0 or 1的位 25 | * @param bit false表示要找下一个为0的位,true表示要找下一个为1的位 26 | * @param bm 要找的起始地址为bm 27 | * @param max_n 要找的从起始地址开始的偏移为[curr+1,max_n) 28 | * @param curr 要找的从起始地址开始的偏移为[curr+1,max_n) 29 | * @return 找到了就返回偏移位置,没找到就返回max_n 30 | */ 31 | static int next_bit(bool bit, const char *bm, int max_n, int curr) { 32 | for (int i = curr + 1; i < max_n; i++) { 33 | if (is_set(bm, i) == bit) { 34 | return i; 35 | } 36 | } 37 | return max_n; 38 | } 39 | 40 | // 找第一个为0 or 1的位 41 | static int first_bit(bool bit, const char *bm, int max_n) { return next_bit(bit, bm, max_n, -1); } 42 | 43 | // for example: 44 | // rid_.slot_no = Bitmap::next_bit(true, page_handle.bitmap, file_handle_->file_hdr_.num_records_per_page, 45 | // rid_.slot_no); int slot_no = Bitmap::first_bit(false, page_handle.bitmap, file_hdr_.num_records_per_page); 46 | 47 | private: 48 | static int get_bucket(int pos) { return pos / BITMAP_WIDTH; } 49 | 50 | static char get_bit(int pos) { return BITMAP_HIGHEST_BIT >> static_cast(pos % BITMAP_WIDTH); } 51 | }; 52 | -------------------------------------------------------------------------------- /src/record/rm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rm_scan.h" 4 | #include "rm_manager.h" 5 | #include "rm_defs.h" 6 | -------------------------------------------------------------------------------- /src/record/rm_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/macros.h" 4 | #include "defs.h" 5 | #include "storage/buffer_pool_manager.h" 6 | 7 | constexpr int RM_NO_PAGE = -1; 8 | constexpr int RM_FILE_HDR_PAGE = 0; 9 | constexpr int RM_FIRST_RECORD_PAGE = 1; 10 | constexpr int RM_MAX_RECORD_SIZE = 512; 11 | 12 | // record file header(RmManager::create_file函数初始化,并写入磁盘文件中的第0页) 13 | struct RmFileHdr { 14 | int record_size; // 元组大小(长度不固定,由上层进行初始化) 15 | // std::atomic num_pages; 16 | int num_pages; // 文件中当前分配的page个数(初始化为1) 17 | int num_records_per_page; // 每个page最多能存储的元组个数 18 | int first_free_page_no; // 文件中当前第一个可用的page no(初始化为-1) 19 | int bitmap_size; // bitmap大小 20 | }; 21 | 22 | // record page header(RmFileHandle::create_page函数进行初始化) 23 | struct RmPageHdr { 24 | int next_free_page_no; // 当前page满了之后,下一个可用的page no(初始化为-1) 25 | int num_records; // 当前page中当前分配的record个数(初始化为0) 26 | }; 27 | 28 | // 类似于Tuple 29 | struct RmRecord { 30 | char *data; // data初始化分配size个字节的空间 31 | int size; // size = RmFileHdr的record_size 32 | bool allocated_ = false; 33 | 34 | // DISALLOW_COPY(RmRecord); 35 | // RmRecord(const RmRecord &other) = delete; 36 | // RmRecord &operator=(const RmRecord &other) = delete; 37 | 38 | RmRecord() = default; 39 | 40 | RmRecord(const RmRecord &other) { 41 | size = other.size; 42 | data = new char[size]; 43 | memcpy(data, other.data, size); 44 | allocated_ = true; 45 | }; 46 | 47 | RmRecord &operator=(const RmRecord &other) { 48 | size = other.size; 49 | data = new char[size]; 50 | memcpy(data, other.data, size); 51 | allocated_ = true; 52 | return *this; 53 | }; 54 | 55 | RmRecord(int size_) { 56 | size = size_; 57 | data = new char[size_]; 58 | allocated_ = true; 59 | } 60 | 61 | RmRecord(int size_, char *data_) { 62 | size = size_; 63 | data = new char[size_]; 64 | memcpy(data, data_, size_); 65 | allocated_ = true; 66 | } 67 | 68 | void SetData(char *data_) { 69 | memcpy(data, data_, size); 70 | } 71 | 72 | void Deserialize(const char *data_) { 73 | size = *reinterpret_cast(data_); 74 | delete[] data; 75 | data = new char[size]; 76 | memcpy(data, data_ + sizeof(int), size); 77 | } 78 | 79 | ~RmRecord() { 80 | if(allocated_) { 81 | delete[] data; 82 | } 83 | allocated_ = false; 84 | data = nullptr; 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /src/record/rm_file_handle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "bitmap.h" 8 | #include "common/context.h" 9 | #include "rm_defs.h" 10 | 11 | class RmManager; 12 | 13 | // 对单个page进行封装,用page中的data存RmPageHdr, bitmap, slots的数据 14 | struct RmPageHandle { 15 | const RmFileHdr *file_hdr; // 用到了file_hdr的bitmap_size, record_size 16 | Page *page; // 指向单个page 17 | RmPageHdr *page_hdr; // page->data的第一部分,指针指向首地址,长度为sizeof(RmPageHdr) 18 | char *bitmap; // page->data的第二部分,指针指向首地址,长度为file_hdr->bitmap_size 19 | char *slots; // page->data的第三部分,指针指向首地址,每个slot的长度为file_hdr->record_size 20 | 21 | RmPageHandle(const RmFileHdr *fhdr_, Page *page_) : file_hdr(fhdr_), page(page_) { 22 | page_hdr = reinterpret_cast(page->GetData() + page->OFFSET_PAGE_HDR); 23 | bitmap = page->GetData() + sizeof(RmPageHdr) + page->OFFSET_PAGE_HDR; 24 | slots = bitmap + file_hdr->bitmap_size; 25 | } 26 | 27 | // 返回位于slot_no的record的地址 28 | char *get_slot(int slot_no) const { 29 | return slots + slot_no * file_hdr->record_size; // slots的首地址 + slot个数 * 每个slot的大小(每个record的大小) 30 | } 31 | }; 32 | 33 | // 每个RmFileHandle对应一个文件,里面有多个page,每个page的数据封装在RmPageHandle 34 | class RmFileHandle { // TableHeap 35 | friend class RmScan; // TableIterator 36 | friend class RmManager; 37 | 38 | private: 39 | DiskManager *disk_manager_; 40 | BufferPoolManager *buffer_pool_manager_; 41 | int fd_; 42 | /** @brief file_hdr中的num_pages记录此文件分配的page个数 43 | * page_no范围为[0,file_hdr.num_pages),page_no从0开始增加,其中第0页存file_hdr,从第1页开始存page_handle 44 | * 在page_handle中有page_hdr.free_page_no存第一个可用(未满)的page_no 45 | * */ 46 | RmFileHdr file_hdr_; 47 | 48 | public: 49 | RmFileHandle(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager, int fd) 50 | : disk_manager_(disk_manager), buffer_pool_manager_(buffer_pool_manager), fd_(fd) { 51 | // 注意:这里从磁盘中读出文件描述符为fd的文件的file_hdr,读到内存中 52 | // 这里实际就是初始化file_hdr,只不过是从磁盘中读出进行初始化 53 | // init file_hdr_ 54 | disk_manager_->read_page(fd, RM_FILE_HDR_PAGE, (char *)&file_hdr_, sizeof(file_hdr_)); 55 | // disk_manager管理的fd对应的文件中,设置从file_hdr_.num_pages开始分配page_no 56 | disk_manager_->set_fd2pageno(fd, file_hdr_.num_pages); 57 | } 58 | 59 | DISALLOW_COPY(RmFileHandle); 60 | // RmFileHandle(const RmFileHandle &other) = delete; 61 | // RmFileHandle &operator=(const RmFileHandle &other) = delete; 62 | 63 | RmFileHdr get_file_hdr() { return file_hdr_; } 64 | int GetFd() { return fd_; } 65 | 66 | bool is_record(const Rid &rid) const { 67 | RmPageHandle page_handle = fetch_page_handle(rid.page_no); 68 | return Bitmap::is_set(page_handle.bitmap, rid.slot_no); // page的slot_no位置上是否有record 69 | } 70 | 71 | std::unique_ptr get_record(const Rid &rid, Context *context) const; 72 | 73 | Rid insert_record(char *buf, Context *context); 74 | 75 | void insert_record(const Rid &rid, char *buf); 76 | 77 | void delete_record(const Rid &rid, Context *context); 78 | 79 | void update_record(const Rid &rid, char *buf, Context *context); 80 | 81 | RmPageHandle create_new_page_handle(); 82 | 83 | RmPageHandle fetch_page_handle(int page_no) const; 84 | 85 | private: 86 | RmPageHandle create_page_handle(); 87 | 88 | void release_page_handle(RmPageHandle &page_handle); 89 | }; 90 | -------------------------------------------------------------------------------- /src/record/rm_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "bitmap.h" 6 | #include "rm_defs.h" 7 | #include "rm_file_handle.h" 8 | 9 | //只用于创建/打开/关闭/删除文件,打开文件的时候会返回record file handle 10 | //它可以管理多个record文件(管理多个record file handle) 11 | class RmManager { 12 | private: 13 | DiskManager *disk_manager_; 14 | BufferPoolManager *buffer_pool_manager_; 15 | 16 | public: 17 | RmManager(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager) 18 | : disk_manager_(disk_manager), buffer_pool_manager_(buffer_pool_manager) {} 19 | 20 | void create_file(const std::string &filename, int record_size) { 21 | if (record_size < 1 || record_size > RM_MAX_RECORD_SIZE) { 22 | throw InvalidRecordSizeError(record_size); 23 | } 24 | disk_manager_->create_file(filename); 25 | int fd = disk_manager_->open_file(filename); 26 | 27 | // 初始化file header 28 | RmFileHdr file_hdr{}; 29 | file_hdr.record_size = record_size; 30 | file_hdr.num_pages = 1; 31 | file_hdr.first_free_page_no = RM_NO_PAGE; 32 | // We have: sizeof(hdr) + (n + 7) / 8 + n * record_size <= PAGE_SIZE 33 | file_hdr.num_records_per_page = 34 | (BITMAP_WIDTH * (PAGE_SIZE - 1 - (int)sizeof(RmFileHdr)) + 1) / (1 + record_size * BITMAP_WIDTH); 35 | file_hdr.bitmap_size = (file_hdr.num_records_per_page + BITMAP_WIDTH - 1) / BITMAP_WIDTH; 36 | 37 | // 将file header写入磁盘文件(名为file name,文件描述符为fd)中的第0页 38 | // head page直接写入磁盘,没有经过缓冲区的NewPage,那么也就不需要FlushPage 39 | disk_manager_->write_page(fd, RM_FILE_HDR_PAGE, (char *)&file_hdr, sizeof(file_hdr)); 40 | disk_manager_->close_file(fd); 41 | } 42 | 43 | void destroy_file(const std::string &filename) { disk_manager_->destroy_file(filename); } 44 | 45 | // 注意这里打开文件,创建并返回了record file handle的指针 46 | std::unique_ptr open_file(const std::string &filename) { 47 | int fd = disk_manager_->open_file(filename); 48 | return std::make_unique(disk_manager_, buffer_pool_manager_, fd); 49 | } 50 | 51 | void close_file(const RmFileHandle *file_handle) { 52 | disk_manager_->write_page(file_handle->fd_, RM_FILE_HDR_PAGE, (char *)&file_handle->file_hdr_, 53 | sizeof(file_handle->file_hdr_)); 54 | // 缓冲区的所有页刷到磁盘,注意这句话必须写在close_file前面 55 | buffer_pool_manager_->FlushAllPages(file_handle->fd_); 56 | disk_manager_->close_file(file_handle->fd_); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/record/rm_scan.cpp: -------------------------------------------------------------------------------- 1 | #include "rm_scan.h" 2 | 3 | #include "rm_file_handle.h" 4 | 5 | /** 6 | * @brief 初始化file_handle和rid 7 | * 8 | * @param file_handle 9 | */ 10 | RmScan::RmScan(const RmFileHandle *file_handle) : file_handle_(file_handle) { 11 | // Todo: 12 | // 初始化file_handle和rid(指向第一个存放了记录的位置) 13 | //这是查有record的位置,而不是查空闲的表,不要弄错了! 14 | int maxpage = file_handle_->file_hdr_.num_pages; 15 | int pageno = 1; 16 | if(maxpage > 1){ 17 | for(pageno = 1; pageno < maxpage; pageno++){ 18 | if(file_handle_->fetch_page_handle(pageno).page_hdr->num_records > 0){ 19 | int i = Bitmap::first_bit(1, file_handle_->fetch_page_handle(pageno).bitmap, file_handle_->file_hdr_.num_records_per_page); 20 | rid_.page_no = pageno; 21 | rid_.slot_no = i; 22 | return; 23 | } 24 | } 25 | } 26 | rid_=Rid{-1,-1}; 27 | } 28 | 29 | /** 30 | * @brief 找到文件中下一个存放了记录的位置 31 | */ 32 | void RmScan::next() { 33 | // Todo: 34 | // 找到文件中下一个存放了记录的非空闲位置,用rid_来指向这个位置 35 | int maxpage = file_handle_->file_hdr_.num_pages; 36 | int pageno = rid_.page_no; 37 | int slotno = rid_.slot_no; 38 | for(;pageno < maxpage; pageno++){ 39 | int i = Bitmap::next_bit(1, file_handle_->fetch_page_handle(pageno).bitmap, file_handle_->file_hdr_.num_records_per_page, slotno); 40 | if(i == file_handle_->file_hdr_.num_records_per_page){ 41 | slotno = -1; 42 | continue; 43 | } 44 | else{ 45 | rid_.page_no = pageno; 46 | rid_.slot_no = i; 47 | return; 48 | } 49 | } 50 | rid_=Rid{-1,-1}; 51 | } 52 | 53 | /** 54 | * @brief ​ 判断是否到达文件末尾 55 | */ 56 | bool RmScan::is_end() const { 57 | // Todo: 修改返回值 58 | if(rid_.page_no==-1&&rid_.slot_no==-1)return true; 59 | return false; 60 | } 61 | 62 | /** 63 | * @brief RmScan内部存放的rid 64 | */ 65 | Rid RmScan::rid() const { 66 | // Todo: 修改返回值 67 | return rid_; 68 | } -------------------------------------------------------------------------------- /src/record/rm_scan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rm_defs.h" 4 | 5 | class RmFileHandle; 6 | 7 | class RmScan : public RecScan { 8 | const RmFileHandle *file_handle_; 9 | Rid rid_; 10 | public: 11 | RmScan(const RmFileHandle *file_handle); 12 | 13 | void next() override; 14 | 15 | bool is_end() const override; 16 | 17 | Rid rid() const override; 18 | }; 19 | -------------------------------------------------------------------------------- /src/record_printer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "common/context.h" 9 | 10 | class RecordPrinter { 11 | static constexpr size_t COL_WIDTH = 16; 12 | size_t num_cols; 13 | public: 14 | RecordPrinter(size_t num_cols_) : num_cols(num_cols_) { 15 | assert(num_cols_ > 0); 16 | } 17 | 18 | void print_separator(Context *context) const { 19 | for (size_t i = 0; i < num_cols; i++) { 20 | // std::cout << '+' << std::string(COL_WIDTH + 2, '-'); 21 | std::string str = "+" + std::string(COL_WIDTH + 2, '-'); 22 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 23 | *(context->offset_) = *(context->offset_) + str.length(); 24 | } 25 | std::string str = "+\n"; 26 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 27 | *(context->offset_) = *(context->offset_) + str.length(); 28 | // std::cout << "+\n"; 29 | } 30 | 31 | void print_record(const std::vector &rec_str, Context *context) const { 32 | assert(rec_str.size() == num_cols); 33 | for (auto col: rec_str) { 34 | if (col.size() > COL_WIDTH) { 35 | col = col.substr(0, COL_WIDTH - 3) + "..."; 36 | } 37 | // std::cout << "| " << std::setw(COL_WIDTH) << col << ' '; 38 | std::stringstream ss; 39 | ss << "| " << std::setw(COL_WIDTH) << col << " "; 40 | memcpy(context->data_send_ + *(context->offset_), ss.str().c_str(), ss.str().length()); 41 | *(context->offset_) = *(context->offset_) + ss.str().length(); 42 | } 43 | // std::cout << "|\n"; 44 | std::string str = "|\n"; 45 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 46 | *(context->offset_) = *(context->offset_) + str.length(); 47 | } 48 | 49 | static void print_record_count(size_t num_rec, Context *context) { 50 | // std::cout << "Total record(s): " << num_rec << '\n'; 51 | std::string str = "Total record(s): " + std::to_string(num_rec) + '\n'; 52 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 53 | *(context->offset_) = *(context->offset_) + str.length(); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/recovery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # revovery module 2 | set(SOURCES log_manager.cpp log_recovery.cpp checkpoint.cpp) 3 | add_library(recovery STATIC ${SOURCES}) 4 | add_library(recoverys SHARED ${SOURCES}) 5 | target_link_libraries(recovery system pthread) -------------------------------------------------------------------------------- /src/recovery/checkpoint.cpp: -------------------------------------------------------------------------------- 1 | #include "checkpoint.h" 2 | 3 | void CheckpointManager::BeginCheckpoint() {} 4 | 5 | void CheckpointManager::EndCheckpoint() {} -------------------------------------------------------------------------------- /src/recovery/checkpoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "transaction/transaction_manager.h" 4 | 5 | class CheckpointManager { 6 | public: 7 | CheckpointManager(TransactionManager * txn_manager, LogManager *log_manager, 8 | BufferPoolManager *buffer_pool_manager) 9 | : txn_manager_(txn_manager), log_manager_(log_manager), 10 | buffer_pool_manager_(buffer_pool_manager){} 11 | 12 | ~CheckpointManager() = default; 13 | 14 | void BeginCheckpoint(); 15 | void EndCheckpoint(); 16 | 17 | private: 18 | TransactionManager *txn_manager_; 19 | LogManager *log_manager_; 20 | BufferPoolManager *buffer_pool_manager_; 21 | }; -------------------------------------------------------------------------------- /src/recovery/log_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #include "storage/disk_manager.h" 5 | #include "common/config.h" 6 | 7 | #include 8 | #include 9 | 10 | static constexpr std::chrono::duration FLUSH_TIMEOUT = std::chrono::seconds(1); 11 | -------------------------------------------------------------------------------- /src/recovery/log_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "log_manager.h" 2 | 3 | #include 4 | 5 | /** 6 | * 开启日志刷新线程 7 | */ 8 | void LogManager::RunFlushThread() { 9 | // Todo: 10 | // 1. 如果系统未开启日志功能,则不能开启日志刷新线程(通过log_mode_判断) 11 | // 2. 开启一个新线程,用来把flush_buffer_中的内容刷新到磁盘当中 12 | // 3. 在刷新之前,需要判断当前线程由于哪种原因被唤醒,如果是time_out唤醒,则需要交换log_buffer和flush_buffer 13 | // 4. 刷新之后需要更新flush_buffer的偏移量、persistent_lsn_等信息 14 | 15 | } 16 | 17 | /** 18 | * 辅助函数,用于DiskManager唤醒flush_thread_ 19 | * @param p 20 | */ 21 | void LogManager::WakeUpFlushThread(std::promise *p) { 22 | { 23 | std::unique_lock lock(latch_); 24 | SwapBuffer(); 25 | SetPromise(p); 26 | } 27 | 28 | cv_.notify_one(); 29 | 30 | // waiting for flush_done 31 | if (promise != nullptr) { 32 | promise->get_future().wait(); 33 | } 34 | 35 | SetPromise(nullptr); 36 | } 37 | 38 | /** 39 | * 辅助函数,交换log_buffer_和flush_buffer_及其相关信息 40 | */ 41 | void LogManager::SwapBuffer() { 42 | std::swap(log_buffer_, flush_buffer_); 43 | std::swap(log_buffer_write_offset_, flush_buffer_write_offset_); 44 | flush_lsn_ = next_lsn_ - 1; 45 | } 46 | 47 | /** 48 | * 添加一条日志记录到log_buffer_中 49 | * @param log_record 要添加的日志记录 50 | * @return 返回该日志的日志序列号 51 | */ 52 | lsn_t LogManager::AppendLogRecord(LogRecord *log_record) { 53 | // Todo: 54 | // 1. 获取互斥锁latch_ 55 | // 2. 判断log_buffer_中是否还存在足够的剩余空间,如果空间不足,需要交换log_buffer_和flush_buffer_,唤醒日志刷新线程 56 | // 3. 为该日志分配日志序列号 57 | // 4. 把该日志写入到log_buffer_中 58 | 59 | return log_record->lsn_; 60 | } -------------------------------------------------------------------------------- /src/recovery/log_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log_record.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /** 10 | * TODO: PROBLEM{ if the log_record.size is large than the page_size } 11 | */ 12 | 13 | class LogManager{ 14 | public: 15 | 16 | explicit LogManager(DiskManager *disk_manager) { 17 | next_lsn_ = 0; 18 | flush_lsn_ = INVALID_LSN; 19 | persistent_lsn_ = INVALID_LSN; 20 | log_buffer_ = new char[LOG_BUFFER_SIZE]; 21 | std::memset(log_buffer_, 0, LOG_BUFFER_SIZE); 22 | flush_buffer_ = new char[LOG_BUFFER_SIZE]; 23 | std::memset(flush_buffer_, 0, LOG_BUFFER_SIZE); 24 | disk_manager_ = disk_manager; 25 | } 26 | 27 | ~LogManager() { 28 | delete[] log_buffer_; 29 | delete[] flush_buffer_; 30 | log_buffer_ = nullptr; 31 | flush_buffer_ = nullptr; 32 | disk_manager_ = nullptr; 33 | } 34 | 35 | /** 36 | * @brief create the flush_thread to flush the log records into disk 37 | */ 38 | void RunFlushThread(); 39 | void StopFlushThread() { log_mode_ = false; flush_thread_->join(); delete flush_thread_; } 40 | /** 41 | * @brief swap the log_buffer and flush_buffer in order to flush records into disk 42 | */ 43 | void SwapBuffer(); 44 | /** 45 | * @brief provided for bufferpool 46 | * when bufferpool wants to force the flush, it can call this function 47 | */ 48 | void WakeUpFlushThread(std::promise *p); 49 | 50 | lsn_t AppendLogRecord(LogRecord * log_record); 51 | 52 | inline bool GetLogMode() { return log_mode_; } 53 | inline void SetLogMode(bool log_mode) { log_mode_ = log_mode; } 54 | inline lsn_t GetNextLsn() { return next_lsn_; } 55 | inline lsn_t GetFlushLsn() { return flush_lsn_; } 56 | inline char * GetLogBuffer() { return log_buffer_; } 57 | inline lsn_t GetPersistentLsn() { return persistent_lsn_; } 58 | inline void SetPromise(std::promise *p) { promise = p; } 59 | 60 | private: 61 | bool log_mode_{false}; // 标识系统是否开启日志功能,默认开启日志功能,如果不开启日志功能,需要设置该变量为false 62 | 63 | char *log_buffer_; // 用来暂时存储系统运行过程中添加的日志; append log_record into log_buffer 64 | char *flush_buffer_; // 用来暂时存储需要刷新到磁盘中的日志; flush the logs in flush_buffer into disk file 65 | 66 | std::atomic next_lsn_; // 用于分发日志序列号; next lsn in the system 67 | std::atomic persistent_lsn_; // 已经刷新到磁盘中的最后一条日志的日志序列号; the last persistent lsn 68 | lsn_t flush_lsn_; // flush_buffer_中最后一条日志的日志记录号; the last lsn in the flush_buffer 69 | 70 | size_t log_buffer_write_offset_ = 0; // log_buffer_的偏移量 71 | size_t flush_buffer_write_offset_ = 0; // flush_buffer_的偏移量 72 | 73 | std::thread *flush_thread_; // 日志刷新线程 74 | 75 | std::mutex latch_; // 互斥锁,用于log_buffer_的互斥访问 76 | 77 | std::condition_variable cv_; // 条件变量,用于flush_thread的唤醒; to notify the flush_thread 78 | 79 | // used to notify the WakeUpFlushThread() that the log records have been flushed into the disk 80 | std::promise *promise; // 主动唤醒flush_thread_,强制要求刷新日志 81 | 82 | DiskManager *disk_manager_; 83 | }; -------------------------------------------------------------------------------- /src/recovery/log_recovery.cpp: -------------------------------------------------------------------------------- 1 | #include "log_recovery.h" 2 | #include "record/rm.h" 3 | #include "system/sm_manager.h" 4 | 5 | /** 6 | * 重做未刷入磁盘的写操作 7 | * 只需要考虑DML操作,暂时不需要考虑DDL操作 8 | */ 9 | void LogRecovery::Redo() { 10 | // Todo: 11 | // 1. 从磁盘的日志文件中顺序读取日志记录 12 | // 2. 根据日志对应操作的类型,执行相应的操作 13 | // 2.1 如果是事务相关操作,则需要维护事务活动列表active_txns_ 14 | // 2.2 如果是写操作,需要比较该日志的日志序列号和对应数据页的page_lsn_,判断是否要执行写操作 15 | 16 | } 17 | 18 | /** 19 | * 撤销未完成事务的写操作 20 | * 只需要考虑DML操作,暂时不需要考虑DDL操作 21 | */ 22 | void LogRecovery::Undo() { 23 | // Todo: 24 | // 1. 遍历事务活动列表active_txns_获取所有未完成事务 25 | // 2. 根据日志中的prev_lsn_信息遍历该事务已经执行的所有写操作 26 | // 3. 撤销该事务的所有写操作 27 | 28 | } -------------------------------------------------------------------------------- /src/recovery/log_recovery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log_record.h" 4 | #include "system/sm_manager.h" 5 | 6 | class LogRecovery { 7 | public: 8 | LogRecovery(SmManager *sm_manager, DiskManager *disk_manager) { 9 | log_buffer_ = new char[LOG_BUFFER_SIZE]; 10 | log_offset_ = 0; 11 | active_txns_ = std::unordered_map(); 12 | lsn_mapping_ = std::unordered_map(); 13 | sm_manager_ = sm_manager; 14 | disk_manager_ = disk_manager; 15 | } 16 | 17 | ~LogRecovery() { 18 | delete[] log_buffer_; 19 | sm_manager_ = nullptr; 20 | disk_manager_ = nullptr; 21 | } 22 | 23 | void Redo(); 24 | void Undo(); 25 | inline bool GetRecoveryMode() { return recovery_mode_; } 26 | 27 | bool DeserializeLogRecord(const char* data, LogRecord &log_record); 28 | 29 | private: 30 | // store the running transactions, the mapping of running transactions to their lastest log records 31 | std::unordered_map active_txns_; // 活动事务列表,记录当前系统运行过程中所有正在执行的事务 32 | std::unordered_map lsn_mapping_; 33 | char *log_buffer_; // 从磁盘中读取的日志记录 34 | int log_offset_; // log_buffer_的偏移量 35 | SmManager *sm_manager_; 36 | DiskManager *disk_manager_; 37 | bool recovery_mode_ = false; // 用于标识在系统开启时是否进行系统故障恢复 38 | }; -------------------------------------------------------------------------------- /src/replacer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # replacer module 2 | set(SOURCES lru_replacer.cpp clock_replacer.cpp) 3 | add_library(lru_replacer STATIC ${SOURCES}) 4 | add_library(clock_replacer STATIC ${SOURCES}) 5 | 6 | add_executable(lru_replacer_test lru_replacer_test.cpp) 7 | target_link_libraries(lru_replacer_test lru_replacer gtest_main) # add gtest 8 | 9 | 10 | 11 | add_executable(clock_replacer_test clock_replacer_test.cpp) 12 | target_link_libraries(clock_replacer_test clock_replacer gtest_main) # add gtest 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/replacer/clock_replacer.cpp: -------------------------------------------------------------------------------- 1 | #include "replacer/clock_replacer.h" 2 | 3 | #include 4 | 5 | ClockReplacer::ClockReplacer(size_t num_pages) 6 | : circular_{num_pages, ClockReplacer::Status::EMPTY_OR_PINNED}, hand_{0}, capacity_{num_pages} { 7 | // 成员初始化列表语法 8 | circular_.reserve(num_pages); 9 | } 10 | 11 | ClockReplacer::~ClockReplacer() = default; 12 | 13 | bool ClockReplacer::Victim(frame_id_t *frame_id) { 14 | const std::lock_guard guard(mutex_); 15 | // Todo: try to find a victim frame in buffer pool with clock scheme 16 | // and make the *frame_id = victim_frame_id 17 | // not found, frame_id=nullptr and return false 18 | frame_id_t fuben=hand_; 19 | bool flag=false; 20 | while(true) 21 | { 22 | hand_++; 23 | hand_=hand_%ClockReplacer::capacity_; 24 | if(fuben==hand_&&!flag) 25 | { 26 | frame_id=nullptr; 27 | return false; 28 | } 29 | if(circular_[hand_]==Status::UNTOUCHED) 30 | { 31 | flag=true; 32 | *frame_id=hand_; 33 | circular_[hand_]=Status::EMPTY_OR_PINNED; 34 | return true; 35 | } 36 | else if(circular_[hand_]==Status::ACCESSED) 37 | { 38 | flag=true; 39 | circular_[hand_]=Status::UNTOUCHED; 40 | } 41 | } 42 | } 43 | 44 | void ClockReplacer::Pin(frame_id_t frame_id) { 45 | const std::lock_guard guard(mutex_); 46 | // Todo: you can implement it! 47 | circular_[frame_id]=Status::EMPTY_OR_PINNED; 48 | 49 | } 50 | 51 | void ClockReplacer::Unpin(frame_id_t frame_id) { 52 | const std::lock_guard guard(mutex_); 53 | // Todo: you can implement it! 54 | if(circular_[frame_id]==Status::EMPTY_OR_PINNED) 55 | { 56 | circular_[frame_id]=Status::ACCESSED; 57 | } 58 | } 59 | 60 | size_t ClockReplacer::Size() { 61 | // Todo: 62 | // 返回在[arg0, arg1)范围内满足特定条件(arg2)的元素的数目 63 | // return all items that in the range[circular_.begin, circular_.end ) 64 | // and be met the condition: status!=EMPTY_OR_PINNED 65 | // That is the number of frames in the buffer pool that storage page (NOT EMPTY_OR_PINNED) 66 | size_t num=0; 67 | for(size_t i=0;i // NOLINT 16 | #include 17 | 18 | #include "common/config.h" 19 | #include "replacer/replacer.h" 20 | 21 | /** 22 | * ClockReplacer implements the clock replacement policy, which approximates the Least Recently Used 23 | * policy. 24 | */ 25 | class ClockReplacer : public Replacer { 26 | using mutex_t = std::mutex; 27 | 28 | public: 29 | // EMPTY: This frame not storage page or pinned (is using by some thread,can not be victim) 30 | // ACCESSED: This frame is used by some thread not so long ago 31 | // UNTOUCHED: This frame can be victim 32 | enum class Status { UNTOUCHED, ACCESSED, EMPTY_OR_PINNED }; 33 | /** 34 | * Create a new ClockReplacer. 35 | * @param num_pages the maximum number of pages the ClockReplacer will be required to store 36 | */ 37 | explicit ClockReplacer(size_t num_pages); 38 | 39 | /** 40 | * Destroys the ClockReplacer. 41 | */ 42 | ~ClockReplacer() override; 43 | 44 | bool Victim(frame_id_t *frame_id) override; 45 | 46 | void Pin(frame_id_t frame_id) override; 47 | 48 | void Unpin(frame_id_t frame_id) override; 49 | 50 | size_t Size() override; 51 | 52 | private: 53 | std::vector circular_; 54 | frame_id_t hand_{0}; // initial hand_ value = 0, the scan starter 55 | size_t capacity_; 56 | mutex_t mutex_; 57 | }; -------------------------------------------------------------------------------- /src/replacer/clock_replacer_test.cpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // clock_replacer_test.cpp 6 | // 7 | // Identification: test/buffer/clock_replacer_test.cpp 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #include "replacer/clock_replacer.h" 14 | 15 | #include 16 | #include // NOLINT 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | 21 | /** 22 | * @note 选做 23 | */ 24 | TEST(ClockReplacerTest, SimpleTest) { 25 | ClockReplacer clock_replacer(7); 26 | 27 | // Scenario: unpin six elements, i.e. add them to the replacer. 28 | clock_replacer.Unpin(1); 29 | clock_replacer.Unpin(2); 30 | clock_replacer.Unpin(3); 31 | clock_replacer.Unpin(4); 32 | clock_replacer.Unpin(5); 33 | clock_replacer.Unpin(6); 34 | clock_replacer.Unpin(1); 35 | EXPECT_EQ(6, clock_replacer.Size()); 36 | 37 | // Scenario: get three victims from the clock. 38 | int value; 39 | clock_replacer.Victim(&value); 40 | EXPECT_EQ(1, value); 41 | clock_replacer.Victim(&value); 42 | EXPECT_EQ(2, value); 43 | clock_replacer.Victim(&value); 44 | EXPECT_EQ(3, value); 45 | 46 | // Scenario: pin elements in the replacer. 47 | // Note that 3 has already been victimized, so pinning 3 should have no effect. 48 | clock_replacer.Pin(3); 49 | clock_replacer.Pin(4); 50 | EXPECT_EQ(2, clock_replacer.Size()); 51 | 52 | // Scenario: unpin 4. We expect that the reference bit of 4 will be set to 1. 53 | clock_replacer.Unpin(4); 54 | 55 | // Scenario: continue looking for victims. We expect these victims. 56 | clock_replacer.Victim(&value); 57 | EXPECT_EQ(5, value); 58 | clock_replacer.Victim(&value); 59 | EXPECT_EQ(6, value); 60 | clock_replacer.Victim(&value); 61 | EXPECT_EQ(4, value); 62 | } 63 | 64 | /** 65 | * @note 选做 66 | */ 67 | TEST(ClockReplacerTest, CornerCaseTest) { 68 | ClockReplacer clock_replacer(4); 69 | int value; 70 | bool result = clock_replacer.Victim(&value); 71 | EXPECT_FALSE(result); 72 | 73 | clock_replacer.Unpin(3); 74 | clock_replacer.Unpin(2); 75 | EXPECT_EQ(2, clock_replacer.Size()); 76 | clock_replacer.Victim(&value); 77 | EXPECT_EQ(2, value); 78 | clock_replacer.Unpin(1); 79 | EXPECT_EQ(2, clock_replacer.Size()); 80 | clock_replacer.Victim(&value); 81 | EXPECT_EQ(3, value); 82 | clock_replacer.Victim(&value); 83 | EXPECT_EQ(1, value); 84 | EXPECT_FALSE(clock_replacer.Victim(&value)); 85 | EXPECT_EQ(0, clock_replacer.Size()); 86 | } 87 | -------------------------------------------------------------------------------- /src/replacer/lru_replacer.cpp: -------------------------------------------------------------------------------- 1 | #include "lru_replacer.h" 2 | 3 | LRUReplacer::LRUReplacer(size_t num_pages) { max_size_ = num_pages; } 4 | 5 | LRUReplacer::~LRUReplacer() = default; 6 | 7 | /** 8 | * @brief 使用LRU策略删除一个victim frame,这个函数能得到frame_id 9 | * @param[out] frame_id id of frame that was removed, nullptr if no victim was found 10 | * @return true if a victim frame was found, false otherwise 11 | */ 12 | bool LRUReplacer::Victim(frame_id_t *frame_id) { 13 | // C++17 std::scoped_lock 14 | // 它能够避免死锁发生,其构造函数能够自动进行上锁操作,析构函数会对互斥量进行解锁操作,保证线程安全。 15 | std::scoped_lock lock{latch_}; 16 | 17 | // Todo: 18 | // 利用lru_replacer中的LRUlist_,LRUHash_实现LRU策略 19 | // 选择合适的frame指定为淘汰页面,赋值给*frame_id 20 | if(LRUlist_.empty())return false; 21 | *frame_id = LRUlist_.back(); 22 | LRUlist_.pop_back(); 23 | return true; 24 | } 25 | 26 | /** 27 | * @brief 固定一个frame, 表明它不应该成为victim(即在replacer中移除该frame_id) 28 | * @param frame_id the id of the frame to pin 29 | */ 30 | void LRUReplacer::Pin(frame_id_t frame_id) { 31 | std::scoped_lock lock{latch_}; 32 | // Todo: 33 | // 固定指定id的frame 34 | // 在数据结构中移除该frame 35 | for(auto it=LRUlist_.begin();it!=LRUlist_.end();it++) 36 | { 37 | if(*it==frame_id) 38 | { 39 | LRUlist_.erase(it); 40 | return ; 41 | } 42 | } 43 | 44 | } 45 | 46 | /** 47 | * 取消固定一个frame, 表明它可以成为victim(即将该frame_id添加到replacer) 48 | * @param frame_id the id of the frame to unpin 49 | */ 50 | void LRUReplacer::Unpin(frame_id_t frame_id) { 51 | // Todo: 52 | // 支持并发锁 53 | // 选择一个frame取消固定 54 | std::scoped_lock lock{latch_}; 55 | for(auto it=LRUlist_.begin();it!=LRUlist_.end();it++) 56 | { 57 | if(*it==frame_id) 58 | { 59 | return ; 60 | } 61 | } 62 | LRUlist_.push_front(frame_id); 63 | } 64 | 65 | /** @return replacer中能够victim的数量 */ 66 | size_t LRUReplacer::Size() { 67 | // Todo: 68 | // 改写return size 69 | return LRUlist_.size(); 70 | } 71 | -------------------------------------------------------------------------------- /src/replacer/lru_replacer.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // lru_replacer.h 6 | // 7 | // Identification: src/include/buffer/lru_replacer.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #pragma once 14 | 15 | #include 16 | #include // NOLINT 包含std::mutex、std::scoped_lock 17 | #include 18 | 19 | #include "common/config.h" 20 | #include "replacer/replacer.h" 21 | #include "unordered_map" 22 | 23 | /** 24 | * LRUReplacer implements the lru replacement policy, which approximates the Least Recently Used policy. 25 | */ 26 | class LRUReplacer : public Replacer { 27 | public: 28 | /** 29 | * Create a new LRUReplacer. 30 | * @param num_pages the maximum number of pages the LRUReplacer will be required to store 31 | */ 32 | explicit LRUReplacer(size_t num_pages); 33 | // explicit关键字只能用来修饰类内部的构造函数声明,作用于单个参数的构造函数;被修饰的构造函数的类,不能发生相应的隐式类型转换。 34 | 35 | /** 36 | * Destroys the LRUReplacer. 37 | */ 38 | ~LRUReplacer(); 39 | 40 | bool Victim(frame_id_t *frame_id); 41 | 42 | void Pin(frame_id_t frame_id); 43 | 44 | void Unpin(frame_id_t frame_id); 45 | 46 | size_t Size(); 47 | 48 | private: 49 | std::mutex latch_; // 互斥锁 50 | std::list LRUlist_; // 按加入的时间顺序存放unpinned pages的frame id,首部表示最近被访问 51 | std::unordered_map::iterator> LRUhash_; // frame_id_t -> unpinned pages的frame id 52 | size_t max_size_; // 最大容量(与缓冲池的容量相同) 53 | }; 54 | -------------------------------------------------------------------------------- /src/replacer/lru_replacer_test.cpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // lru_replacer_test.cpp 6 | // 7 | // Identification: test/buffer/lru_replacer_test.cpp 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #include "replacer/lru_replacer.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "gtest/gtest.h" 24 | 25 | /** 26 | * @brief 简单测试LRUReplacer的基本功能 27 | * @note lab1 计分:5 points 28 | */ 29 | TEST(LRUReplacerTest, SimpleTest) { 30 | LRUReplacer lru_replacer(7); 31 | 32 | // Scenario: unpin six elements, i.e. add them to the replacer. 33 | lru_replacer.Unpin(1); 34 | lru_replacer.Unpin(2); 35 | lru_replacer.Unpin(3); 36 | lru_replacer.Unpin(4); 37 | lru_replacer.Unpin(5); 38 | lru_replacer.Unpin(6); 39 | lru_replacer.Unpin(1); 40 | EXPECT_EQ(6, lru_replacer.Size()); 41 | 42 | // Scenario: get three victims from the lru. 43 | int value; 44 | lru_replacer.Victim(&value); 45 | EXPECT_EQ(1, value); 46 | lru_replacer.Victim(&value); 47 | EXPECT_EQ(2, value); 48 | lru_replacer.Victim(&value); 49 | EXPECT_EQ(3, value); 50 | 51 | // Scenario: pin elements in the replacer. 52 | // Note that 3 has already been victimized, so pinning 3 should have no effect. 53 | lru_replacer.Pin(3); 54 | lru_replacer.Pin(4); 55 | EXPECT_EQ(2, lru_replacer.Size()); 56 | 57 | // Scenario: unpin 4. We expect that the reference bit of 4 will be set to 1. 58 | lru_replacer.Unpin(4); 59 | 60 | // Scenario: continue looking for victims. We expect these victims. 61 | lru_replacer.Victim(&value); 62 | EXPECT_EQ(5, value); 63 | lru_replacer.Victim(&value); 64 | EXPECT_EQ(6, value); 65 | lru_replacer.Victim(&value); 66 | EXPECT_EQ(4, value); 67 | } 68 | 69 | /** 70 | * @brief 加大数据量,测试LRUReplacer的基本功能 71 | * @note lab1 计分:5 points 72 | */ 73 | TEST(LRUReplacerTest, MixTest) { 74 | int result; 75 | int value_size = 10000; 76 | auto lru_replacer = new LRUReplacer(value_size); 77 | std::vector value(value_size); 78 | for (int i = 0; i < value_size; i++) { 79 | value[i] = i; 80 | } 81 | auto rng = std::default_random_engine{}; 82 | std::shuffle(value.begin(), value.end(), rng); 83 | 84 | for (int i = 0; i < value_size; i++) { 85 | lru_replacer->Unpin(value[i]); 86 | } 87 | EXPECT_EQ(value_size, lru_replacer->Size()); 88 | 89 | // Pin and unpin 777 90 | lru_replacer->Pin(777); 91 | lru_replacer->Unpin(777); 92 | // Pin and unpin 0 93 | EXPECT_EQ(1, lru_replacer->Victim(&result)); 94 | EXPECT_EQ(value[0], result); 95 | lru_replacer->Unpin(value[0]); 96 | 97 | for (int i = 0; i < value_size / 2; i++) { 98 | if (value[i] != value[0] && value[i] != 777) { 99 | lru_replacer->Pin(value[i]); 100 | lru_replacer->Unpin(value[i]); 101 | } 102 | } 103 | 104 | std::vector lru_array; 105 | for (int i = value_size / 2; i < value_size; ++i) { 106 | if (value[i] != value[0] && value[i] != 777) { 107 | lru_array.push_back(value[i]); 108 | } 109 | } 110 | lru_array.push_back(777); 111 | lru_array.push_back(value[0]); 112 | for (int i = 0; i < value_size / 2; ++i) { 113 | if (value[i] != value[0] && value[i] != 777) { 114 | lru_array.push_back(value[i]); 115 | } 116 | } 117 | EXPECT_EQ(value_size, lru_replacer->Size()); 118 | 119 | for (int e : lru_array) { 120 | EXPECT_EQ(true, lru_replacer->Victim(&result)); 121 | EXPECT_EQ(e, result); 122 | } 123 | EXPECT_EQ(value_size - lru_array.size(), lru_replacer->Size()); 124 | 125 | delete lru_replacer; 126 | } 127 | 128 | /** 129 | * @brief 并发测试LRUReplacer 130 | * @note lab1 计分:10 points 131 | */ 132 | TEST(LRUReplacerTest, ConcurrencyTest) { 133 | const int num_threads = 5; 134 | const int num_runs = 50; 135 | for (int run = 0; run < num_runs; run++) { 136 | int value_size = 1000; 137 | std::shared_ptr lru_replacer{new LRUReplacer(value_size)}; 138 | std::vector threads; 139 | int result; 140 | std::vector value(value_size); 141 | for (int i = 0; i < value_size; i++) { 142 | value[i] = i; 143 | } 144 | auto rng = std::default_random_engine{}; 145 | std::shuffle(value.begin(), value.end(), rng); 146 | 147 | for (int tid = 0; tid < num_threads; tid++) { 148 | threads.push_back(std::thread([tid, &lru_replacer, &value]() { 149 | int share = 1000 / 5; 150 | for (int i = 0; i < share; i++) { 151 | lru_replacer->Unpin(value[tid * share + i]); 152 | } 153 | })); 154 | } 155 | 156 | for (int i = 0; i < num_threads; i++) { 157 | threads[i].join(); 158 | } 159 | std::vector out_values; 160 | for (int i = 0; i < value_size; i++) { 161 | EXPECT_EQ(1, lru_replacer->Victim(&result)); 162 | out_values.push_back(result); 163 | } 164 | std::sort(value.begin(), value.end()); 165 | std::sort(out_values.begin(), out_values.end()); 166 | EXPECT_EQ(value, out_values); 167 | EXPECT_EQ(0, lru_replacer->Victim(&result)); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/replacer/replacer.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // replacer.h 6 | // 7 | // Identification: src/include/buffer/replacer.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #pragma once 14 | 15 | #include "common/config.h" 16 | 17 | /** 18 | * Replacer is an abstract class that tracks page usage. 19 | */ 20 | class Replacer { 21 | public: 22 | Replacer() = default; 23 | virtual ~Replacer() = default; 24 | 25 | /** 26 | * Remove the victim frame as defined by the replacement policy. 27 | * @param[out] frame_id id of frame that was removed, nullptr if no victim was found 28 | * @return true if a victim frame was found, false otherwise 29 | */ 30 | virtual bool Victim(frame_id_t *frame_id) = 0; 31 | 32 | /** 33 | * Pins a frame, indicating that it should not be victimized until it is unpinned. 34 | * @param frame_id the id of the frame to pin 35 | */ 36 | virtual void Pin(frame_id_t frame_id) = 0; 37 | 38 | /** 39 | * Unpins a frame, indicating that it can now be victimized. 40 | * @param frame_id the id of the frame to unpin 41 | */ 42 | virtual void Unpin(frame_id_t frame_id) = 0; 43 | 44 | /** @return the number of elements in the replacer that can be victimized */ 45 | virtual size_t Size() = 0; 46 | }; 47 | -------------------------------------------------------------------------------- /src/storage/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # storage module 2 | set(SOURCES 3 | disk_manager.cpp 4 | buffer_pool_manager.cpp 5 | ../replacer/replacer.h 6 | ../replacer/lru_replacer.cpp 7 | ../replacer/clock_replacer.cpp 8 | ) 9 | add_library(storage STATIC ${SOURCES}) 10 | 11 | # disk_manager_test 12 | add_library(disk STATIC disk_manager.cpp) 13 | add_executable(disk_manager_test disk_manager_test.cpp) 14 | target_link_libraries(disk_manager_test disk gtest_main) # add gtest 15 | 16 | # buffer_pool_manager_test 17 | add_executable(buffer_pool_manager_test buffer_pool_manager_test.cpp) 18 | target_link_libraries(buffer_pool_manager_test storage gtest_main) # add gtest 19 | -------------------------------------------------------------------------------- /src/storage/buffer_pool_manager.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // buffer_pool_manager.h 6 | // 7 | // Identification: src/include/buffer/buffer_pool_manager.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | //===----------------------------------------------------------------------===// 14 | // 15 | // Rucbase 16 | // 17 | // buffer_pool_manager.h 18 | // 19 | // Identification: src/storage/buffer_pool_manager.h 20 | // 21 | // Copyright (c) 2022, RUC Deke Group 22 | // 23 | //===----------------------------------------------------------------------===// 24 | 25 | #pragma once 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "common/logger.h" // for debug 35 | #include "disk_manager.h" 36 | #include "errors.h" 37 | #include "page.h" 38 | #include "replacer/clock_replacer.h" 39 | #include "replacer/lru_replacer.h" 40 | #include "replacer/replacer.h" 41 | 42 | class BufferPoolManager { 43 | private: 44 | /** 45 | * @brief Number of pages in the buffer pool. 46 | */ 47 | size_t pool_size_; 48 | /** 49 | * @brief BufferPool中的Page对象数组(指针) 50 | * @note 在构造函数中申请内存空间,折构函数中释放,大小为BUFFER_POOL_SIZE 51 | */ 52 | Page *pages_; 53 | /** 54 | * @brief 以自定义PageIdHash为哈希函数的哈希表. 55 | * @note 用于根据PageId定位其在BufferPool中的frame_id_t 56 | */ 57 | std::unordered_map page_table_; 58 | /** 59 | * @brief BufferPool空闲帧的id构成的链表 60 | */ 61 | std::list free_list_; 62 | /** 上层传入disk_manager */ 63 | DiskManager *disk_manager_; 64 | 65 | /** 66 | * @brief BufferPool页面替换策略类 67 | * 68 | */ 69 | Replacer *replacer_; 70 | 71 | /** This latch protects shared data structures */ 72 | std::mutex latch_; 73 | 74 | public: 75 | BufferPoolManager(size_t pool_size, DiskManager *disk_manager) 76 | : pool_size_(pool_size), disk_manager_(disk_manager) { 77 | // We allocate a consecutive memory space for the buffer pool. 78 | pages_ = new Page[pool_size_]; 79 | // can be changed to ClockReplacer 80 | if (REPLACER_TYPE.compare("LRU")) 81 | replacer_ = new LRUReplacer(pool_size_); 82 | else if (REPLACER_TYPE.compare("CLOCK")) 83 | replacer_ = new LRUReplacer(pool_size_); 84 | else { 85 | LOG_WARN("BufferPoolManager Replacer type defined wrong, use LRU as replacer.\n"); 86 | replacer_ = new LRUReplacer(pool_size_); 87 | } 88 | // Initially, every page is in the free list. 89 | for (size_t i = 0; i < pool_size_; ++i) { 90 | free_list_.emplace_back(static_cast(i)); // static_cast转换数据类型 91 | } 92 | } 93 | 94 | /** 95 | * @brief Destroy the Buffer Pool object 96 | * 97 | */ 98 | ~BufferPoolManager() { 99 | delete[] pages_; 100 | delete replacer_; 101 | } 102 | 103 | public: 104 | /** 105 | * Fetch the requested page from the buffer pool. 106 | * @param page_id id of page to be fetched 107 | * @return the requested page 108 | */ 109 | Page *FetchPage(PageId page_id); 110 | 111 | /** 112 | * Unpin the target page from the buffer pool. 113 | * @param page_id id of page to be unpinned 114 | * @param is_dirty true if the page should be marked as dirty, false otherwise 115 | * @return false if the page pin count is <= 0 before this call, true otherwise 116 | */ 117 | bool UnpinPage(PageId page_id, bool is_dirty); 118 | 119 | /** 120 | * Flushes the target page to disk. 121 | * @param page_id id of page to be flushed, cannot be INVALID_PAGE_ID 122 | * @return false if the page could not be found in the page table, true otherwise 123 | */ 124 | bool FlushPage(PageId page_id); 125 | 126 | /** 127 | * Creates a new page in the buffer pool. 128 | * @param[out] page_id id of created page 129 | * @return nullptr if no new pages could be created, otherwise pointer to new page 130 | */ 131 | Page *NewPage(PageId *page_id); 132 | 133 | /** 134 | * Deletes a page from the buffer pool. 135 | * @param page_id id of page to be deleted 136 | * @return false if the page exists but could not be deleted, true if the page didn't exist or deletion succeeded 137 | */ 138 | bool DeletePage(PageId page_id); 139 | 140 | /** 141 | * Flushes all the pages in the buffer pool to disk. 142 | */ 143 | void FlushAllPages(int fd); 144 | 145 | private: 146 | bool FindVictimPage(frame_id_t *frame_id); 147 | 148 | void UpdatePage(Page *page, PageId new_page_id, frame_id_t new_frame_id); 149 | }; -------------------------------------------------------------------------------- /src/storage/disk_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "storage/disk_manager.h" 2 | 3 | #include // for assert 4 | #include // for memset 5 | #include // for stat 6 | #include // for lseek 7 | 8 | #include "defs.h" 9 | 10 | DiskManager::DiskManager() { memset(fd2pageno_, 0, MAX_FD * (sizeof(std::atomic) / sizeof(char))); } 11 | 12 | /** 13 | * @brief Write the contents of the specified page into disk file 14 | * 15 | */ 16 | void DiskManager::write_page(int fd, page_id_t page_no, const char *offset, int num_bytes) { 17 | // todo: 18 | // 1.lseek()定位到文件头,通过(fd,page_no)可以定位指定页面及其在磁盘文件中的偏移量 19 | // 2.调用write()函数 20 | // 注意处理异常 21 | lseek(fd,page_no*PAGE_SIZE,SEEK_SET); 22 | write(fd,offset,num_bytes); 23 | } 24 | 25 | /** 26 | * @brief Read the contents of the specified page into the given memory area 27 | */ 28 | void DiskManager::read_page(int fd, page_id_t page_no, char *offset, int num_bytes) { 29 | // todo: 30 | // 1.lseek()定位到文件头,通过(fd,page_no)可以定位指定页面及其在磁盘文件中的偏移量 31 | // 2.调用read()函数 32 | // 注意处理异常 33 | lseek(fd,page_no*PAGE_SIZE,SEEK_SET); 34 | read(fd,offset,num_bytes); 35 | } 36 | 37 | /** 38 | * @brief Allocate new page (operations like create index/table) 39 | * For now just keep an increasing counter 40 | */ 41 | page_id_t DiskManager::AllocatePage(int fd) { 42 | // todo: 43 | // 简单的自增分配策略,指定文件的页面编号加1 44 | page_id_t tmp = get_fd2pageno(fd); 45 | set_fd2pageno(fd,tmp+1); 46 | return tmp; 47 | } 48 | 49 | /** 50 | * @brief Deallocate page (operations like drop index/table) 51 | * Need bitmap in header page for tracking pages 52 | * This does not actually need to do anything for now. 53 | */ 54 | void DiskManager::DeallocatePage(__attribute__((unused)) page_id_t page_id) {} 55 | 56 | bool DiskManager::is_dir(const std::string &path) { 57 | struct stat st; 58 | return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode); 59 | } 60 | 61 | void DiskManager::create_dir(const std::string &path) { 62 | // Create a subdirectory 63 | std::string cmd = "mkdir " + path; 64 | if (system(cmd.c_str()) < 0) { // 创建一个名为path的目录 65 | throw UnixError(); 66 | } 67 | } 68 | 69 | void DiskManager::destroy_dir(const std::string &path) { 70 | std::string cmd = "rm -r " + path; 71 | if (system(cmd.c_str()) < 0) { 72 | throw UnixError(); 73 | } 74 | } 75 | 76 | /** 77 | * @brief 用于判断指定路径文件是否存在 78 | */ 79 | bool DiskManager::is_file(const std::string &path) { 80 | // todo:FINISH 81 | // 用struct stat获取文件信息 82 | // 83 | struct stat st; 84 | return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode); 85 | } 86 | 87 | /** 88 | * @brief 用于创建指定路径文件 89 | */ 90 | void DiskManager::create_file(const std::string &path) { 91 | // todo:FINISH 92 | // 调用open()函数,使用O_CREAT模式 93 | // 注意不能重复创建相同文件 94 | if (is_file(path)) 95 | { 96 | throw FileExistsError(path); 97 | return; 98 | } 99 | int fd = open(path.c_str(), O_CREAT|O_RDWR,0600); 100 | if (fd == -1) 101 | { 102 | 103 | } 104 | return; 105 | } 106 | 107 | /** 108 | * @brief 用于删除指定路径文件 109 | */ 110 | void DiskManager::destroy_file(const std::string &path) { 111 | // todo:FINISH 112 | // 调用unlink()函数 113 | // 注意不能删除未关闭的文件 114 | if(!is_file(path)) 115 | { 116 | throw FileNotFoundError(path); 117 | return; 118 | } 119 | // 说明打开文件列表里面没有 120 | if (path2fd_.find(path) == path2fd_.end()) { 121 | unlink(path.c_str()); 122 | } 123 | } 124 | 125 | /** 126 | * @brief 用于打开指定路径文件 127 | */ 128 | int DiskManager::open_file(const std::string &path) { 129 | // todo:FINISH 130 | // 调用open()函数,使用O_RDWR模式 131 | // 注意不能重复打开相同文件,并且需要更新文件打开列表 132 | if(!is_file(path)) 133 | { 134 | throw FileNotFoundError(path); 135 | } 136 | // 说明打开文件列表里面没有 137 | if (path2fd_.find(path) == path2fd_.end()) { 138 | int fd = open(path.c_str(), O_RDWR); 139 | path2fd_.insert({path, fd}); 140 | fd2path_.insert({fd, path}); 141 | return fd; 142 | } 143 | return -1; 144 | } 145 | 146 | /** 147 | * @brief 用于关闭指定路径文件 148 | */ 149 | void DiskManager::close_file(int fd) { 150 | // todo:FINISH 151 | // 调用close()函数 152 | // 注意不能关闭未打开的文件,并且需要更新文件打开列表 153 | 154 | // 说明打开文件列表里面有 155 | if(fd2path_.find(fd)!=fd2path_.end()) 156 | { 157 | std::string path = fd2path_.at(fd); 158 | if(!is_file(path)) 159 | { 160 | throw FileNotFoundError(path); 161 | } 162 | close(fd); 163 | path2fd_.erase(path); 164 | fd2path_.erase(fd); 165 | } 166 | } 167 | 168 | int DiskManager::GetFileSize(const std::string &file_name) { 169 | struct stat stat_buf; 170 | int rc = stat(file_name.c_str(), &stat_buf); 171 | return rc == 0 ? stat_buf.st_size : -1; 172 | } 173 | 174 | std::string DiskManager::GetFileName(int fd) { 175 | if (!fd2path_.count(fd)) { 176 | throw FileNotOpenError(fd); 177 | } 178 | return fd2path_[fd]; 179 | } 180 | 181 | int DiskManager::GetFileFd(const std::string &file_name) { 182 | if (!path2fd_.count(file_name)) { 183 | return open_file(file_name); 184 | } 185 | return path2fd_[file_name]; 186 | } 187 | 188 | bool DiskManager::ReadLog(char *log_data, int size, int offset, int prev_log_end) { 189 | // read log file from the previous end 190 | if (log_fd_ == -1) { 191 | log_fd_ = open_file(LOG_FILE_NAME); 192 | } 193 | offset += prev_log_end; 194 | int file_size = GetFileSize(LOG_FILE_NAME); 195 | if (offset >= file_size) { 196 | return false; 197 | } 198 | 199 | size = std::min(size, file_size - offset); 200 | lseek(log_fd_, offset, SEEK_SET); 201 | ssize_t bytes_read = read(log_fd_, log_data, size); 202 | if (bytes_read != size) { 203 | throw UnixError(); 204 | } 205 | return true; 206 | } 207 | 208 | void DiskManager::WriteLog(char *log_data, int size) { 209 | if (log_fd_ == -1) { 210 | log_fd_ = open_file(LOG_FILE_NAME); 211 | } 212 | 213 | // write from the file_end 214 | lseek(log_fd_, 0, SEEK_END); 215 | ssize_t bytes_write = write(log_fd_, log_data, size); 216 | if (bytes_write != size) { 217 | throw UnixError(); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/storage/disk_manager.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // Rucbase 4 | // 5 | // disk_manager.h 6 | // 7 | // Identification: src/storage/disk_manager.h 8 | // 9 | // Copyright (c) 2022, RUC Deke Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #pragma once 14 | 15 | #include // for open/close 16 | #include // for S_ISREG 17 | #include // for open/close 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "common/config.h" 26 | #include "errors.h" // for throw Exception 27 | 28 | /** 29 | * @brief DiskManager takes care of the allocation and deallocation of pages within a database. It performs the reading 30 | * and writing of pages to and from disk, providing a logical file layer within the context of a database management 31 | * system. 32 | */ 33 | class DiskManager { 34 | public: 35 | explicit DiskManager(); 36 | 37 | ~DiskManager() = default; 38 | 39 | /** 40 | * @brief 将buffer中的页面数据写回diskFile中 41 | */ 42 | void write_page(int fd, page_id_t page_no, const char *offset, int num_bytes); 43 | 44 | /** 45 | * @brief 读取指定编号的页面部分字节到buffer中 46 | * 47 | * @param fd 页面所在文件开启后的文件描述符 48 | * @param page_no 指定页面编号 49 | * @param offset 读取的内容写入buffer 50 | * @param num_bytes 读取的字节数 51 | */ 52 | void read_page(int fd, page_id_t page_no, char *offset, int num_bytes); 53 | 54 | /** 55 | * @brief Allocate a page on disk. 56 | * @return the page_no of the allocated page 57 | */ 58 | page_id_t AllocatePage(int fd); 59 | 60 | /** 61 | * @brief Deallocate a page on disk. 62 | * @param page_id id of the page to deallocate 63 | */ 64 | void DeallocatePage(page_id_t page_id); 65 | 66 | // 目录操作 67 | bool is_dir(const std::string &path); 68 | 69 | void create_dir(const std::string &path); 70 | 71 | void destroy_dir(const std::string &path); 72 | 73 | // 文件操作 74 | bool is_file(const std::string &path); 75 | 76 | void create_file(const std::string &path); 77 | 78 | void destroy_file(const std::string &path); 79 | 80 | int open_file(const std::string &path); 81 | 82 | void close_file(int fd); 83 | 84 | int GetFileSize(const std::string &file_name); 85 | 86 | std::string GetFileName(int fd); 87 | 88 | int GetFileFd(const std::string &file_name); 89 | 90 | // LOG操作 91 | bool ReadLog(char *log_data, int size, int offset, int prev_log_end); 92 | 93 | void WriteLog(char *log_data, int size); 94 | 95 | void SetLogFd(int log_fd) { log_fd_ = log_fd; } 96 | 97 | int GetLogFd() { return log_fd_; } 98 | 99 | // 在fd对应文件中,从start_page_no开始分配page_no 100 | void set_fd2pageno(int fd, int start_page_no) { fd2pageno_[fd] = start_page_no; } 101 | 102 | page_id_t get_fd2pageno(int fd) { return fd2pageno_[fd]; } 103 | 104 | static constexpr int MAX_FD = 8192; 105 | 106 | private: 107 | // 文件打开列表,用于记录文件是否被打开 108 | std::unordered_map path2fd_; //哈希表 109 | std::unordered_map fd2path_; //哈希表 110 | 111 | int log_fd_ = -1; // log file 112 | std::atomic fd2pageno_[MAX_FD]{}; // 在文件fd中分配的page no个数 113 | }; 114 | -------------------------------------------------------------------------------- /src/storage/disk_manager_test.cpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // Rucbase 4 | // 5 | // disk_manager_test.cpp 6 | // 7 | // Identification: src/storage/disk_manager_test.cpp 8 | // 9 | // Copyright (c) 2022, RUC Deke Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #include "disk_manager.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "gtest/gtest.h" 21 | 22 | constexpr int MAX_FILES = 32; 23 | constexpr int MAX_PAGES = 128; 24 | const std::string TEST_DB_NAME = "DiskManagerTest_db"; // 以TEST_DB_NAME作为存放测试文件的根目录名 25 | 26 | // Add by jiawen 27 | class DiskManagerTest : public ::testing::Test { 28 | public: 29 | std::unique_ptr disk_manager_; 30 | 31 | public: 32 | // This function is called before every test. 33 | void SetUp() override { 34 | ::testing::Test::SetUp(); 35 | // 对于每个测试点,创建一个disk manager 36 | disk_manager_ = std::make_unique(); 37 | // 如果测试目录不存在,则先创建测试目录 38 | if (!disk_manager_->is_dir(TEST_DB_NAME)) { 39 | disk_manager_->create_dir(TEST_DB_NAME); 40 | } 41 | assert(disk_manager_->is_dir(TEST_DB_NAME)); // 检查是否创建目录成功 42 | // 进入测试目录 43 | if (chdir(TEST_DB_NAME.c_str()) < 0) { 44 | throw UnixError(); 45 | } 46 | } 47 | 48 | // This function is called after every test. 49 | void TearDown() override { 50 | // 返回上一层目录 51 | if (chdir("..") < 0) { 52 | throw UnixError(); 53 | } 54 | assert(disk_manager_->is_dir(TEST_DB_NAME)); 55 | }; 56 | 57 | /** 58 | * @brief 将buf填充size个字节的随机数据 59 | */ 60 | void rand_buf(char *buf, int size) { 61 | srand((unsigned)time(nullptr)); 62 | for (int i = 0; i < size; i++) { 63 | int rand_ch = rand() & 0xff; 64 | buf[i] = rand_ch; 65 | } 66 | } 67 | }; 68 | 69 | /** 70 | * @brief 测试文件操作 create/open/close/destroy file 71 | * @note lab1 计分:5 points 72 | */ 73 | TEST_F(DiskManagerTest, FileOperation) { 74 | std::vector filenames(MAX_FILES); // MAX_FILES=32 75 | std::unordered_map fd2name; // fd -> filename 76 | for (size_t i = 0; i < filenames.size(); i++) { // 创建MAX_FILES个文件 77 | auto &filename = filenames[i]; 78 | filename = "FileOperationTestFile" + std::to_string(i); 79 | // 清理残留文件 80 | if (disk_manager_->is_file(filename)) { 81 | disk_manager_->destroy_file(filename); 82 | } 83 | // 测试异常:如果没有创建文件就打开文件 84 | try { 85 | disk_manager_->open_file(filename); 86 | assert(false); 87 | } catch (const FileNotFoundError &e) { 88 | } 89 | // 创建文件 90 | disk_manager_->create_file(filename); 91 | EXPECT_EQ(disk_manager_->is_file(filename), true); // 检查是否创建文件成功 92 | try { 93 | disk_manager_->create_file(filename); 94 | assert(false); 95 | } catch (const FileExistsError &e) { 96 | } 97 | // 打开文件 98 | int fd = disk_manager_->open_file(filename); 99 | fd2name[fd] = filename; 100 | 101 | // 关闭后重新打开 102 | if (rand() % 5 == 0) { 103 | disk_manager_->close_file(fd); 104 | int new_fd = disk_manager_->open_file(filename); 105 | fd2name[new_fd] = filename; 106 | } 107 | } 108 | 109 | // 关闭&删除文件 110 | for (auto &entry : fd2name) { 111 | int fd = entry.first; 112 | auto &filename = entry.second; 113 | disk_manager_->close_file(fd); 114 | disk_manager_->destroy_file(filename); 115 | EXPECT_EQ(disk_manager_->is_file(filename), false); // 检查是否删除文件成功 116 | try { 117 | disk_manager_->destroy_file(filename); 118 | assert(false); 119 | } catch (const FileNotFoundError &e) { 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * @brief 测试读写页面,分配页面编号 read/write page, allocate page_no 126 | * @note lab1 计分:5 points 127 | */ 128 | TEST_F(DiskManagerTest, PageOperation) { 129 | const std::string filename = "PageOperationTestFile"; 130 | // 清理残留文件 131 | if (disk_manager_->is_file(filename)) { 132 | disk_manager_->destroy_file(filename); 133 | } 134 | // 创建文件 135 | disk_manager_->create_file(filename); 136 | EXPECT_EQ(disk_manager_->is_file(filename), true); 137 | // 打开文件 138 | int fd = disk_manager_->open_file(filename); 139 | // 初始页面编号为0 140 | disk_manager_->set_fd2pageno(fd, 0); 141 | 142 | // 读写页面&分配页面编号(在单个文件上测试) 143 | char buf[PAGE_SIZE] = {0}; 144 | char data[PAGE_SIZE] = {0}; 145 | for (int page_no = 0; page_no < MAX_PAGES; page_no++) { 146 | // 分配页面编号 147 | int ret_page_no = disk_manager_->AllocatePage(fd); // 注意此处返回值是分配编号之前的值 148 | EXPECT_EQ(ret_page_no, page_no); 149 | // 读写页面 150 | rand_buf(data, PAGE_SIZE); // generate data 151 | disk_manager_->write_page(fd, page_no, data, PAGE_SIZE); // write data to disk (data -> disk page) 152 | std::memset(buf, 0, sizeof(buf)); // clear buf 153 | disk_manager_->read_page(fd, page_no, buf, PAGE_SIZE); // read buf from disk (disk page -> buf) 154 | EXPECT_EQ(std::memcmp(buf, data, sizeof(buf)), 0); // check if buf == data 155 | } 156 | 157 | // 关闭&删除文件 158 | disk_manager_->close_file(fd); 159 | disk_manager_->destroy_file(filename); 160 | EXPECT_EQ(disk_manager_->is_file(filename), false); 161 | } 162 | -------------------------------------------------------------------------------- /src/storage/page.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // BusTub 4 | // 5 | // page.h 6 | // 7 | // Identification: src/include/storage/page/page.h 8 | // 9 | // Copyright (c) 2015-2019, Carnegie Mellon University Database Group 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | //===----------------------------------------------------------------------===// 14 | // 15 | // Rucbase 16 | // 17 | // page.h 18 | // 19 | // Identification: src/storage/page.h 20 | // 21 | // Copyright (c) 2022, RUC Deke Group 22 | // 23 | //===----------------------------------------------------------------------===// 24 | 25 | #pragma once 26 | 27 | #include "common/config.h" 28 | #include "common/rwlatch.h" 29 | 30 | /** 31 | @brief 存储层每个Page的id的声明 32 | */ 33 | struct PageId { 34 | int fd; // Page所在的磁盘文件开启后的文件描述符, 来定位打开的文件在内存中的位置 35 | page_id_t page_no = INVALID_PAGE_ID; 36 | 37 | friend bool operator==(const PageId &x, const PageId &y) { return x.fd == y.fd && x.page_no == y.page_no; } 38 | }; 39 | 40 | // PageId的自定义哈希算法, 用于构建unordered_map 41 | struct PageIdHash { 42 | size_t operator()(const PageId &x) const { return (x.fd << 16) | x.page_no; } 43 | }; 44 | 45 | /** 46 | @brief Page类声明, Page是rucbase数据块的单位. 47 | @note Page是负责数据操作Record模块的操作对象. 48 | @note Page对象在磁盘上有文件存储, 若在Buffer中则有帧偏移, 并非特指Buffer或Disk上的数据 49 | */ 50 | class Page { 51 | friend class BufferPoolManager; 52 | 53 | public: 54 | /** Constructor. Zeros out the page data. */ 55 | Page() { ResetMemory(); } 56 | 57 | /** Default destructor. */ 58 | ~Page() = default; 59 | 60 | PageId GetPageId() const { return id_; } 61 | 62 | /** @return the actual data contained within this page */ 63 | inline char *GetData() { return data_; } 64 | 65 | bool IsDirty() const { return is_dirty_; } 66 | 67 | /** Acquire the page write latch. */ 68 | inline void WLatch() { rwlatch_.WLock(); } 69 | 70 | /** Release the page write latch. */ 71 | inline void WUnlatch() { rwlatch_.WUnlock(); } 72 | 73 | /** Acquire the page read latch. */ 74 | inline void RLatch() { rwlatch_.RLock(); } 75 | 76 | /** Release the page read latch. */ 77 | inline void RUnlatch() { rwlatch_.RUnlock(); } 78 | 79 | static constexpr size_t OFFSET_PAGE_START = 0; 80 | static constexpr size_t OFFSET_LSN = 0; 81 | static constexpr size_t OFFSET_PAGE_HDR = 4; 82 | 83 | inline lsn_t GetPageLsn() { return *reinterpret_cast(GetData() + OFFSET_LSN) ; } 84 | 85 | inline void SetPageLsn(lsn_t page_lsn) { memcpy(GetData() + OFFSET_LSN, &page_lsn, sizeof(lsn_t)); } 86 | 87 | private: 88 | void ResetMemory() { memset(data_, OFFSET_PAGE_START, PAGE_SIZE); } // 将data_的PAGE_SIZE个字节填充为0 89 | 90 | /** page的唯一标识符 */ 91 | PageId id_; 92 | 93 | /** The actual data that is stored within a page. 94 | * 该页面在bufferPool中的偏移地址 95 | */ 96 | char data_[PAGE_SIZE] = {}; 97 | 98 | /** 脏页判断 */ 99 | bool is_dirty_ = false; 100 | 101 | /** The pin count of this page. */ 102 | int pin_count_ = 0; 103 | 104 | /** Page latch. */ 105 | ReaderWriterLatch rwlatch_; 106 | }; -------------------------------------------------------------------------------- /src/system/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES sm_manager.cpp) 2 | add_library(system STATIC ${SOURCES}) 3 | target_link_libraries(system index record) 4 | 5 | 6 | # sm_gtest 7 | add_executable(sm_gtest sm_gtest.cpp) 8 | target_link_libraries(sm_gtest system gtest_main) 9 | -------------------------------------------------------------------------------- /src/system/sm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sm_manager.h" 4 | #include "sm_meta.h" 5 | #include "sm_defs.h" 6 | -------------------------------------------------------------------------------- /src/system/sm_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #include 5 | 6 | static const std::string DB_META_NAME = "db.meta"; 7 | -------------------------------------------------------------------------------- /src/system/sm_gtest.cpp: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | 3 | #include 4 | #include 5 | 6 | #include "gtest/gtest.h" 7 | #include "record/rm_manager.h" 8 | #include "sm.h" 9 | #define BUFFER_LENGTH 8192 10 | 11 | // 测试SmManager的函数 12 | TEST(SystemManagerTest, SimpleTest) { 13 | std::string db = "db"; 14 | std::string tab1 = "tab1"; 15 | std::string tab2 = "tab2"; 16 | 17 | // 创建SmManager类的对象sm_manager 18 | auto disk_manager = std::make_unique(); 19 | auto buffer_pool_manager = std::make_unique(BUFFER_POOL_SIZE, disk_manager.get()); 20 | auto rm_manager = std::make_unique(disk_manager.get(), buffer_pool_manager.get()); 21 | auto ix_manager = std::make_unique(disk_manager.get(), buffer_pool_manager.get()); 22 | auto sm_manager = 23 | std::make_unique(disk_manager.get(), buffer_pool_manager.get(), rm_manager.get(), ix_manager.get()); 24 | char *result = new char[BUFFER_LENGTH]; 25 | int offset = 0; 26 | Context *context = new Context(nullptr, nullptr, nullptr, result, &offset); 27 | 28 | if (sm_manager->is_dir(db)) { 29 | sm_manager->drop_db(db); 30 | } 31 | // Cannot use a database that does not exist 32 | try { 33 | sm_manager->open_db(db); 34 | assert(0); 35 | } catch (DatabaseNotFoundError &) { 36 | } 37 | // Create database 38 | sm_manager->create_db(db); 39 | // Cannot re-create database 40 | try { 41 | sm_manager->create_db(db); 42 | assert(0); 43 | } catch (DatabaseExistsError &) { 44 | } 45 | // Open database 46 | sm_manager->open_db(db); 47 | std::vector col_defs = {{.name = "a", .type = TYPE_INT, .len = 4}, 48 | {.name = "b", .type = TYPE_FLOAT, .len = 4}, 49 | {.name = "c", .type = TYPE_STRING, .len = 256}}; 50 | // Create table 1 51 | sm_manager->create_table(tab1, col_defs, context); 52 | // Cannot re-create table 53 | try { 54 | sm_manager->create_table(tab1, col_defs, context); 55 | assert(0); 56 | } catch (TableExistsError &) { 57 | } 58 | // Create table 2 59 | sm_manager->create_table(tab2, col_defs, context); 60 | // Create index for table 1 61 | sm_manager->create_index(tab1, "a", context); 62 | sm_manager->create_index(tab1, "c", context); 63 | // Cannot re-create index 64 | try { 65 | sm_manager->create_index(tab1, "a", context); 66 | assert(0); 67 | } catch (IndexExistsError &) { 68 | } 69 | // Create index for table 2 70 | sm_manager->create_index(tab2, "b", context); 71 | // Drop index of table 1 72 | sm_manager->drop_index(tab1, "a", context); 73 | // Cannot drop index that does not exist 74 | try { 75 | sm_manager->drop_index(tab1, "b", context); 76 | assert(0); 77 | } catch (IndexNotFoundError &) { 78 | } 79 | // Drop index 80 | sm_manager->drop_table(tab1, context); 81 | // Cannot drop table that does not exist 82 | try { 83 | sm_manager->drop_table(tab1, context); 84 | assert(0); 85 | } catch (TableNotFoundError &) { 86 | } 87 | // Clean up 88 | sm_manager->close_db(); 89 | sm_manager->drop_db(db); 90 | } -------------------------------------------------------------------------------- /src/system/sm_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "index/ix.h" 4 | // #include "record/rm.h" 5 | #include "common/context.h" 6 | #include "record/rm_file_handle.h" 7 | #include "sm_defs.h" 8 | #include "sm_meta.h" 9 | 10 | class Context; 11 | 12 | struct ColDef { 13 | std::string name; // Column name 14 | ColType type; // Type of column 15 | int len; // Length of column 16 | }; 17 | 18 | // SmManager类似于CMU中的Catalog 19 | // 管理数据库中db, table, index的元数据,支持create/drop/open/close等操作 20 | // 每个SmManager对应一个db 21 | class SmManager { 22 | public: 23 | DbMeta db_; // create_db时将会将DbMeta写入文件,open_db时将会从文件中读出DbMeta 24 | std::unordered_map> fhs_; // file name -> record file handle 25 | std::unordered_map> ihs_; // file name -> index file handle 26 | private: 27 | DiskManager *disk_manager_; 28 | BufferPoolManager *buffer_pool_manager_; 29 | RmManager *rm_manager_; 30 | IxManager *ix_manager_; 31 | // TODO: 全部改成私有变量,并且改成指针形式 32 | // DbMeta *db_; 33 | // std::map> *fhs_; 34 | // std::map> *ihs_; 35 | 36 | public: 37 | SmManager(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager, RmManager *rm_manager, 38 | IxManager *ix_manager) 39 | : disk_manager_(disk_manager), 40 | buffer_pool_manager_(buffer_pool_manager), 41 | rm_manager_(rm_manager), 42 | ix_manager_(ix_manager) { 43 | // db_ = new DbMeta(); 44 | } 45 | 46 | ~SmManager() { 47 | // delete db_; 48 | } 49 | 50 | // TODO: Get private variables (注意,这里的get方法都必须返回指针,否则上层调用会出问题) 51 | // DbMeta *get_db() { return db_; } 52 | 53 | // std::map> *get_fhs() { return fhs_; } 54 | 55 | // std::map> *get_ihs() { return ihs_; } 56 | 57 | RmManager *get_rm_manager() { return rm_manager_; } // called in some excutors to modify record 58 | 59 | IxManager *get_ix_manager() { return ix_manager_; } // called in some excutors to modify index 60 | 61 | BufferPoolManager *get_bpm() { return buffer_pool_manager_; } 62 | 63 | // Database management 64 | bool is_dir(const std::string &db_name); 65 | 66 | void create_db(const std::string &db_name); 67 | 68 | void drop_db(const std::string &db_name); 69 | 70 | void open_db(const std::string &db_name); 71 | 72 | void close_db(); 73 | 74 | // Table management 75 | void show_tables(Context *context); 76 | 77 | void desc_table(const std::string &tab_name, Context *context); 78 | 79 | void create_table(const std::string &tab_name, const std::vector &col_defs, Context *context); 80 | 81 | void drop_table(const std::string &tab_name, Context *context); 82 | 83 | void apply_drop_table(const std::string &tab_name, Context *context); 84 | 85 | // Index management 86 | void create_index(const std::string &tab_name, const std::string &col_name, Context *context); 87 | 88 | void drop_index(const std::string &tab_name, const std::string &col_name, Context *context); 89 | 90 | void apply_drop_index(const std::string &tab_name, const std::string &col_name, Context *context); 91 | 92 | // Transaction rollback management 93 | /** 94 | * @brief rollback the insert operation 95 | * 96 | * @param tab_name the name of the table 97 | * @param rid the rid of the record 98 | * @param txn the transaction 99 | */ 100 | void rollback_insert(const std::string &tab_name, const Rid &rid, Context *context); 101 | 102 | /** 103 | * @brief rollback the delete operation 104 | * 105 | * @param tab_name the name of the table 106 | * @param rid the value of the deleted record 107 | * @param txn the transaction 108 | */ 109 | void rollback_delete(const std::string &tab_name, const RmRecord &record, Context *context); 110 | 111 | /** 112 | * @brief rollback the update operation 113 | * 114 | * @param tab_name the name of the table 115 | * @param rid the rid of the record 116 | * @param record the old value of the record 117 | * @param txn the transaction 118 | */ 119 | void rollback_update(const std::string &tab_name, const Rid &rid, const RmRecord &record, Context *context); 120 | 121 | /** 122 | * @brief rollback the table drop operation 123 | * 124 | * @param tab_name the name of the deleted table 125 | */ 126 | void rollback_drop_table(const std::string &tab_name, Context *context); 127 | 128 | /** 129 | * @brief rollback the table create operation 130 | * 131 | * @param tab_name the name of the created table 132 | */ 133 | void rollback_create_table(const std::string &tab_name, Context *context); 134 | 135 | /** 136 | * @brief rollback the create index operation 137 | * 138 | * @param tab_name the name of the table 139 | * @param col_name the name of the column on which index is created 140 | */ 141 | void rollback_create_index(const std::string &tab_name, const std::string &col_name, Context *context); 142 | 143 | /** 144 | * @brief rollback the drop index operation 145 | * 146 | * @param tab_name the name of the table 147 | * @param col_name the name of the column on which index is created 148 | */ 149 | void rollback_drop_index(const std::string &tab_name, const std::string &col_name, Context *context); 150 | }; 151 | -------------------------------------------------------------------------------- /src/system/sm_meta.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "errors.h" 10 | #include "sm_defs.h" 11 | 12 | struct ColMeta { 13 | std::string tab_name; // 字段所属表名称 14 | std::string name; // 字段名称 15 | ColType type; // 字段类型 16 | int len; // 字段长度 17 | int offset; // 字段位于记录中的偏移量 18 | bool index; // 该字段上是否建立索引 19 | 20 | friend std::ostream &operator<<(std::ostream &os, const ColMeta &col) { 21 | // ColMeta中有各个基本类型的变量,然后调用重载的这些变量的操作符<<(具体实现逻辑在defs.h) 22 | return os << col.tab_name << ' ' << col.name << ' ' << col.type << ' ' << col.len << ' ' << col.offset << ' ' 23 | << col.index; 24 | } 25 | 26 | friend std::istream &operator>>(std::istream &is, ColMeta &col) { 27 | return is >> col.tab_name >> col.name >> col.type >> col.len >> col.offset >> col.index; 28 | } 29 | }; 30 | 31 | struct TabMeta { 32 | std::string name; 33 | std::vector cols; 34 | 35 | /** 36 | * @brief 根据列名在本表元数据结构体中查找是否有该名字的列 37 | * 38 | * @param col_name 目标列名 39 | * @return true 40 | * @return false 41 | */ 42 | bool is_col(const std::string &col_name) const { 43 | for (const auto &col : cols) { 44 | if (col.name == col_name) { 45 | return true; 46 | } 47 | } 48 | return false; 49 | } 50 | /** 51 | * @brief 根据列名获得列元数据ColMeta 52 | * 53 | * @param col_name 目标列名 54 | * @return std::vector::iterator 55 | */ 56 | std::vector::iterator get_col(const std::string &col_name) { 57 | std::vector::iterator it; 58 | for (it = cols.begin(); it != cols.end(); it++) { 59 | if (it->name == col_name) { 60 | return it; 61 | } 62 | } 63 | return it; 64 | } 65 | 66 | friend std::ostream &operator<<(std::ostream &os, const TabMeta &tab) { 67 | os << tab.name << '\n' << tab.cols.size() << '\n'; 68 | for (auto &col : tab.cols) { 69 | os << col << '\n'; // col是ColMeta类型,然后调用重载的ColMeta的操作符<< 70 | } 71 | return os; 72 | } 73 | 74 | friend std::istream &operator>>(std::istream &is, TabMeta &tab) { 75 | size_t n; 76 | is >> tab.name >> n; 77 | for (size_t i = 0; i < n; i++) { 78 | ColMeta col; 79 | is >> col; 80 | tab.cols.push_back(col); 81 | } 82 | return is; 83 | } 84 | }; 85 | 86 | // 注意重载了操作符 << 和 >>,这需要更底层同样重载TabMeta、ColMeta的操作符 << 和 >> 87 | class DbMeta { 88 | friend class SmManager; 89 | 90 | private: 91 | std::string name_; // 数据库名称 92 | std::map tabs_; // 数据库内的表名称和元数据的映射 93 | 94 | public: 95 | // DbMeta(std::string name) : name_(name) {} 96 | 97 | bool is_table(const std::string &tab_name) const { return tabs_.find(tab_name) != tabs_.end(); } 98 | 99 | TabMeta &get_table(const std::string &tab_name) { 100 | auto pos = tabs_.find(tab_name);//?这里已经写了,但是delete_mark是啥? 101 | if (pos == tabs_.end()) { 102 | throw TableNotFoundError(tab_name); 103 | } 104 | return pos->second; 105 | } 106 | 107 | // 重载操作符 << 108 | friend std::ostream &operator<<(std::ostream &os, const DbMeta &db_meta) { 109 | os << db_meta.name_ << '\n' << db_meta.tabs_.size() << '\n'; 110 | for (auto &entry : db_meta.tabs_) { 111 | os << entry.second << '\n'; // entry.second是TabMeta类型,然后调用重载的TabMeta的操作符<< 112 | } 113 | return os; 114 | } 115 | 116 | friend std::istream &operator>>(std::istream &is, DbMeta &db_meta) { 117 | size_t n; 118 | is >> db_meta.name_ >> n; 119 | for (size_t i = 0; i < n; i++) { 120 | TabMeta tab; 121 | is >> tab; 122 | db_meta.tabs_[tab.name] = tab; 123 | } 124 | return is; 125 | } 126 | }; 127 | -------------------------------------------------------------------------------- /src/transaction/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES concurrency/lock_manager.cpp transaction_manager.cpp) 2 | add_library(transaction STATIC ${SOURCES}) 3 | target_link_libraries(transaction system recovery pthread) 4 | 5 | add_executable(txn_manager_test txn_manager_test.cpp) 6 | target_link_libraries(txn_manager_test transaction execution parser gtest_main) 7 | 8 | add_executable(lock_manager_test lock_manager_test.cpp) 9 | target_link_libraries(lock_manager_test transaction execution gtest_main) 10 | 11 | # concurrency_test 12 | add_executable(concurrency_test concurrency_test.cpp) 13 | target_link_libraries(concurrency_test transaction execution parser gtest_main) -------------------------------------------------------------------------------- /src/transaction/concurrency/lock_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "transaction/transaction.h" 6 | 7 | static const std::string GroupLockModeStr[10] = {"NON_LOCK", "IS", "IX", "S", "X", "SIX"}; 8 | 9 | class LockManager { 10 | enum class LockMode { SHARED, EXLUCSIVE, INTENTION_SHARED, INTENTION_EXCLUSIVE, S_IX }; 11 | /** 12 | * @brief the locks on data 13 | * NON_LOCK: there is no lock on the data 14 | * IS : intention shared lock 15 | * IX : intention exclusive lock 16 | * S : shared lock 17 | * X : exclusive lock 18 | * SIX : shared lock + intention exclusive lock(locks are from the same transaction) 19 | */ 20 | enum class GroupLockMode { NON_LOCK, IS, IX, S, X, SIX}; 21 | 22 | class LockRequest { 23 | public: 24 | LockRequest(txn_id_t txn_id, LockMode lock_mode) 25 | : txn_id_(txn_id), lock_mode_(lock_mode), granted_(false) {} 26 | 27 | txn_id_t txn_id_; 28 | LockMode lock_mode_; 29 | bool granted_; 30 | }; 31 | 32 | class LockRequestQueue { 33 | public: 34 | // locks on the data 35 | std::list request_queue_; // 位于同一个数据对象上的锁的队列 36 | // notify lock request 37 | std::condition_variable cv_; // 条件变量,用于唤醒request_queue_ 38 | // the group_lock_mode_ decides whether to grant a lock request or not 39 | GroupLockMode group_lock_mode_ = GroupLockMode::NON_LOCK; // 当前数据对象上加锁的组模式 40 | // waiting bit: if there is a lock request which is not granted, the waiting -bit will be true 41 | bool is_waiting_ = false; 42 | // upgrading_flag: if there is a lock waiting for upgrading, other transactions that request for upgrading will be aborted 43 | // (deadlock prevetion) 44 | bool upgrading_ = false; // 当前队列中是否存在一个正在upgrade的锁 45 | // the number of shared locks 46 | int shared_lock_num_ = 0; 47 | // the number of IX locks 48 | int IX_lock_num_ = 0; 49 | }; 50 | 51 | public: 52 | LockManager() {} 53 | 54 | ~LockManager() {} 55 | 56 | bool LockSharedOnRecord(Transaction *txn, const Rid &rid, int tab_fd); 57 | 58 | bool LockExclusiveOnRecord(Transaction *txn, const Rid &rid, int tab_fd); 59 | 60 | bool LockSharedOnTable(Transaction *txn, int tab_fd); 61 | 62 | bool LockExclusiveOnTable(Transaction *txn, int tab_fd); 63 | 64 | bool LockISOnTable(Transaction *txn, int tab_fd); 65 | 66 | bool LockIXOnTable(Transaction *txn, int tab_fd); 67 | 68 | bool Unlock(Transaction *txn, LockDataId lock_data_id); 69 | 70 | private: 71 | std::mutex latch_; // 互斥锁,用于锁表的互斥访问 72 | std::unordered_map lock_table_; // 全局锁表 73 | }; 74 | -------------------------------------------------------------------------------- /src/transaction/transaction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "txn_defs.h" 10 | 11 | class Transaction { 12 | public: 13 | explicit Transaction(txn_id_t txn_id, IsolationLevel isolation_level = IsolationLevel::SERIALIZABLE) 14 | : state_(TransactionState::DEFAULT), isolation_level_(isolation_level), txn_id_(txn_id) { 15 | write_set_ = std::make_shared>(); 16 | lock_set_ = std::make_shared>(); 17 | page_set_ = std::make_shared>(); 18 | deleted_page_set_ = std::make_shared>(); 19 | prev_lsn_ = INVALID_LSN; 20 | thread_id_ = std::this_thread::get_id(); 21 | } 22 | 23 | ~Transaction() = default; 24 | 25 | inline txn_id_t GetTransactionId() { return txn_id_; } 26 | 27 | inline std::thread::id GetThreadId() { return thread_id_; } 28 | 29 | inline void SetTxnMode(bool txn_mode) { txn_mode_ = txn_mode; } 30 | inline bool GetTxnMode() { return txn_mode_; } 31 | 32 | inline void SetStartTs(timestamp_t start_ts) { start_ts_ = start_ts; } 33 | inline timestamp_t GetStartTs() { return start_ts_; } 34 | 35 | inline IsolationLevel GetIsolationLevel() { return isolation_level_; } 36 | 37 | inline TransactionState GetState() { return state_; } 38 | inline void SetState(TransactionState state) { state_ = state; } 39 | 40 | inline lsn_t GetPrevLsn() { return prev_lsn_; } 41 | inline void SetPrevLsn(lsn_t prev_lsn) { prev_lsn_ = prev_lsn; } 42 | 43 | inline std::shared_ptr> GetWriteSet() { return write_set_; } 44 | 45 | /** 46 | * @brief 向write_set_中添加写操作 47 | * @param write_record 要添加的写操作 48 | */ 49 | inline void AppendWriteRecord(WriteRecord *write_record) { write_set_->push_back(write_record); } 50 | 51 | inline std::shared_ptr> GetLockSet() { return lock_set_; } 52 | 53 | /** @return the page set */ 54 | inline std::shared_ptr> GetPageSet() { 55 | return page_set_; 56 | } 57 | 58 | /** 59 | * Adds a page into the page set. 60 | * @param page page to be added 61 | */ 62 | inline void AddIntoPageSet(Page *page) { page_set_->push_back(page); } 63 | 64 | /** @return the deleted page set */ 65 | inline std::shared_ptr> GetDeletedPageSet() { return deleted_page_set_; } 66 | 67 | /** 68 | * Adds a page to the deleted page set. 69 | * @param page page to be marked as deleted 70 | */ 71 | inline void AddIntoDeletedPageSet(Page *page) { deleted_page_set_->push_back(page); } 72 | 73 | private: 74 | bool txn_mode_; // 用于标识当前事务是否还包含未执行的操作,用于interp函数,与lab需要完成的code无关 75 | TransactionState state_; // 事务状态 76 | IsolationLevel isolation_level_; // 事务的隔离级别,默认隔离级别为可串行化 77 | std::thread::id thread_id_; // 当前事务对应的线程id 78 | lsn_t prev_lsn_; // 当前事务执行的最后一条操作对应的lsn 79 | txn_id_t txn_id_; // 事务的ID,唯一标识符 80 | timestamp_t start_ts_; // 事务的开始时间戳 81 | 82 | std::shared_ptr> write_set_; // 事务包含的所有写操作 83 | 84 | std::shared_ptr> lock_set_; // 事务申请的所有锁 85 | 86 | /** 用于索引lab: The pages that were latched during index operation, used for concurrent index */ 87 | std::shared_ptr> page_set_; 88 | /** 用于索引lab: Concurrent index: the page IDs that were deleted during index operation.*/ 89 | std::shared_ptr> deleted_page_set_; 90 | }; -------------------------------------------------------------------------------- /src/transaction/transaction_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "transaction.h" 7 | #include "recovery/log_manager.h" 8 | #include "concurrency/lock_manager.h" 9 | #include "system/sm_manager.h" 10 | 11 | enum class ConcurrencyMode { TWO_PHASE_LOCKING = 0, BASIC_TO }; 12 | 13 | class TransactionManager{ 14 | public: 15 | explicit TransactionManager(LockManager *lock_manager, SmManager *sm_manager, 16 | ConcurrencyMode concurrency_mode = ConcurrencyMode::TWO_PHASE_LOCKING) { 17 | sm_manager_ = sm_manager; 18 | lock_manager_ = lock_manager; 19 | concurrency_mode_ = concurrency_mode; 20 | } 21 | 22 | ~TransactionManager() = default; 23 | 24 | Transaction * Begin(Transaction * txn, LogManager *log_manager); 25 | 26 | void Commit(Transaction * txn, LogManager *log_manager); 27 | 28 | void Abort(Transaction * txn, LogManager *log_manager); 29 | 30 | ConcurrencyMode GetConcurrencyMode() { return concurrency_mode_; } 31 | 32 | void SetConcurrencyMode(ConcurrencyMode concurrency_mode) { concurrency_mode_ = concurrency_mode; } 33 | 34 | LockManager *GetLockManager() { return lock_manager_; } 35 | 36 | /** 37 | * 获取对应ID的事务指针 38 | * @param txn_id 事务ID 39 | * @return 对应事务对象的指针 40 | */ 41 | Transaction *GetTransaction(txn_id_t txn_id) { 42 | if(txn_id == INVALID_TXN_ID) return nullptr; 43 | 44 | assert(TransactionManager::txn_map.find(txn_id) != TransactionManager::txn_map.end()); 45 | 46 | auto *res = TransactionManager::txn_map[txn_id]; 47 | assert(res != nullptr); 48 | assert(res->GetThreadId() == std::this_thread::get_id()); 49 | return res; 50 | } 51 | 52 | // used for test 53 | inline txn_id_t GetNextTxnId() { return next_txn_id_; } 54 | 55 | // global map of transactions which are running in the system. 56 | static std::unordered_map txn_map; 57 | 58 | /** 59 | * @brief used for checkpoint 60 | */ 61 | void BlockAllTransactions(); 62 | void ResumeAllTransactions(); 63 | 64 | private: 65 | ConcurrencyMode concurrency_mode_; // 事务使用的并发控制算法,目前只需要考虑2PL 66 | // Transaction * current_txn_; 67 | std::atomic next_txn_id_{0}; // 用于分发事务ID 68 | std::atomic next_timestamp_{0}; // 用于分发事务时间戳 69 | SmManager *sm_manager_; 70 | LockManager *lock_manager_; 71 | }; -------------------------------------------------------------------------------- /src/transaction/txn_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common/config.h" 6 | #include "defs.h" 7 | #include "record/rm_defs.h" 8 | 9 | enum class TransactionState { DEFAULT, GROWING, SHRINKING, COMMITTED, ABORTED }; 10 | 11 | enum class IsolationLevel { READ_UNCOMMITTED, REPEATABLE_READ, READ_COMMITTED, SERIALIZABLE }; 12 | 13 | enum class WType { INSERT_TUPLE = 0, DELETE_TUPLE, UPDATE_TUPLE}; 14 | 15 | class WriteRecord { 16 | public: 17 | WriteRecord() = default; 18 | 19 | // constructor for insert operation 20 | WriteRecord(WType wtype, const std::string &tab_name, const Rid &rid) 21 | : wtype_(wtype), tab_name_(tab_name), rid_(rid) {} 22 | 23 | // constructor for delete operation 24 | WriteRecord(WType wtype, const std::string &tab_name, const RmRecord &record) 25 | : wtype_(wtype), tab_name_(tab_name), record_(record) {} 26 | 27 | // constructor for update operation 28 | WriteRecord(WType wtype, const std::string &tab_name, const Rid &rid, const RmRecord &record) 29 | : wtype_(wtype), tab_name_(tab_name), rid_(rid), record_(record) {} 30 | 31 | ~WriteRecord() = default; 32 | 33 | inline RmRecord &GetRecord() { return record_; } 34 | 35 | inline Rid &GetRid() { return rid_; } 36 | 37 | inline WType &GetWriteType() { return wtype_; } 38 | 39 | inline std::string &GetTableName() { return tab_name_; } 40 | 41 | private: 42 | WType wtype_; 43 | std::string tab_name_; 44 | 45 | // for insert/update/delete operation 46 | Rid rid_; 47 | RmRecord record_; 48 | }; 49 | 50 | enum class LockDataType { TABLE = 0, RECORD = 1 }; 51 | 52 | class LockDataId { 53 | public: 54 | // lock on table 55 | LockDataId(int fd, LockDataType type) { 56 | assert(type == LockDataType::TABLE); 57 | fd_ = fd; 58 | type_ = type; 59 | rid_.page_no = -1; 60 | rid_.slot_no = -1; 61 | } 62 | 63 | LockDataId(int fd, const Rid &rid, LockDataType type) { 64 | assert(type == LockDataType::RECORD); 65 | fd_ = fd; 66 | rid_ = rid; 67 | type_ = type; 68 | } 69 | 70 | inline int64_t Get() const { 71 | if (type_ == LockDataType::TABLE) { 72 | // fd_ 73 | return static_cast(fd_); 74 | } else { 75 | // fd_, rid_.page_no, rid.slot_no 76 | return ((static_cast(type_)) << 63) | ((static_cast(fd_)) << 31) | 77 | ((static_cast(rid_.page_no)) << 16) | rid_.slot_no; 78 | } 79 | } 80 | 81 | bool operator==(const LockDataId &other) const { 82 | if (type_ != other.type_) return false; 83 | if (fd_ != other.fd_) return false; 84 | return rid_ == other.rid_; 85 | } 86 | int fd_; 87 | Rid rid_; 88 | LockDataType type_; 89 | }; 90 | 91 | template <> 92 | struct std::hash { 93 | size_t operator()(const LockDataId &obj) const { return std::hash()(obj.Get()); } 94 | }; 95 | 96 | enum class AbortReason { LOCK_ON_SHIRINKING = 0, UPGRADE_CONFLICT, DEADLOCK_PREVENTION }; 97 | 98 | class TransactionAbortException : public std::exception { 99 | txn_id_t txn_id_; 100 | AbortReason abort_reason_; 101 | 102 | public: 103 | explicit TransactionAbortException(txn_id_t txn_id, AbortReason abort_reason) 104 | : txn_id_(txn_id), abort_reason_(abort_reason) {} 105 | 106 | txn_id_t GetTransactionId() { return txn_id_; } 107 | AbortReason GetAbortReason() { return abort_reason_; } 108 | std::string GetInfo() { 109 | switch (abort_reason_) { 110 | case AbortReason::LOCK_ON_SHIRINKING: { 111 | return "Transaction " + std::to_string(txn_id_) + 112 | " aborted because it cannot request locks on SHRINKING phase\n"; 113 | } break; 114 | 115 | case AbortReason::UPGRADE_CONFLICT: { 116 | return "Transaction " + std::to_string(txn_id_) + 117 | " aborted because another transaction is waiting for upgrading\n"; 118 | } break; 119 | 120 | case AbortReason::DEADLOCK_PREVENTION: { 121 | return "Transaction " + std::to_string(txn_id_) + " aborted for deadlock prevention\n"; 122 | } break; 123 | 124 | default: { 125 | return "Transaction aborted\n"; 126 | } break; 127 | } 128 | } 129 | }; -------------------------------------------------------------------------------- /src/transaction/txn_manager_test.cpp: -------------------------------------------------------------------------------- 1 | #include "execution/execution_manager.h" 2 | #include "transaction_manager.h" 3 | #include "gtest/gtest.h" 4 | #include "interp.h" 5 | 6 | #define BUFFER_LENGTH 8192 7 | 8 | class TransactionTest : public::testing::Test { 9 | public: 10 | std::string db_name_ = "Txn_Test_DB"; 11 | std::unique_ptr disk_manager_; 12 | std::unique_ptr buffer_pool_manager_; 13 | std::unique_ptr rm_manager_; 14 | std::unique_ptr ix_manager_; 15 | std::unique_ptr sm_manager_; 16 | std::unique_ptr ql_manager_; 17 | std::unique_ptr log_manager_; 18 | std::unique_ptr lock_manager_; 19 | std::unique_ptr txn_manager_; 20 | std::unique_ptr interp_; 21 | txn_id_t txn_id = INVALID_TXN_ID; 22 | char *result = new char[BUFFER_LENGTH]; 23 | int offset; 24 | 25 | public: 26 | // This function is called before every test. 27 | void SetUp() override { 28 | ::testing::Test::SetUp(); 29 | // For each test, we create a new BufferPoolManager... 30 | disk_manager_ = std::make_unique(); 31 | buffer_pool_manager_ = std::make_unique(BUFFER_POOL_SIZE, disk_manager_.get()); 32 | rm_manager_ = std::make_unique(disk_manager_.get(), buffer_pool_manager_.get()); 33 | ix_manager_ = std::make_unique(disk_manager_.get(), buffer_pool_manager_.get()); 34 | sm_manager_ = std::make_unique(disk_manager_.get(), buffer_pool_manager_.get(), rm_manager_.get(), 35 | ix_manager_.get()); 36 | ql_manager_ = std::make_unique(sm_manager_.get()); 37 | log_manager_ = std::make_unique(disk_manager_.get()); 38 | log_manager_->SetLogMode(false); 39 | lock_manager_ = std::make_unique(); 40 | txn_manager_ = std::make_unique(lock_manager_.get(), sm_manager_.get()); 41 | interp_ = std::make_unique(sm_manager_.get(), ql_manager_.get(), txn_manager_.get()); 42 | // create db and open db 43 | if (sm_manager_->is_dir(db_name_)) { 44 | sm_manager_->drop_db(db_name_); 45 | } 46 | sm_manager_->create_db(db_name_); 47 | sm_manager_->open_db(db_name_); 48 | } 49 | 50 | // This function is called after every test. 51 | void TearDown() override { 52 | sm_manager_->close_db(); // exit 53 | // sm_manager_->drop_db(db_name_); // 若不删除数据库文件,则将保留最后一个测试点的数据库 54 | }; 55 | 56 | // The below helper functions are useful for testing. 57 | void exec_sql(const std::string &sql) { 58 | YY_BUFFER_STATE yy_buffer = yy_scan_string(sql.c_str()); 59 | assert(yyparse() == 0 && ast::parse_tree != nullptr); 60 | yy_delete_buffer(yy_buffer); 61 | memset(result, 0, BUFFER_LENGTH); 62 | offset = 0; 63 | Context *context = new Context(lock_manager_.get(), log_manager_.get(), 64 | nullptr, result, &offset); 65 | interp_->interp_sql(ast::parse_tree, &txn_id, context); // 主要执行逻辑 66 | }; 67 | }; 68 | 69 | TEST_F(TransactionTest, BeginTest) { 70 | Transaction *txn = nullptr; 71 | txn = txn_manager_->Begin(txn, log_manager_.get()); 72 | 73 | EXPECT_EQ(txn_manager_->txn_map.size(), 1); 74 | EXPECT_NE(txn, nullptr); 75 | EXPECT_EQ(txn->GetState(), TransactionState::DEFAULT); 76 | } 77 | 78 | // test commit 79 | TEST_F(TransactionTest, CommitTest) { 80 | exec_sql("create table t1 (num int);"); 81 | exec_sql("begin;"); 82 | exec_sql("insert into t1 values(1);"); 83 | exec_sql("insert into t1 values(2);"); 84 | exec_sql("insert into t1 values(3);"); 85 | exec_sql("update t1 set num = 4 where num = 1;"); 86 | exec_sql("delete from t1 where num = 3;"); 87 | exec_sql("commit;"); 88 | exec_sql("select * from t1;"); 89 | const char *str = "+------------------+\n" 90 | "| num |\n" 91 | "+------------------+\n" 92 | "| 4 |\n" 93 | "| 2 |\n" 94 | "+------------------+\n" 95 | "Total record(s): 2\n"; 96 | EXPECT_STREQ(result, str); 97 | // there should be 3 transactions 98 | EXPECT_EQ(txn_manager_->GetNextTxnId(), 3); 99 | Transaction *txn = txn_manager_->GetTransaction(1); 100 | EXPECT_EQ(txn->GetState(), TransactionState::COMMITTED); 101 | } 102 | 103 | // test abort 104 | TEST_F(TransactionTest, AbortTest) { 105 | exec_sql("create table t1 (num int);"); 106 | exec_sql("begin;"); 107 | exec_sql("insert into t1 values(1);"); 108 | exec_sql("insert into t1 values(2);"); 109 | exec_sql("insert into t1 values(3);"); 110 | exec_sql("update t1 set num = 4 where num = 1;"); 111 | exec_sql("delete from t1 where num = 3;"); 112 | exec_sql("abort;"); 113 | exec_sql("select * from t1;"); 114 | const char * str = "+------------------+\n" 115 | "| num |\n" 116 | "+------------------+\n" 117 | "+------------------+\n" 118 | "Total record(s): 0\n"; 119 | EXPECT_STREQ(result, str); 120 | EXPECT_EQ(txn_manager_->GetNextTxnId(), 3); 121 | Transaction *txn = txn_manager_->GetTransaction(1); 122 | EXPECT_EQ(txn->GetState(), TransactionState::ABORTED); 123 | } 124 | 125 | --------------------------------------------------------------------------------