├── .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项目结构.pdf └── 框架图.pdf ├── pics ├── B+树删除流程.png ├── B+树插入流程.png ├── B+树的结构.png ├── architecture_fixed.jpg ├── ast.png ├── 存储层-类之间的关系.png ├── 执行模块流程图.jpg └── 锁表结构.png ├── rucbase_client ├── CMakeLists.txt └── main.cpp └── src ├── CMakeLists.txt ├── analyze ├── CMakeLists.txt ├── analyze.cpp └── analyze.h ├── common ├── CMakeLists.txt ├── common.h ├── config.h └── context.h ├── defs.h ├── errors.h ├── execution ├── CMakeLists.txt ├── execution.h ├── execution_defs.h ├── execution_manager.cpp ├── execution_manager.h ├── execution_sort.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 ├── index ├── CMakeLists.txt ├── ix.h ├── ix_defs.h ├── ix_index_handle.cpp ├── ix_index_handle.h ├── ix_manager.h ├── ix_scan.cpp └── ix_scan.h ├── optimizer ├── CMakeLists.txt ├── optimizer.h ├── plan.h ├── planner.cpp └── planner.h ├── parser ├── CMakeLists.txt ├── ast.cpp ├── ast.h ├── ast_printer.h ├── lex.l ├── lex.yy.c ├── lex.yy.cpp ├── lex.yy.cpp~origin_master ├── lex.yy.hpp ├── parse_node.h ├── parser.h ├── parser_defs.h ├── test_parser.cpp ├── yacc.tab.c ├── yacc.tab.cpp ├── yacc.tab.cpp~origin_master ├── yacc.tab.h ├── yacc.tab.hpp ├── yacc.tab.h~origin_master └── yacc.y ├── portal.h ├── record ├── CMakeLists.txt ├── bitmap.h ├── rm.h ├── rm_defs.h ├── rm_file_handle.cpp ├── rm_file_handle.h ├── rm_manager.h ├── rm_scan.cpp └── rm_scan.h ├── record_printer.h ├── recovery ├── CMakeLists.txt ├── log_defs.h ├── log_manager.cpp ├── log_manager.h ├── log_recovery.cpp └── log_recovery.h ├── replacer ├── CMakeLists.txt ├── lru_replacer.cpp ├── lru_replacer.h └── replacer.h ├── rmdb.cpp ├── storage ├── CMakeLists.txt ├── buffer_pool_manager.cpp ├── buffer_pool_manager.h ├── disk_manager.cpp ├── disk_manager.h └── page.h ├── system ├── CMakeLists.txt ├── sm.h ├── sm_defs.h ├── sm_manager.cpp ├── sm_manager.h └── sm_meta.h ├── test ├── CMakeLists.txt ├── concurrency │ ├── concurrency_sql │ │ ├── concurrency_read_test.sql │ │ ├── concurrency_read_test_output.txt │ │ ├── concurrency_test.sql │ │ ├── dirty_read_test.sql │ │ ├── dirty_read_test_output.txt │ │ ├── dirty_write_test.sql │ │ ├── dirty_write_test_output.txt │ │ ├── lost_update_test.sql │ │ ├── lost_update_test_output.txt │ │ ├── phantom_read_test_1.sql │ │ ├── phantom_read_test_1_output.txt │ │ ├── phantom_read_test_2.sql │ │ ├── phantom_read_test_2_output.txt │ │ ├── phantom_read_test_3.sql │ │ ├── phantom_read_test_3_output.txt │ │ ├── phantom_read_test_4.sql │ │ ├── phantom_read_test_4_output.txt │ │ ├── unrepeatable_read_test.sql │ │ ├── unrepeatable_read_test_hard.sql │ │ ├── unrepeatable_read_test_hard_output.txt │ │ └── unrepeatable_read_test_output.txt │ ├── concurrency_test.cpp │ ├── concurrency_test.h │ ├── concurrency_test.py │ ├── concurrency_test_bonus.py │ ├── concurrency_test_main.cpp │ └── concurrency_unit_test.py ├── index │ ├── b_plus_tree_concurrent_test.cpp │ ├── b_plus_tree_delete_test.cpp │ └── b_plus_tree_insert_test.cpp ├── query │ ├── query_sql │ │ ├── basic_query_answer1.txt │ │ ├── basic_query_answer2.txt │ │ ├── basic_query_answer3.txt │ │ ├── basic_query_answer4.txt │ │ ├── basic_query_answer5.txt │ │ ├── basic_query_test1.sql │ │ ├── basic_query_test2.sql │ │ ├── basic_query_test3.sql │ │ ├── basic_query_test4.sql │ │ └── basic_query_test5.sql │ ├── query_test.cpp │ ├── query_test_basic.py │ └── query_unit_test.py ├── regress │ ├── regress_test.cpp │ ├── regress_test.h │ └── regress_test_main.cpp ├── storage │ ├── buffer_pool_manager_test.cpp │ ├── disk_manager_test.cpp │ ├── lru_replacer_test.cpp │ └── record_manager_test.cpp └── transaction │ ├── transaction_sql │ ├── abort_index_test.sql │ ├── abort_index_test_output.txt │ ├── abort_test.sql │ ├── abort_test_output.txt │ ├── commit_index_test.sql │ ├── commit_index_test_output.txt │ ├── commit_test.sql │ ├── commit_test_output.txt │ └── transaction_test.cpp │ ├── transaction_test.cpp │ ├── transaction_test.py │ ├── transaction_test_bonus.py │ └── transaction_unit_test.py └── transaction ├── CMakeLists.txt ├── concurrency ├── lock_manager.cpp └── lock_manager.h ├── transaction.h ├── transaction_manager.cpp ├── transaction_manager.h └── txn_defs.h /.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 | deps/ 32 | -------------------------------------------------------------------------------- /.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(RMDB) 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 -O0 -g -ggdb3") 10 | # set(CMAKE_CXX_FLAGS "-Wall -O3") 11 | 12 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") 13 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g") 14 | 15 | 16 | enable_testing() 17 | add_subdirectory(src) 18 | add_subdirectory(deps) 19 | -------------------------------------------------------------------------------- /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 | ``` 3 | ____ _ _ ____ ____ _ ____ _____ 4 | | _ \| | | |/ ___| __ ) / \ / ___|| ____| 5 | | |_) | | | | | | _ \ / _ \ \___ \| _| 6 | | _ <| |_| | |___| |_) / ___ \ ___) | |___ 7 | |_| \_ \___/ \____|____/_/ \_\____/|_____| 8 | ``` 9 | RUCBase是由中国人民大学数据库教学团队开发,配套教育部“101计划”计算机核心教材《数据库管理系统原理与实现》建设的教学用数据库管理系统,旨在支撑面向本科数据库零基础学生的数据库系统课程实验教学。RUCBase系统框架部分参考和借鉴了CMU15-445课程的[BusTub](https://github.com/cmu-db/bustub) 和Standford CS346课程的[Redbase](https://web.stanford.edu/class/cs346/2015/redbase.html),目前中国人民大学、哈尔滨工业大学、华中科技大学、西安电子科技大学等高校使用该教学系统进行数据库内核实验。 10 | 11 | ## 实验环境: 12 | - 操作系统:Ubuntu 18.04 及以上(64位) 13 | - 编译器:GCC 14 | - 编程语言:C++17 15 | - 管理工具:cmake 16 | - 推荐编辑器:VScode 17 | 18 | ### 依赖环境库配置: 19 | - gcc 7.1及以上版本(要求完全支持C++17) 20 | - cmake 3.16及以上版本 21 | - flex 22 | - bison 23 | - readline 24 | 25 | 欲查看有关依赖运行库和编译工具的更多信息,以及如何运行的说明,请查阅[Rucbase使用文档](docs/Rucbase使用文档.md) 26 | 27 | 欲了解如何在非Linux系统PC上部署实验环境的指导,请查阅[Rucbase环境配置文档](docs/Rucbase环境配置文档.md) 28 | 29 | ## 实验文档索引 30 | 31 | > 这里给出目前公开的文档分类索引 32 | 33 | ### 开发规范文档 34 | 35 | - [Rucbase开发文档](docs/Rucbase开发文档.md) 36 | 37 | ### 项目说明文档 38 | 39 | - [Rucbase环境配置文档](docs/Rucbase环境配置文档.md) 40 | - [Rucbase使用文档](docs/Rucbase使用文档.md) 41 | - [Rucbase项目结构](docs/Rucbase项目结构.pdf) 42 | - [框架图](docs/框架图.pdf) 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 | | 实验环境配置 | - | - | 1~2h | 简单 | 60 | | 存储管理实验 | 10.22(第七周) | 11.12(第十周) | 15h | 简单 | 61 | | 索引管理实验 | 11.5(第九周) | 11.26(第十二周) | 35h | 中等 | 62 | | 查询执行实验 | 11.26(第十二周) | 12.24(十六周) | 30-40h | 困难 | 63 | | 并发控制实验 | 12.17(第十五周) | 暂定1.7(第十八周) | 25-30h | 中等 | 64 | -------------------------------------------------------------------------------- /deps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_SUBDIRECTORY(googletest) -------------------------------------------------------------------------------- /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 | - [使用Docker进行环境配置](#) 11 | - [代码编辑器VS Code](#%E4%BB%A3%E7%A0%81%E7%BC%96%E8%BE%91%E5%99%A8vs-code) 12 | - [安装VS Code](#%E5%AE%89%E8%A3%85vs-code) 13 | - [在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) 14 | - [代码托管平台GitHub](#%E4%BB%A3%E7%A0%81%E6%89%98%E7%AE%A1%E5%B9%B3%E5%8F%B0github) 15 | - [Git使用](#git%E4%BD%BF%E7%94%A8) 16 | 17 | 18 | 19 | # Rucbase环境配置文档 20 | 21 | 巧妇难为无米之炊,我们需要先对实验进行一些准备工作。配置好开发环境后,才能在Linux系统上运行Rucbase。 22 | 23 | 在本实验中,我们统一使用Ubuntu操作系统(Linux的一个主流发行版)。推荐在你的主机(Windows/macOS)中使用VS Code作为代码编辑器。使用虚拟机的同学,推荐使用SSH连接到虚拟机进行实验。 24 | 25 | 请同学们按照我们的要求进行环境配置。如果你没有按照文档要求进行实验(比如使用了其他的Linux发行版),可能会出现一些奇怪的bug,进而影响实验进度和分数。 26 | 27 | 如果配置环境的过程中遇到了文档中没提及的问题,你可以向助教求助。 28 | 29 | 30 | 31 | ## 虚拟机软件VMware/VirtualBox 32 | 33 | 我们使用虚拟机软件安装Linux操作系统。虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。 34 | 35 | VMware是目前流行的虚拟机运行软件,在Windows/macOS主机上均可以安装使用。它在Windows和macOS中有不同的名字,但使用方法基本一致,请根据你主机的操作系统选择对应版本。 36 | 37 | [下载Windows版VMware Workstation 16 Pro](https://www.vmware.com/go/getworkstation-win) 38 | 39 | [下载macOS版本VMware Fusion 12 Pro](https://www.vmware.com/go/getfusion) 40 | 41 | VMware是收费软件,你可以选择试用一段时间,在激活过程中需要小心防范欺诈链接。 42 | 43 | 如果你不希望使用收费软件,我们推荐[Oracle VM VirtualBox](https://www.virtualbox.org/),它是一款优秀的开源跨平台虚拟化软件。 44 | 45 | ## WSL 2.0 (Windows Subsystem for Linux) 46 | 47 | 对于使用Windows10,11操作系统的同学,你也可以使用WSL2.0作为自己的Linux开发环境。请注意,WSL开发环境默认安装为1.0版本,1.0并不能提供完整的Linux内核功能,**你必须升级为WSL2.0**,对此有疑问的同学,可在实验课上联系助教获得支持。 48 | 49 | 你也可以阅读Microsoft提供的官方文档:[适用于 Linux 的 Windows 子系统文档 | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/wsl/) 50 | 51 | ### WSL2.0相关注意事项 52 | 53 | 1. 开启WSL2.0后,会开启hyper-v虚拟层,这样可能会导致你的pc性能略有下降(10%以内,包括磁盘IO和CPU/GPU处理速度)。 54 | 2. 部分使用低版本VMware,VirtualBox等虚拟化软件的同学可能无法正常启动虚拟化软件,需要进行升级 55 | 3. 对**国内手游/安卓模拟器有需求的同学**请注意,开启WSL2.0后可能你安装的模拟器将无法使用,你需要改用`BlueStack`等模拟器 56 | 4. PC机性能较为孱弱且对于日常使用性能有较高要求的同学不建议部署WSL2.0 57 | 58 | ## Ubuntu操作系统 59 | 60 | 安装并激活好虚拟机软件后,我们就要在VMware上安装Linux操作系统。 61 | 62 | Linux是开源的操作系统,拥有数百个发行版,其中Ubuntu是一个非常流行的桌面发行版。我们本次实验中就采用Ubuntu操作系统作为Rucbase的运行环境。 63 | 64 | **我们强烈建议你使用Ubuntu系统,虽然在内部测试中,项目在原生Debian和CentOS上也可以通过编译,但是仍然可能遇到一些始料未及的问题** 65 | 66 | [下载Ubuntu 20.04.5 LTS](https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/20.04.5/ubuntu-20.04.5-desktop-amd64.iso) 67 | 68 | 下载好Ubuntu后,在VMware中选择文件-新建虚拟机,选择“安装程序光盘映像文件”,即刚才下载的iso文件,然后按提示安装即可。 69 | 70 | 安装好系统之后,建议进行换源,以提高下载速度。Ubuntu官方的服务器在国外,速度较慢且时常断连。为了提高软件安装/更新速度,Ubuntu提供了选择服务器的功能,可以帮助我们使用位于国内的快速稳定的镜像服务器。先进入系统设置,打开软件和更新,在服务器列表中选择较近的服务器,我们建议使用清华或者阿里源。 71 | 72 | 我们还需要安装Rucbase的依赖环境,如gcc、cmake等。具体请参阅[Rucbase使用文档](Rucbase使用文档.md) 73 | 74 | 关于Linux的学习,还可以参考以下资料: 75 | 76 | [Linux教程](https://www.runoob.com/linux) 77 | 78 | [学习Linux命令行](https://nju-projectn.github.io/ics-pa-gitbook/ics2021/linux.html) 79 | 80 | ## 使用Docker进行环境配置 81 | 82 | [使用Docker配置环境教程](https://www.bilibili.com/video/BV1Nc41147Pd/?vd_source=1e0431eaeb521fce46cdddb12e68d8c6#reply202992270912) 83 | 84 | ## 代码编辑器VS Code 85 | 86 | ### 安装VS Code 87 | 88 | VS Code(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器。该软件支持语法高亮、代码自动补全、代码重构、查看定义功能,并且内置了命令行工具和 Git 版本控制系统。用户可以更改主题和键盘快捷方式实现个性化设置,也可以通过内置的扩展程序商店安装扩展以拓展软件功能。 89 | 90 | [下载VS Code](https://code.visualstudio.com/Download) 91 | 92 | 在主机上下载并安装VS Code后,可以进行少量的配置,如安装C++扩展包、SSH相关扩展等。 93 | 94 | **我们推荐你使用VScode,你也可以使用其他IDE或代码编辑工具,如Clion, Vim, Emacs等,但是请注意,使用其他工具请确保自己对其有一定的熟练度,在遇到部分配置问题时助教可能无法提供充足的支持** 95 | 96 | ### 在VS Code中使用SSH连接到虚拟机 97 | 98 | 需要在VS Code中安装SSH的相关扩展。 99 | 100 | [官方教程](https://code.visualstudio.com/docs/remote/ssh-tutorial) 101 | 102 | 在VS Code扩展商店中搜索并安装Remote SSH,安装成功后进入配置页。在配置文件中填入虚拟机的用户名等相关信息,就可以连接虚拟机。 103 | 104 | 105 | 106 | ## 代码托管平台GitHub 107 | 108 | Git 是一个版本控制系统,可以让你跟踪你对文件所做的修改。GitHub是世界上最大的软件远程仓库,是一个面向开源和私有软件项目的托管平台,使用Git做分布式版本控制。 109 | 110 | 你需要在Ubuntu中安装Git来下载实验代码。具体请参阅[Rucbase使用文档](Rucbase使用文档.md)。 111 | 112 | [下载Git](https://git-scm.com/download) 113 | 114 | ## Git使用 115 | 116 | 对于不熟悉Git使用的同学,我们推荐你阅读该博客教程[Git教程 - 廖雪峰的官方网站 (liaoxuefeng.com)](https://www.liaoxuefeng.com/wiki/896043488029600) 117 | 118 | 同时,我们也推荐你尽早申请Github教育优惠,获取相关福利软件。其中,我们强烈推荐使用`GitKraken`作为你的`git`可视化管理工具。 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /docs/Rucbase项目结构.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/docs/Rucbase项目结构.pdf -------------------------------------------------------------------------------- /docs/框架图.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/docs/框架图.pdf -------------------------------------------------------------------------------- /pics/B+树删除流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/B+树删除流程.png -------------------------------------------------------------------------------- /pics/B+树插入流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/B+树插入流程.png -------------------------------------------------------------------------------- /pics/B+树的结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/B+树的结构.png -------------------------------------------------------------------------------- /pics/architecture_fixed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/architecture_fixed.jpg -------------------------------------------------------------------------------- /pics/ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/ast.png -------------------------------------------------------------------------------- /pics/存储层-类之间的关系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/存储层-类之间的关系.png -------------------------------------------------------------------------------- /pics/执行模块流程图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/pics/执行模块流程图.jpg -------------------------------------------------------------------------------- /pics/锁表结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruc-deke/rucbase-lab/7d2e1451242a9fa7cf0b8bf7b54ca9193f6e95d8/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 | ) -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | 3 | add_subdirectory(analyze) 4 | add_subdirectory(record) 5 | add_subdirectory(index) 6 | add_subdirectory(system) 7 | add_subdirectory(execution) 8 | add_subdirectory(parser) 9 | add_subdirectory(optimizer) 10 | add_subdirectory(storage) 11 | add_subdirectory(common) 12 | add_subdirectory(replacer) 13 | add_subdirectory(transaction) 14 | add_subdirectory(recovery) 15 | add_subdirectory(test) 16 | 17 | 18 | target_link_libraries(parser execution pthread) 19 | 20 | add_executable(rmdb rmdb.cpp) 21 | target_link_libraries(rmdb parser execution readline pthread planner analyze) 22 | -------------------------------------------------------------------------------- /src/analyze/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES analyze.cpp) 2 | add_library(analyze STATIC ${SOURCES}) 3 | 4 | -------------------------------------------------------------------------------- /src/analyze/analyze.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "parser/parser.h" 20 | #include "system/sm.h" 21 | #include "common/common.h" 22 | 23 | class Query{ 24 | public: 25 | std::shared_ptr parse; 26 | // TODO jointree 27 | // where条件 28 | std::vector conds; 29 | // 投影列 30 | std::vector cols; 31 | // 表名 32 | std::vector tables; 33 | // update 的set 值 34 | std::vector set_clauses; 35 | //insert 的values值 36 | std::vector values; 37 | 38 | Query(){} 39 | 40 | }; 41 | 42 | class Analyze 43 | { 44 | private: 45 | SmManager *sm_manager_; 46 | public: 47 | Analyze(SmManager *sm_manager) : sm_manager_(sm_manager){} 48 | ~Analyze(){} 49 | 50 | std::shared_ptr do_analyze(std::shared_ptr root); 51 | 52 | private: 53 | TabCol check_column(const std::vector &all_cols, TabCol target); 54 | void get_all_cols(const std::vector &tab_names, std::vector &all_cols); 55 | void get_clause(const std::vector> &sv_conds, std::vector &conds); 56 | void check_clause(const std::vector &tab_names, std::vector &conds); 57 | Value convert_sv_value(const std::shared_ptr &sv_val); 58 | CompOp convert_sv_comp_op(ast::SvCompOp op); 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /src/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/common/common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "defs.h" 19 | #include "record/rm_defs.h" 20 | 21 | 22 | struct TabCol { 23 | std::string tab_name; 24 | std::string col_name; 25 | 26 | friend bool operator<(const TabCol &x, const TabCol &y) { 27 | return std::make_pair(x.tab_name, x.col_name) < std::make_pair(y.tab_name, y.col_name); 28 | } 29 | }; 30 | 31 | struct Value { 32 | ColType type; // type of value 33 | union { 34 | int int_val; // int value 35 | float float_val; // float value 36 | }; 37 | std::string str_val; // string value 38 | 39 | std::shared_ptr raw; // raw record buffer 40 | 41 | void set_int(int int_val_) { 42 | type = TYPE_INT; 43 | int_val = int_val_; 44 | } 45 | 46 | void set_float(float float_val_) { 47 | type = TYPE_FLOAT; 48 | float_val = float_val_; 49 | } 50 | 51 | void set_str(std::string str_val_) { 52 | type = TYPE_STRING; 53 | str_val = std::move(str_val_); 54 | } 55 | 56 | void init_raw(int len) { 57 | assert(raw == nullptr); 58 | raw = std::make_shared(len); 59 | if (type == TYPE_INT) { 60 | assert(len == sizeof(int)); 61 | *(int *)(raw->data) = int_val; 62 | } else if (type == TYPE_FLOAT) { 63 | assert(len == sizeof(float)); 64 | *(float *)(raw->data) = float_val; 65 | } else if (type == TYPE_STRING) { 66 | if (len < (int)str_val.size()) { 67 | throw StringOverflowError(); 68 | } 69 | memset(raw->data, 0, len); 70 | memcpy(raw->data, str_val.c_str(), str_val.size()); 71 | } 72 | } 73 | }; 74 | 75 | enum CompOp { OP_EQ, OP_NE, OP_LT, OP_GT, OP_LE, OP_GE }; 76 | 77 | struct Condition { 78 | TabCol lhs_col; // left-hand side column 79 | CompOp op; // comparison operator 80 | bool is_rhs_val; // true if right-hand side is a value (not a column) 81 | TabCol rhs_col; // right-hand side column 82 | Value rhs_val; // right-hand side value 83 | }; 84 | 85 | struct SetClause { 86 | TabCol lhs; 87 | Value rhs; 88 | }; -------------------------------------------------------------------------------- /src/common/config.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #define BUFFER_LENGTH 8192 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 4KB 35 | static constexpr int BUFFER_POOL_SIZE = 65536; // size of buffer pool 256MB 36 | // static constexpr int BUFFER_POOL_SIZE = 262144; // size of buffer pool 1GB 37 | static constexpr int LOG_BUFFER_SIZE = (1024 * PAGE_SIZE); // size of a log buffer in byte 38 | static constexpr int BUCKET_SIZE = 50; // size of extendible hash bucket 39 | 40 | using frame_id_t = int32_t; // frame id type, 帧页ID, 页在BufferPool中的存储单元称为帧,一帧对应一页 41 | using page_id_t = int32_t; // page id type , 页ID 42 | using txn_id_t = int32_t; // transaction id type 43 | using lsn_t = int32_t; // log sequence number type 44 | using slot_offset_t = size_t; // slot offset type 45 | using oid_t = uint16_t; 46 | using timestamp_t = int32_t; // timestamp type, used for transaction concurrency 47 | 48 | // log file 49 | static const std::string LOG_FILE_NAME = "db.log"; 50 | 51 | // replacer 52 | static const std::string REPLACER_TYPE = "LRU"; 53 | 54 | static const std::string DB_META_NAME = "db.meta"; 55 | -------------------------------------------------------------------------------- /src/common/context.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "transaction/transaction.h" 14 | #include "transaction/concurrency/lock_manager.h" 15 | #include "recovery/log_manager.h" 16 | 17 | // class TransactionManager; 18 | 19 | // used for data_send 20 | static int const_offset = -1; 21 | 22 | class Context { 23 | public: 24 | Context (LockManager *lock_mgr, LogManager *log_mgr, 25 | Transaction *txn, char *data_send = nullptr, int *offset = &const_offset) 26 | : lock_mgr_(lock_mgr), log_mgr_(log_mgr), txn_(txn), 27 | data_send_(data_send), offset_(offset) { 28 | ellipsis_ = false; 29 | } 30 | 31 | // TransactionManager *txn_mgr_; 32 | LockManager *lock_mgr_; 33 | LogManager *log_mgr_; 34 | Transaction *txn_; 35 | char *data_send_; 36 | int *offset_; 37 | bool ellipsis_; 38 | }; -------------------------------------------------------------------------------- /src/defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | // 此处重载了<<操作符,在ColMeta中进行了调用 17 | template::value, T>::type> 18 | std::ostream &operator<<(std::ostream &os, const T &enum_val) { 19 | os << static_cast(enum_val); 20 | return os; 21 | } 22 | 23 | template::value, T>::type> 24 | std::istream &operator>>(std::istream &is, T &enum_val) { 25 | int int_val; 26 | is >> int_val; 27 | enum_val = static_cast(int_val); 28 | return is; 29 | } 30 | 31 | struct Rid { 32 | int page_no; 33 | int slot_no; 34 | 35 | friend bool operator==(const Rid &x, const Rid &y) { 36 | return x.page_no == y.page_no && x.slot_no == y.slot_no; 37 | } 38 | 39 | friend bool operator!=(const Rid &x, const Rid &y) { return !(x == y); } 40 | }; 41 | 42 | enum ColType { 43 | TYPE_INT, TYPE_FLOAT, TYPE_STRING 44 | }; 45 | 46 | inline std::string coltype2str(ColType type) { 47 | std::map m = { 48 | {TYPE_INT, "INT"}, 49 | {TYPE_FLOAT, "FLOAT"}, 50 | {TYPE_STRING, "STRING"} 51 | }; 52 | return m.at(type); 53 | } 54 | 55 | class RecScan { 56 | public: 57 | virtual ~RecScan() = default; 58 | 59 | virtual void next() = 0; 60 | 61 | virtual bool is_end() const = 0; 62 | 63 | virtual Rid rid() const = 0; 64 | }; 65 | -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /src/execution/execution.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "execution_defs.h" 14 | #include "execution_manager.h" 15 | -------------------------------------------------------------------------------- /src/execution/execution_defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "defs.h" 14 | #include "errors.h" 15 | -------------------------------------------------------------------------------- /src/execution/execution_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "execution_defs.h" 20 | #include "record/rm.h" 21 | #include "system/sm.h" 22 | #include "common/context.h" 23 | #include "common/common.h" 24 | #include "optimizer/plan.h" 25 | #include "executor_abstract.h" 26 | #include "transaction/transaction_manager.h" 27 | 28 | 29 | class QlManager { 30 | private: 31 | SmManager *sm_manager_; 32 | TransactionManager *txn_mgr_; 33 | 34 | public: 35 | QlManager(SmManager *sm_manager, TransactionManager *txn_mgr) 36 | : sm_manager_(sm_manager), txn_mgr_(txn_mgr) {} 37 | 38 | void run_mutli_query(std::shared_ptr plan, Context *context); 39 | void run_cmd_utility(std::shared_ptr plan, txn_id_t *txn_id, Context *context); 40 | void select_from(std::unique_ptr executorTreeRoot, std::vector sel_cols, 41 | Context *context); 42 | 43 | void run_dml(std::unique_ptr exec); 44 | }; 45 | -------------------------------------------------------------------------------- /src/execution/execution_sort.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include "execution_defs.h" 13 | #include "execution_manager.h" 14 | #include "executor_abstract.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class SortExecutor : public AbstractExecutor { 19 | private: 20 | std::unique_ptr prev_; 21 | ColMeta cols_; // 框架中只支持一个键排序,需要自行修改数据结构支持多个键排序 22 | size_t tuple_num; 23 | bool is_desc_; 24 | std::vector used_tuple; 25 | std::unique_ptr current_tuple; 26 | 27 | public: 28 | SortExecutor(std::unique_ptr prev, TabCol sel_cols, bool is_desc) { 29 | prev_ = std::move(prev); 30 | cols_ = prev_->get_col_offset(sel_cols); 31 | is_desc_ = is_desc; 32 | tuple_num = 0; 33 | used_tuple.clear(); 34 | } 35 | 36 | void beginTuple() override { 37 | 38 | } 39 | 40 | void nextTuple() override { 41 | 42 | } 43 | 44 | std::unique_ptr Next() override { 45 | return nullptr; 46 | } 47 | 48 | Rid &rid() override { return _abstract_rid; } 49 | }; -------------------------------------------------------------------------------- /src/execution/executor_abstract.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "execution_defs.h" 14 | #include "common/common.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class AbstractExecutor { 19 | public: 20 | Rid _abstract_rid; 21 | 22 | Context *context_; 23 | 24 | virtual ~AbstractExecutor() = default; 25 | 26 | virtual size_t tupleLen() const { return 0; }; 27 | 28 | virtual const std::vector &cols() const { 29 | std::vector *_cols = nullptr; 30 | return *_cols; 31 | }; 32 | 33 | virtual std::string getType() { return "AbstractExecutor"; }; 34 | 35 | virtual void beginTuple(){}; 36 | 37 | virtual void nextTuple(){}; 38 | 39 | virtual bool is_end() const { return true; }; 40 | 41 | virtual Rid &rid() = 0; 42 | 43 | virtual std::unique_ptr Next() = 0; 44 | 45 | virtual ColMeta get_col_offset(const TabCol &target) { return ColMeta();}; 46 | 47 | std::vector::const_iterator get_col(const std::vector &rec_cols, const TabCol &target) { 48 | auto pos = std::find_if(rec_cols.begin(), rec_cols.end(), [&](const ColMeta &col) { 49 | return col.tab_name == target.tab_name && col.name == target.col_name; 50 | }); 51 | if (pos == rec_cols.end()) { 52 | throw ColumnNotFoundError(target.tab_name + '.' + target.col_name); 53 | } 54 | return pos; 55 | } 56 | }; -------------------------------------------------------------------------------- /src/execution/executor_delete.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include "execution_defs.h" 13 | #include "execution_manager.h" 14 | #include "executor_abstract.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class DeleteExecutor : public AbstractExecutor { 19 | private: 20 | TabMeta tab_; // 表的元数据 21 | std::vector conds_; // delete的条件 22 | RmFileHandle *fh_; // 表的数据文件句柄 23 | std::vector rids_; // 需要删除的记录的位置 24 | std::string tab_name_; // 表名称 25 | SmManager *sm_manager_; 26 | 27 | public: 28 | DeleteExecutor(SmManager *sm_manager, const std::string &tab_name, std::vector conds, 29 | std::vector rids, Context *context) { 30 | sm_manager_ = sm_manager; 31 | tab_name_ = tab_name; 32 | tab_ = sm_manager_->db_.get_table(tab_name); 33 | fh_ = sm_manager_->fhs_.at(tab_name).get(); 34 | conds_ = conds; 35 | rids_ = rids; 36 | context_ = context; 37 | } 38 | 39 | std::unique_ptr Next() override { 40 | return nullptr; 41 | } 42 | 43 | Rid &rid() override { return _abstract_rid; } 44 | }; -------------------------------------------------------------------------------- /src/execution/executor_index_scan.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "execution_defs.h" 14 | #include "execution_manager.h" 15 | #include "executor_abstract.h" 16 | #include "index/ix.h" 17 | #include "system/sm.h" 18 | 19 | class IndexScanExecutor : public AbstractExecutor { 20 | private: 21 | std::string tab_name_; // 表名称 22 | TabMeta tab_; // 表的元数据 23 | std::vector conds_; // 扫描条件 24 | RmFileHandle *fh_; // 表的数据文件句柄 25 | std::vector cols_; // 需要读取的字段 26 | size_t len_; // 选取出来的一条记录的长度 27 | std::vector fed_conds_; // 扫描条件,和conds_字段相同 28 | 29 | std::vector index_col_names_; // index scan涉及到的索引包含的字段 30 | IndexMeta index_meta_; // index scan涉及到的索引元数据 31 | 32 | Rid rid_; 33 | std::unique_ptr scan_; 34 | 35 | SmManager *sm_manager_; 36 | 37 | public: 38 | IndexScanExecutor(SmManager *sm_manager, std::string tab_name, std::vector conds, std::vector index_col_names, 39 | Context *context) { 40 | sm_manager_ = sm_manager; 41 | context_ = context; 42 | tab_name_ = std::move(tab_name); 43 | tab_ = sm_manager_->db_.get_table(tab_name_); 44 | conds_ = std::move(conds); 45 | // index_no_ = index_no; 46 | index_col_names_ = index_col_names; 47 | index_meta_ = *(tab_.get_index_meta(index_col_names_)); 48 | fh_ = sm_manager_->fhs_.at(tab_name_).get(); 49 | cols_ = tab_.cols; 50 | len_ = cols_.back().offset + cols_.back().len; 51 | std::map swap_op = { 52 | {OP_EQ, OP_EQ}, {OP_NE, OP_NE}, {OP_LT, OP_GT}, {OP_GT, OP_LT}, {OP_LE, OP_GE}, {OP_GE, OP_LE}, 53 | }; 54 | 55 | for (auto &cond : conds_) { 56 | if (cond.lhs_col.tab_name != tab_name_) { 57 | // lhs is on other table, now rhs must be on this table 58 | assert(!cond.is_rhs_val && cond.rhs_col.tab_name == tab_name_); 59 | // swap lhs and rhs 60 | std::swap(cond.lhs_col, cond.rhs_col); 61 | cond.op = swap_op.at(cond.op); 62 | } 63 | } 64 | fed_conds_ = conds_; 65 | } 66 | 67 | void beginTuple() override { 68 | 69 | } 70 | 71 | void nextTuple() override { 72 | 73 | } 74 | 75 | std::unique_ptr Next() override { 76 | return nullptr; 77 | } 78 | 79 | Rid &rid() override { return rid_; } 80 | }; -------------------------------------------------------------------------------- /src/execution/executor_insert.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include "execution_defs.h" 13 | #include "execution_manager.h" 14 | #include "executor_abstract.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class InsertExecutor : public AbstractExecutor { 19 | private: 20 | TabMeta tab_; // 表的元数据 21 | std::vector values_; // 需要插入的数据 22 | RmFileHandle *fh_; // 表的数据文件句柄 23 | std::string tab_name_; // 表名称 24 | Rid rid_; // 插入的位置,由于系统默认插入时不指定位置,因此当前rid_在插入后才赋值 25 | SmManager *sm_manager_; 26 | 27 | public: 28 | InsertExecutor(SmManager *sm_manager, const std::string &tab_name, std::vector values, Context *context) { 29 | sm_manager_ = sm_manager; 30 | tab_ = sm_manager_->db_.get_table(tab_name); 31 | values_ = values; 32 | tab_name_ = tab_name; 33 | if (values.size() != tab_.cols.size()) { 34 | throw InvalidValueCountError(); 35 | } 36 | fh_ = sm_manager_->fhs_.at(tab_name).get(); 37 | context_ = context; 38 | }; 39 | 40 | std::unique_ptr Next() override { 41 | // Make record buffer 42 | RmRecord rec(fh_->get_file_hdr().record_size); 43 | for (size_t i = 0; i < values_.size(); i++) { 44 | auto &col = tab_.cols[i]; 45 | auto &val = values_[i]; 46 | if (col.type != val.type) { 47 | throw IncompatibleTypeError(coltype2str(col.type), coltype2str(val.type)); 48 | } 49 | val.init_raw(col.len); 50 | memcpy(rec.data + col.offset, val.raw->data, col.len); 51 | } 52 | // Insert into record file 53 | rid_ = fh_->insert_record(rec.data, context_); 54 | 55 | // Insert into index 56 | for(size_t i = 0; i < tab_.indexes.size(); ++i) { 57 | auto& index = tab_.indexes[i]; 58 | auto ih = sm_manager_->ihs_.at(sm_manager_->get_ix_manager()->get_index_name(tab_name_, index.cols)).get(); 59 | char* key = new char[index.col_tot_len]; 60 | int offset = 0; 61 | for(size_t i = 0; i < index.col_num; ++i) { 62 | memcpy(key + offset, rec.data + index.cols[i].offset, index.cols[i].len); 63 | offset += index.cols[i].len; 64 | } 65 | ih->insert_entry(key, rid_, context_->txn_); 66 | } 67 | return nullptr; 68 | } 69 | Rid &rid() override { return rid_; } 70 | }; -------------------------------------------------------------------------------- /src/execution/executor_nestedloop_join.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include "execution_defs.h" 13 | #include "execution_manager.h" 14 | #include "executor_abstract.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class NestedLoopJoinExecutor : public AbstractExecutor { 19 | private: 20 | std::unique_ptr left_; // 左儿子节点(需要join的表) 21 | std::unique_ptr right_; // 右儿子节点(需要join的表) 22 | size_t len_; // join后获得的每条记录的长度 23 | std::vector cols_; // join后获得的记录的字段 24 | 25 | std::vector fed_conds_; // join条件 26 | bool isend; 27 | 28 | public: 29 | NestedLoopJoinExecutor(std::unique_ptr left, std::unique_ptr right, 30 | std::vector conds) { 31 | left_ = std::move(left); 32 | right_ = std::move(right); 33 | len_ = left_->tupleLen() + right_->tupleLen(); 34 | cols_ = left_->cols(); 35 | auto right_cols = right_->cols(); 36 | for (auto &col : right_cols) { 37 | col.offset += left_->tupleLen(); 38 | } 39 | 40 | cols_.insert(cols_.end(), right_cols.begin(), right_cols.end()); 41 | isend = false; 42 | fed_conds_ = std::move(conds); 43 | 44 | } 45 | 46 | void beginTuple() override { 47 | 48 | } 49 | 50 | void nextTuple() override { 51 | 52 | } 53 | 54 | std::unique_ptr Next() override { 55 | return nullptr; 56 | } 57 | 58 | Rid &rid() override { return _abstract_rid; } 59 | }; -------------------------------------------------------------------------------- /src/execution/executor_projection.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include "execution_defs.h" 13 | #include "execution_manager.h" 14 | #include "executor_abstract.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class ProjectionExecutor : public AbstractExecutor { 19 | private: 20 | std::unique_ptr prev_; // 投影节点的儿子节点 21 | std::vector cols_; // 需要投影的字段 22 | size_t len_; // 字段总长度 23 | std::vector sel_idxs_; 24 | 25 | public: 26 | ProjectionExecutor(std::unique_ptr prev, const std::vector &sel_cols) { 27 | prev_ = std::move(prev); 28 | 29 | size_t curr_offset = 0; 30 | auto &prev_cols = prev_->cols(); 31 | for (auto &sel_col : sel_cols) { 32 | auto pos = get_col(prev_cols, sel_col); 33 | sel_idxs_.push_back(pos - prev_cols.begin()); 34 | auto col = *pos; 35 | col.offset = curr_offset; 36 | curr_offset += col.len; 37 | cols_.push_back(col); 38 | } 39 | len_ = curr_offset; 40 | } 41 | 42 | void beginTuple() override {} 43 | 44 | void nextTuple() override {} 45 | 46 | std::unique_ptr Next() override { 47 | return nullptr; 48 | } 49 | 50 | Rid &rid() override { return _abstract_rid; } 51 | }; -------------------------------------------------------------------------------- /src/execution/executor_seq_scan.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "execution_defs.h" 14 | #include "execution_manager.h" 15 | #include "executor_abstract.h" 16 | #include "index/ix.h" 17 | #include "system/sm.h" 18 | 19 | class SeqScanExecutor : public AbstractExecutor { 20 | private: 21 | std::string tab_name_; // 表的名称 22 | std::vector conds_; // scan的条件 23 | RmFileHandle *fh_; // 表的数据文件句柄 24 | std::vector cols_; // scan后生成的记录的字段 25 | size_t len_; // scan后生成的每条记录的长度 26 | std::vector fed_conds_; // 同conds_,两个字段相同 27 | 28 | Rid rid_; 29 | std::unique_ptr scan_; // table_iterator 30 | 31 | SmManager *sm_manager_; 32 | 33 | public: 34 | SeqScanExecutor(SmManager *sm_manager, std::string tab_name, std::vector conds, Context *context) { 35 | sm_manager_ = sm_manager; 36 | tab_name_ = std::move(tab_name); 37 | conds_ = std::move(conds); 38 | TabMeta &tab = sm_manager_->db_.get_table(tab_name_); 39 | fh_ = sm_manager_->fhs_.at(tab_name_).get(); 40 | cols_ = tab.cols; 41 | len_ = cols_.back().offset + cols_.back().len; 42 | 43 | context_ = context; 44 | 45 | fed_conds_ = conds_; 46 | } 47 | 48 | /** 49 | * @brief 构建表迭代器scan_,并开始迭代扫描,直到扫描到第一个满足谓词条件的元组停止,并赋值给rid_ 50 | * 51 | */ 52 | void beginTuple() override { 53 | 54 | } 55 | 56 | /** 57 | * @brief 从当前scan_指向的记录开始迭代扫描,直到扫描到第一个满足谓词条件的元组停止,并赋值给rid_ 58 | * 59 | */ 60 | void nextTuple() override { 61 | 62 | } 63 | 64 | /** 65 | * @brief 返回下一个满足扫描条件的记录 66 | * 67 | * @return std::unique_ptr 68 | */ 69 | std::unique_ptr Next() override { 70 | return nullptr; 71 | } 72 | 73 | Rid &rid() override { return rid_; } 74 | }; -------------------------------------------------------------------------------- /src/execution/executor_update.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include "execution_defs.h" 13 | #include "execution_manager.h" 14 | #include "executor_abstract.h" 15 | #include "index/ix.h" 16 | #include "system/sm.h" 17 | 18 | class UpdateExecutor : public AbstractExecutor { 19 | private: 20 | TabMeta tab_; 21 | std::vector conds_; 22 | RmFileHandle *fh_; 23 | std::vector rids_; 24 | std::string tab_name_; 25 | std::vector set_clauses_; 26 | SmManager *sm_manager_; 27 | 28 | public: 29 | UpdateExecutor(SmManager *sm_manager, const std::string &tab_name, std::vector set_clauses, 30 | std::vector conds, std::vector rids, Context *context) { 31 | sm_manager_ = sm_manager; 32 | tab_name_ = tab_name; 33 | set_clauses_ = set_clauses; 34 | tab_ = sm_manager_->db_.get_table(tab_name); 35 | fh_ = sm_manager_->fhs_.at(tab_name).get(); 36 | conds_ = conds; 37 | rids_ = rids; 38 | context_ = context; 39 | } 40 | std::unique_ptr Next() override { 41 | 42 | return nullptr; 43 | } 44 | 45 | Rid &rid() override { return _abstract_rid; } 46 | }; -------------------------------------------------------------------------------- /src/index/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES ix_index_handle.cpp ix_scan.cpp) 2 | add_library(index STATIC ${SOURCES}) 3 | target_link_libraries(index storage) -------------------------------------------------------------------------------- /src/index/ix.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "ix_scan.h" 14 | #include "ix_manager.h" 15 | -------------------------------------------------------------------------------- /src/index/ix_scan.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "ix_scan.h" 12 | 13 | /** 14 | * @brief 15 | * @todo 加上读锁(需要使用缓冲池得到page) 16 | */ 17 | void IxScan::next() { 18 | assert(!is_end()); 19 | IxNodeHandle *node = ih_->fetch_node(iid_.page_no); 20 | assert(node->is_leaf_page()); 21 | assert(iid_.slot_no < node->get_size()); 22 | // increment slot no 23 | iid_.slot_no++; 24 | if (iid_.page_no != ih_->file_hdr_->last_leaf_ && iid_.slot_no == node->get_size()) { 25 | // go to next leaf 26 | iid_.slot_no = 0; 27 | iid_.page_no = node->get_next_leaf(); 28 | } 29 | } 30 | 31 | Rid IxScan::rid() const { 32 | return ih_->get_rid(iid_); 33 | } -------------------------------------------------------------------------------- /src/index/ix_scan.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "ix_defs.h" 14 | #include "ix_index_handle.h" 15 | 16 | // class IxIndexHandle; 17 | 18 | // 用于遍历叶子结点 19 | // 用于直接遍历叶子结点,而不用findleafpage来得到叶子结点 20 | // TODO:对page遍历时,要加上读锁 21 | class IxScan : public RecScan { 22 | const IxIndexHandle *ih_; 23 | Iid iid_; // 初始为lower(用于遍历的指针) 24 | Iid end_; // 初始为upper 25 | BufferPoolManager *bpm_; 26 | 27 | public: 28 | IxScan(const IxIndexHandle *ih, const Iid &lower, const Iid &upper, BufferPoolManager *bpm) 29 | : ih_(ih), iid_(lower), end_(upper), bpm_(bpm) {} 30 | 31 | void next() override; 32 | 33 | bool is_end() const override { return iid_ == end_; } 34 | 35 | Rid rid() const override; 36 | 37 | const Iid &iid() const { return iid_; } 38 | }; -------------------------------------------------------------------------------- /src/optimizer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES planner.cpp) 2 | add_library(planner STATIC ${SOURCES}) -------------------------------------------------------------------------------- /src/optimizer/optimizer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include "errors.h" 16 | #include "execution/execution.h" 17 | #include "parser/parser.h" 18 | #include "system/sm.h" 19 | #include "common/context.h" 20 | #include "transaction/transaction_manager.h" 21 | #include "planner.h" 22 | #include "plan.h" 23 | 24 | class Optimizer { 25 | private: 26 | SmManager *sm_manager_; 27 | Planner *planner_; 28 | 29 | public: 30 | Optimizer(SmManager *sm_manager, Planner *planner) 31 | : sm_manager_(sm_manager), planner_(planner) 32 | {} 33 | 34 | std::shared_ptr plan_query(std::shared_ptr query, Context *context) { 35 | if (auto x = std::dynamic_pointer_cast(query->parse)) { 36 | // help; 37 | return std::make_shared(T_Help, std::string()); 38 | } else if (auto x = std::dynamic_pointer_cast(query->parse)) { 39 | // show tables; 40 | return std::make_shared(T_ShowTable, std::string()); 41 | } else if (auto x = std::dynamic_pointer_cast(query->parse)) { 42 | // desc table; 43 | return std::make_shared(T_DescTable, x->tab_name); 44 | } else if (auto x = std::dynamic_pointer_cast(query->parse)) { 45 | // begin; 46 | return std::make_shared(T_Transaction_begin, std::string()); 47 | } else if (auto x = std::dynamic_pointer_cast(query->parse)) { 48 | // abort; 49 | return std::make_shared(T_Transaction_abort, std::string()); 50 | } else if (auto x = std::dynamic_pointer_cast(query->parse)) { 51 | // commit; 52 | return std::make_shared(T_Transaction_commit, std::string()); 53 | } else if (auto x = std::dynamic_pointer_cast(query->parse)) { 54 | // rollback; 55 | return std::make_shared(T_Transaction_rollback, std::string()); 56 | } else { 57 | return planner_->do_planner(query, context); 58 | } 59 | } 60 | 61 | }; 62 | -------------------------------------------------------------------------------- /src/optimizer/planner.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "execution/execution_defs.h" 20 | #include "execution/execution_manager.h" 21 | #include "record/rm.h" 22 | #include "system/sm.h" 23 | #include "common/context.h" 24 | #include "plan.h" 25 | #include "parser/parser.h" 26 | #include "common/common.h" 27 | #include "analyze/analyze.h" 28 | 29 | class Planner { 30 | private: 31 | SmManager *sm_manager_; 32 | 33 | public: 34 | Planner(SmManager *sm_manager) : sm_manager_(sm_manager) {} 35 | 36 | 37 | std::shared_ptr do_planner(std::shared_ptr query, Context *context); 38 | 39 | private: 40 | std::shared_ptr logical_optimization(std::shared_ptr query, Context *context); 41 | std::shared_ptr physical_optimization(std::shared_ptr query, Context *context); 42 | 43 | std::shared_ptr make_one_rel(std::shared_ptr query); 44 | 45 | std::shared_ptr generate_sort_plan(std::shared_ptr query, std::shared_ptr plan); 46 | 47 | std::shared_ptr generate_select_plan(std::shared_ptr query, Context *context); 48 | 49 | 50 | // int get_indexNo(std::string tab_name, std::vector curr_conds); 51 | bool get_index_cols(std::string tab_name, std::vector curr_conds, std::vector& index_col_names); 52 | 53 | ColType interp_sv_type(ast::SvType sv_type) { 54 | std::map m = { 55 | {ast::SV_TYPE_INT, TYPE_INT}, {ast::SV_TYPE_FLOAT, TYPE_FLOAT}, {ast::SV_TYPE_STRING, TYPE_STRING}}; 56 | return m.at(sv_type); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(BISON REQUIRED) 2 | find_package(FLEX REQUIRED) 3 | 4 | bison_target(yacc yacc.y ${CMAKE_CURRENT_SOURCE_DIR}/yacc.tab.cpp 5 | DEFINES_FILE ${CMAKE_CURRENT_SOURCE_DIR}/yacc.tab.h) 6 | flex_target(lex lex.l ${CMAKE_CURRENT_SOURCE_DIR}/lex.yy.cpp) 7 | add_flex_bison_dependency(lex yacc) 8 | 9 | set(SOURCES ${BISON_yacc_OUTPUT_SOURCE} ${FLEX_lex_OUTPUTS} ast.cpp) 10 | add_library(parser STATIC ${SOURCES}) 11 | 12 | add_executable(test_parser test_parser.cpp) 13 | target_link_libraries(test_parser parser) 14 | add_test(NAME test_parser COMMAND test_parser 15 | WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 16 | -------------------------------------------------------------------------------- /src/parser/ast.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | #include "ast.h" 11 | 12 | namespace ast { 13 | 14 | std::shared_ptr parse_tree; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /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 | "DESC" { return DESC; } 68 | "INSERT" { return INSERT; } 69 | "INTO" { return INTO; } 70 | "VALUES" { return VALUES; } 71 | "DELETE" { return DELETE; } 72 | "FROM" { return FROM; } 73 | "WHERE" { return WHERE; } 74 | "UPDATE" { return UPDATE; } 75 | "SET" { return SET; } 76 | "SELECT" { return SELECT; } 77 | "INT" { return INT; } 78 | "CHAR" { return CHAR; } 79 | "FLOAT" { return FLOAT; } 80 | "INDEX" { return INDEX; } 81 | "AND" { return AND; } 82 | "JOIN" {return JOIN;} 83 | "EXIT" { return EXIT; } 84 | "HELP" { return HELP; } 85 | "ORDER" { return ORDER; } 86 | "BY" { return BY; } 87 | "ASC" { return ASC; } 88 | /* operators */ 89 | ">=" { return GEQ; } 90 | "<=" { return LEQ; } 91 | "<>" { return NEQ; } 92 | {single_op} { return yytext[0]; } 93 | /* id */ 94 | {identifier} { 95 | yylval->sv_str = yytext; 96 | return IDENTIFIER; 97 | } 98 | /* literals */ 99 | {value_int} { 100 | yylval->sv_int = atoi(yytext); 101 | return VALUE_INT; 102 | } 103 | {value_float} { 104 | yylval->sv_float = atof(yytext); 105 | return VALUE_FLOAT; 106 | } 107 | {value_string} { 108 | yylval->sv_str = std::string(yytext + 1, strlen(yytext) - 2); 109 | return VALUE_STRING; 110 | } 111 | /* EOF */ 112 | <> { return T_EOF; } 113 | /* unexpected char */ 114 | . { std::cerr << "Lexer Error: unexpected character " << yytext[0] << std::endl; } 115 | %% 116 | -------------------------------------------------------------------------------- /src/parser/parse_node.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace ast { 18 | 19 | // Base class for tree nodes 20 | struct TreeNode { 21 | virtual ~TreeNode() = default; // enable polymorphism 22 | }; 23 | 24 | struct Help : public TreeNode { 25 | }; 26 | 27 | struct ShowTables : public TreeNode { 28 | }; 29 | 30 | struct TxnBegin : public TreeNode { 31 | }; 32 | 33 | struct TxnCommit : public TreeNode { 34 | }; 35 | 36 | struct TxnAbort : public TreeNode { 37 | }; 38 | 39 | struct TxnRollback : public TreeNode { 40 | }; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/parser/parser.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "ast_printer.h" 14 | #include "ast.h" 15 | #include "parser_defs.h" 16 | -------------------------------------------------------------------------------- /src/parser/parser_defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "defs.h" 14 | 15 | int yyparse(); 16 | 17 | typedef struct yy_buffer_state *YY_BUFFER_STATE; 18 | 19 | YY_BUFFER_STATE yy_scan_string(const char *str); 20 | 21 | void yy_delete_buffer(YY_BUFFER_STATE buffer); 22 | -------------------------------------------------------------------------------- /src/parser/test_parser.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | #undef NDEBUG 11 | 12 | #include 13 | 14 | #include "parser.h" 15 | 16 | int main() { 17 | std::vector sqls = { 18 | "show tables;", 19 | "desc tb;", 20 | "create table tb (a int, b float, c char(4));", 21 | "drop table tb;", 22 | "create index tb(a);", 23 | "create index tb(a, b, c);", 24 | "drop index tb(a, b, c);", 25 | "drop index tb(b);", 26 | "insert into tb values (1, 3.14, 'pi');", 27 | "delete from tb where a = 1;", 28 | "update tb set a = 1, b = 2.2, c = 'xyz' where x = 2 and y < 1.1 and z > 'abc';", 29 | "select * from tb;", 30 | "select * from tb where x <> 2 and y >= 3. and z <= '123' and b < tb.a;", 31 | "select x.a, y.b from x, y where x.a = y.b and c = d;", 32 | "select x.a, y.b from x join y where x.a = y.b and c = d;", 33 | "exit;", 34 | "help;", 35 | "", 36 | }; 37 | for (auto &sql : sqls) { 38 | std::cout << sql << std::endl; 39 | YY_BUFFER_STATE buf = yy_scan_string(sql.c_str()); 40 | assert(yyparse() == 0); 41 | if (ast::parse_tree != nullptr) { 42 | ast::TreePrinter::print(ast::parse_tree); 43 | yy_delete_buffer(buf); 44 | std::cout << std::endl; 45 | } else { 46 | std::cout << "exit/EOF" << std::endl; 47 | } 48 | } 49 | ast::parse_tree.reset(); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /src/parser/yacc.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.0.4. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . */ 19 | 20 | /* As a special exception, you may create a larger work that contains 21 | part or all of the Bison parser skeleton and distribute that work 22 | under terms of your choice, so long as that work isn't itself a 23 | parser generator using the skeleton or a modified version thereof 24 | as a parser skeleton. Alternatively, if you modify or redistribute 25 | the parser skeleton itself, you may (at your option) remove this 26 | special exception, which will cause the skeleton and the resulting 27 | Bison output files to be licensed under the GNU General Public 28 | License without this special exception. 29 | 30 | This special exception was added by the Free Software Foundation in 31 | version 2.2 of Bison. */ 32 | 33 | #ifndef YY_YY_RUCBASE_LAB_SRC_PARSER_YACC_TAB_H_INCLUDED 34 | # define YY_YY_RUCBASE_LAB_SRC_PARSER_YACC_TAB_H_INCLUDED 35 | /* Debug traces. */ 36 | #ifndef YYDEBUG 37 | # define YYDEBUG 0 38 | #endif 39 | #if YYDEBUG 40 | extern int yydebug; 41 | #endif 42 | 43 | /* Token type. */ 44 | #ifndef YYTOKENTYPE 45 | # define YYTOKENTYPE 46 | enum yytokentype 47 | { 48 | SHOW = 258, 49 | TABLES = 259, 50 | CREATE = 260, 51 | TABLE = 261, 52 | DROP = 262, 53 | DESC = 263, 54 | INSERT = 264, 55 | INTO = 265, 56 | VALUES = 266, 57 | DELETE = 267, 58 | FROM = 268, 59 | ASC = 269, 60 | ORDER = 270, 61 | BY = 271, 62 | WHERE = 272, 63 | UPDATE = 273, 64 | SET = 274, 65 | SELECT = 275, 66 | INT = 276, 67 | CHAR = 277, 68 | FLOAT = 278, 69 | INDEX = 279, 70 | AND = 280, 71 | JOIN = 281, 72 | EXIT = 282, 73 | HELP = 283, 74 | TXN_BEGIN = 284, 75 | TXN_COMMIT = 285, 76 | TXN_ABORT = 286, 77 | TXN_ROLLBACK = 287, 78 | ORDER_BY = 288, 79 | LEQ = 289, 80 | NEQ = 290, 81 | GEQ = 291, 82 | T_EOF = 292, 83 | IDENTIFIER = 293, 84 | VALUE_STRING = 294, 85 | VALUE_INT = 295, 86 | VALUE_FLOAT = 296 87 | }; 88 | #endif 89 | 90 | /* Value type. */ 91 | 92 | /* Location type. */ 93 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 94 | typedef struct YYLTYPE YYLTYPE; 95 | struct YYLTYPE 96 | { 97 | int first_line; 98 | int first_column; 99 | int last_line; 100 | int last_column; 101 | }; 102 | # define YYLTYPE_IS_DECLARED 1 103 | # define YYLTYPE_IS_TRIVIAL 1 104 | #endif 105 | 106 | 107 | 108 | int yyparse (void); 109 | 110 | #endif /* !YY_YY_RUCBASE_LAB_SRC_PARSER_YACC_TAB_H_INCLUDED */ 111 | -------------------------------------------------------------------------------- /src/parser/yacc.tab.hpp: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.8.2. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 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 | /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, 35 | especially those whose name start with YY_ or yy_. They are 36 | private implementation details that can be changed or removed. */ 37 | 38 | #ifndef YY_YY_YACC_TAB_HPP_INCLUDED 39 | # define YY_YY_YACC_TAB_HPP_INCLUDED 40 | /* Debug traces. */ 41 | #ifndef YYDEBUG 42 | # define YYDEBUG 0 43 | #endif 44 | #if YYDEBUG 45 | extern int yydebug; 46 | #endif 47 | 48 | /* Token kinds. */ 49 | #ifndef YYTOKENTYPE 50 | # define YYTOKENTYPE 51 | enum yytokentype 52 | { 53 | YYEMPTY = -2, 54 | YYEOF = 0, /* "end of file" */ 55 | YYerror = 256, /* error */ 56 | YYUNDEF = 257, /* "invalid token" */ 57 | SHOW = 258, /* SHOW */ 58 | TABLES = 259, /* TABLES */ 59 | CREATE = 260, /* CREATE */ 60 | TABLE = 261, /* TABLE */ 61 | DROP = 262, /* DROP */ 62 | DESC = 263, /* DESC */ 63 | INSERT = 264, /* INSERT */ 64 | INTO = 265, /* INTO */ 65 | VALUES = 266, /* VALUES */ 66 | DELETE = 267, /* DELETE */ 67 | FROM = 268, /* FROM */ 68 | ASC = 269, /* ASC */ 69 | ORDER = 270, /* ORDER */ 70 | BY = 271, /* BY */ 71 | WHERE = 272, /* WHERE */ 72 | UPDATE = 273, /* UPDATE */ 73 | SET = 274, /* SET */ 74 | SELECT = 275, /* SELECT */ 75 | INT = 276, /* INT */ 76 | CHAR = 277, /* CHAR */ 77 | FLOAT = 278, /* FLOAT */ 78 | INDEX = 279, /* INDEX */ 79 | AND = 280, /* AND */ 80 | JOIN = 281, /* JOIN */ 81 | EXIT = 282, /* EXIT */ 82 | HELP = 283, /* HELP */ 83 | TXN_BEGIN = 284, /* TXN_BEGIN */ 84 | TXN_COMMIT = 285, /* TXN_COMMIT */ 85 | TXN_ABORT = 286, /* TXN_ABORT */ 86 | TXN_ROLLBACK = 287, /* TXN_ROLLBACK */ 87 | ORDER_BY = 288, /* ORDER_BY */ 88 | LEQ = 289, /* LEQ */ 89 | NEQ = 290, /* NEQ */ 90 | GEQ = 291, /* GEQ */ 91 | T_EOF = 292, /* T_EOF */ 92 | IDENTIFIER = 293, /* IDENTIFIER */ 93 | VALUE_STRING = 294, /* VALUE_STRING */ 94 | VALUE_INT = 295, /* VALUE_INT */ 95 | VALUE_FLOAT = 296 /* VALUE_FLOAT */ 96 | }; 97 | typedef enum yytokentype yytoken_kind_t; 98 | #endif 99 | 100 | /* Value type. */ 101 | 102 | /* Location type. */ 103 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 104 | typedef struct YYLTYPE YYLTYPE; 105 | struct YYLTYPE 106 | { 107 | int first_line; 108 | int first_column; 109 | int last_line; 110 | int last_column; 111 | }; 112 | # define YYLTYPE_IS_DECLARED 1 113 | # define YYLTYPE_IS_TRIVIAL 1 114 | #endif 115 | 116 | 117 | 118 | 119 | int yyparse (void); 120 | 121 | 122 | #endif /* !YY_YY_YACC_TAB_HPP_INCLUDED */ 123 | -------------------------------------------------------------------------------- /src/record/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES rm_file_handle.cpp rm_scan.cpp) 2 | add_library(record STATIC ${SOURCES}) 3 | add_library(records SHARED ${SOURCES}) 4 | target_link_libraries(record system transaction system storage) 5 | -------------------------------------------------------------------------------- /src/record/bitmap.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | static constexpr int BITMAP_WIDTH = 8; 17 | static constexpr unsigned BITMAP_HIGHEST_BIT = 0x80u; // 128 (2^7) 18 | 19 | class Bitmap { 20 | public: 21 | // 从地址bm开始的size个字节全部置0 22 | static void init(char *bm, int size) { memset(bm, 0, size); } 23 | 24 | // pos位 置1 25 | static void set(char *bm, int pos) { bm[get_bucket(pos)] |= get_bit(pos); } 26 | 27 | // pos位 置0 28 | static void reset(char *bm, int pos) { bm[get_bucket(pos)] &= static_cast(~get_bit(pos)); } 29 | 30 | // 如果pos位是1,则返回true 31 | static bool is_set(const char *bm, int pos) { return (bm[get_bucket(pos)] & get_bit(pos)) != 0; } 32 | 33 | /** 34 | * @brief 找下一个为0 or 1的位 35 | * @param bit false表示要找下一个为0的位,true表示要找下一个为1的位 36 | * @param bm 要找的起始地址为bm 37 | * @param max_n 要找的从起始地址开始的偏移为[curr+1,max_n) 38 | * @param curr 要找的从起始地址开始的偏移为[curr+1,max_n) 39 | * @return 找到了就返回偏移位置,没找到就返回max_n 40 | */ 41 | static int next_bit(bool bit, const char *bm, int max_n, int curr) { 42 | for (int i = curr + 1; i < max_n; i++) { 43 | if (is_set(bm, i) == bit) { 44 | return i; 45 | } 46 | } 47 | return max_n; 48 | } 49 | 50 | // 找第一个为0 or 1的位 51 | static int first_bit(bool bit, const char *bm, int max_n) { return next_bit(bit, bm, max_n, -1); } 52 | 53 | // for example: 54 | // rid_.slot_no = Bitmap::next_bit(true, page_handle.bitmap, file_handle_->file_hdr_.num_records_per_page, 55 | // rid_.slot_no); int slot_no = Bitmap::first_bit(false, page_handle.bitmap, file_hdr_.num_records_per_page); 56 | 57 | private: 58 | static int get_bucket(int pos) { return pos / BITMAP_WIDTH; } 59 | 60 | static char get_bit(int pos) { return BITMAP_HIGHEST_BIT >> static_cast(pos % BITMAP_WIDTH); } 61 | }; 62 | -------------------------------------------------------------------------------- /src/record/rm.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "rm_scan.h" 14 | #include "rm_manager.h" 15 | #include "rm_defs.h" 16 | -------------------------------------------------------------------------------- /src/record/rm_defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "defs.h" 14 | #include "storage/buffer_pool_manager.h" 15 | 16 | constexpr int RM_NO_PAGE = -1; 17 | constexpr int RM_FILE_HDR_PAGE = 0; 18 | constexpr int RM_FIRST_RECORD_PAGE = 1; 19 | constexpr int RM_MAX_RECORD_SIZE = 512; 20 | 21 | /* 文件头,记录表数据文件的元信息,写入磁盘中文件的第0号页面 */ 22 | struct RmFileHdr { 23 | int record_size; // 表中每条记录的大小,由于不包含变长字段,因此当前字段初始化后保持不变 24 | int num_pages; // 文件中分配的页面个数(初始化为1) 25 | int num_records_per_page; // 每个页面最多能存储的元组个数 26 | int first_free_page_no; // 文件中当前第一个包含空闲空间的页面号(初始化为-1) 27 | int bitmap_size; // 每个页面bitmap大小 28 | }; 29 | 30 | /* 表数据文件中每个页面的页头,记录每个页面的元信息 */ 31 | struct RmPageHdr { 32 | int next_free_page_no; // 当前页面满了之后,下一个包含空闲空间的页面号(初始化为-1) 33 | int num_records; // 当前页面中当前已经存储的记录个数(初始化为0) 34 | }; 35 | 36 | /* 表中的记录 */ 37 | struct RmRecord { 38 | char* data; // 记录的数据 39 | int size; // 记录的大小 40 | bool allocated_ = false; // 是否已经为数据分配空间 41 | 42 | RmRecord() = default; 43 | 44 | RmRecord(const RmRecord& other) { 45 | size = other.size; 46 | data = new char[size]; 47 | memcpy(data, other.data, size); 48 | allocated_ = true; 49 | }; 50 | 51 | RmRecord &operator=(const RmRecord& other) { 52 | size = other.size; 53 | data = new char[size]; 54 | memcpy(data, other.data, size); 55 | allocated_ = true; 56 | return *this; 57 | }; 58 | 59 | RmRecord(int size_) { 60 | size = size_; 61 | data = new char[size_]; 62 | allocated_ = true; 63 | } 64 | 65 | RmRecord(int size_, char* data_) { 66 | size = size_; 67 | data = new char[size_]; 68 | memcpy(data, data_, size_); 69 | allocated_ = true; 70 | } 71 | 72 | void SetData(char* data_) { 73 | memcpy(data, data_, size); 74 | } 75 | 76 | void Deserialize(const char* data_) { 77 | size = *reinterpret_cast(data_); 78 | if(allocated_) { 79 | delete[] data; 80 | } 81 | data = new char[size]; 82 | memcpy(data, data_ + sizeof(int), size); 83 | } 84 | 85 | ~RmRecord() { 86 | if(allocated_) { 87 | delete[] data; 88 | } 89 | allocated_ = false; 90 | data = nullptr; 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /src/record/rm_file_handle.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "rm_file_handle.h" 12 | 13 | /** 14 | * @description: 获取当前表中记录号为rid的记录 15 | * @param {Rid&} rid 记录号,指定记录的位置 16 | * @param {Context*} context 17 | * @return {unique_ptr} rid对应的记录对象指针 18 | */ 19 | std::unique_ptr RmFileHandle::get_record(const Rid& rid, Context* context) const { 20 | // Todo: 21 | // 1. 获取指定记录所在的page handle 22 | // 2. 初始化一个指向RmRecord的指针(赋值其内部的data和size) 23 | 24 | return nullptr; 25 | } 26 | 27 | /** 28 | * @description: 在当前表中插入一条记录,不指定插入位置 29 | * @param {char*} buf 要插入的记录的数据 30 | * @param {Context*} context 31 | * @return {Rid} 插入的记录的记录号(位置) 32 | */ 33 | Rid RmFileHandle::insert_record(char* buf, Context* context) { 34 | // Todo: 35 | // 1. 获取当前未满的page handle 36 | // 2. 在page handle中找到空闲slot位置 37 | // 3. 将buf复制到空闲slot位置 38 | // 4. 更新page_handle.page_hdr中的数据结构 39 | // 注意考虑插入一条记录后页面已满的情况,需要更新file_hdr_.first_free_page_no 40 | 41 | return Rid{-1, -1}; 42 | } 43 | 44 | /** 45 | * @description: 在当前表中的指定位置插入一条记录 46 | * @param {Rid&} rid 要插入记录的位置 47 | * @param {char*} buf 要插入记录的数据 48 | */ 49 | void RmFileHandle::insert_record(const Rid& rid, char* buf) { 50 | 51 | } 52 | 53 | /** 54 | * @description: 删除记录文件中记录号为rid的记录 55 | * @param {Rid&} rid 要删除的记录的记录号(位置) 56 | * @param {Context*} context 57 | */ 58 | void RmFileHandle::delete_record(const Rid& rid, Context* context) { 59 | // Todo: 60 | // 1. 获取指定记录所在的page handle 61 | // 2. 更新page_handle.page_hdr中的数据结构 62 | // 注意考虑删除一条记录后页面未满的情况,需要调用release_page_handle() 63 | } 64 | 65 | 66 | /** 67 | * @description: 更新记录文件中记录号为rid的记录 68 | * @param {Rid&} rid 要更新的记录的记录号(位置) 69 | * @param {char*} buf 新记录的数据 70 | * @param {Context*} context 71 | */ 72 | void RmFileHandle::update_record(const Rid& rid, char* buf, Context* context) { 73 | // Todo: 74 | // 1. 获取指定记录所在的page handle 75 | // 2. 更新记录 76 | 77 | } 78 | 79 | /** 80 | * 以下函数为辅助函数,仅提供参考,可以选择完成如下函数,也可以删除如下函数,在单元测试中不涉及如下函数接口的直接调用 81 | */ 82 | /** 83 | * @description: 获取指定页面的页面句柄 84 | * @param {int} page_no 页面号 85 | * @return {RmPageHandle} 指定页面的句柄 86 | */ 87 | RmPageHandle RmFileHandle::fetch_page_handle(int page_no) const { 88 | // Todo: 89 | // 使用缓冲池获取指定页面,并生成page_handle返回给上层 90 | // if page_no is invalid, throw PageNotExistError exception 91 | 92 | return RmPageHandle(&file_hdr_, nullptr); 93 | } 94 | 95 | /** 96 | * @description: 创建一个新的page handle 97 | * @return {RmPageHandle} 新的PageHandle 98 | */ 99 | RmPageHandle RmFileHandle::create_new_page_handle() { 100 | // Todo: 101 | // 1.使用缓冲池来创建一个新page 102 | // 2.更新page handle中的相关信息 103 | // 3.更新file_hdr_ 104 | 105 | return RmPageHandle(&file_hdr_, nullptr); 106 | } 107 | 108 | /** 109 | * @brief 创建或获取一个空闲的page handle 110 | * 111 | * @return RmPageHandle 返回生成的空闲page handle 112 | * @note pin the page, remember to unpin it outside! 113 | */ 114 | RmPageHandle RmFileHandle::create_page_handle() { 115 | // Todo: 116 | // 1. 判断file_hdr_中是否还有空闲页 117 | // 1.1 没有空闲页:使用缓冲池来创建一个新page;可直接调用create_new_page_handle() 118 | // 1.2 有空闲页:直接获取第一个空闲页 119 | // 2. 生成page handle并返回给上层 120 | 121 | return RmPageHandle(&file_hdr_, nullptr); 122 | } 123 | 124 | /** 125 | * @description: 当一个页面从没有空闲空间的状态变为有空闲空间状态时,更新文件头和页头中空闲页面相关的元数据 126 | */ 127 | void RmFileHandle::release_page_handle(RmPageHandle&page_handle) { 128 | // Todo: 129 | // 当page从已满变成未满,考虑如何更新: 130 | // 1. page_handle.page_hdr->next_free_page_no 131 | // 2. file_hdr_.first_free_page_no 132 | 133 | } -------------------------------------------------------------------------------- /src/record/rm_file_handle.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include "bitmap.h" 18 | #include "common/context.h" 19 | #include "rm_defs.h" 20 | 21 | class RmManager; 22 | 23 | /* 对表数据文件中的页面进行封装 */ 24 | struct RmPageHandle { 25 | const RmFileHdr *file_hdr; // 当前页面所在文件的文件头指针 26 | Page *page; // 页面的实际数据,包括页面存储的数据、元信息等 27 | RmPageHdr *page_hdr; // page->data的第一部分,存储页面元信息,指针指向首地址,长度为sizeof(RmPageHdr) 28 | char *bitmap; // page->data的第二部分,存储页面的bitmap,指针指向首地址,长度为file_hdr->bitmap_size 29 | char *slots; // page->data的第三部分,存储表的记录,指针指向首地址,每个slot的长度为file_hdr->record_size 30 | 31 | RmPageHandle(const RmFileHdr *fhdr_, Page *page_) : file_hdr(fhdr_), page(page_) { 32 | page_hdr = reinterpret_cast(page->get_data() + page->OFFSET_PAGE_HDR); 33 | bitmap = page->get_data() + sizeof(RmPageHdr) + page->OFFSET_PAGE_HDR; 34 | slots = bitmap + file_hdr->bitmap_size; 35 | } 36 | 37 | // 返回指定slot_no的slot存储收地址 38 | char* get_slot(int slot_no) const { 39 | return slots + slot_no * file_hdr->record_size; // slots的首地址 + slot个数 * 每个slot的大小(每个record的大小) 40 | } 41 | }; 42 | 43 | /* 每个RmFileHandle对应一个表的数据文件,里面有多个page,每个page的数据封装在RmPageHandle中 */ 44 | class RmFileHandle { 45 | friend class RmScan; 46 | friend class RmManager; 47 | 48 | private: 49 | DiskManager *disk_manager_; 50 | BufferPoolManager *buffer_pool_manager_; 51 | int fd_; // 打开文件后产生的文件句柄 52 | RmFileHdr file_hdr_; // 文件头,维护当前表文件的元数据 53 | 54 | public: 55 | RmFileHandle(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager, int fd) 56 | : disk_manager_(disk_manager), buffer_pool_manager_(buffer_pool_manager), fd_(fd) { 57 | // 注意:这里从磁盘中读出文件描述符为fd的文件的file_hdr,读到内存中 58 | // 这里实际就是初始化file_hdr,只不过是从磁盘中读出进行初始化 59 | // init file_hdr_ 60 | disk_manager_->read_page(fd, RM_FILE_HDR_PAGE, (char *)&file_hdr_, sizeof(file_hdr_)); 61 | // disk_manager管理的fd对应的文件中,设置从file_hdr_.num_pages开始分配page_no 62 | disk_manager_->set_fd2pageno(fd, file_hdr_.num_pages); 63 | } 64 | 65 | RmFileHdr get_file_hdr() { return file_hdr_; } 66 | int GetFd() { return fd_; } 67 | 68 | /* 判断指定位置上是否已经存在一条记录,通过Bitmap来判断 */ 69 | bool is_record(const Rid &rid) const { 70 | RmPageHandle page_handle = fetch_page_handle(rid.page_no); 71 | return Bitmap::is_set(page_handle.bitmap, rid.slot_no); // page的slot_no位置上是否有record 72 | } 73 | 74 | std::unique_ptr get_record(const Rid &rid, Context *context) const; 75 | 76 | Rid insert_record(char *buf, Context *context); 77 | 78 | void insert_record(const Rid &rid, char *buf); 79 | 80 | void delete_record(const Rid &rid, Context *context); 81 | 82 | void update_record(const Rid &rid, char *buf, Context *context); 83 | 84 | RmPageHandle create_new_page_handle(); 85 | 86 | RmPageHandle fetch_page_handle(int page_no) const; 87 | 88 | private: 89 | RmPageHandle create_page_handle(); 90 | 91 | void release_page_handle(RmPageHandle &page_handle); 92 | }; -------------------------------------------------------------------------------- /src/record/rm_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include "bitmap.h" 16 | #include "rm_defs.h" 17 | #include "rm_file_handle.h" 18 | 19 | /* 记录管理器,用于管理表的数据文件,进行文件的创建、打开、删除、关闭 */ 20 | class RmManager { 21 | private: 22 | DiskManager *disk_manager_; 23 | BufferPoolManager *buffer_pool_manager_; 24 | 25 | public: 26 | RmManager(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager) 27 | : disk_manager_(disk_manager), buffer_pool_manager_(buffer_pool_manager) {} 28 | 29 | /** 30 | * @description: 创建表的数据文件并初始化相关信息 31 | * @param {string&} filename 要创建的文件名称 32 | * @param {int} record_size 表中记录的大小 33 | */ 34 | void create_file(const std::string& filename, int record_size) { 35 | if (record_size < 1 || record_size > RM_MAX_RECORD_SIZE) { 36 | throw InvalidRecordSizeError(record_size); 37 | } 38 | disk_manager_->create_file(filename); 39 | int fd = disk_manager_->open_file(filename); 40 | 41 | // 初始化file header 42 | RmFileHdr file_hdr{}; 43 | file_hdr.record_size = record_size; 44 | file_hdr.num_pages = 1; 45 | file_hdr.first_free_page_no = RM_NO_PAGE; 46 | // We have: sizeof(hdr) + (n + 7) / 8 + n * record_size <= PAGE_SIZE 47 | file_hdr.num_records_per_page = 48 | (BITMAP_WIDTH * (PAGE_SIZE - 1 - (int)sizeof(RmFileHdr)) + 1) / (1 + record_size * BITMAP_WIDTH); 49 | file_hdr.bitmap_size = (file_hdr.num_records_per_page + BITMAP_WIDTH - 1) / BITMAP_WIDTH; 50 | 51 | // 将file header写入磁盘文件(名为file name,文件描述符为fd)中的第0页 52 | // head page直接写入磁盘,没有经过缓冲区的NewPage,那么也就不需要FlushPage 53 | disk_manager_->write_page(fd, RM_FILE_HDR_PAGE, (char *)&file_hdr, sizeof(file_hdr)); 54 | disk_manager_->close_file(fd); 55 | } 56 | 57 | /** 58 | * @description: 删除表的数据文件 59 | * @param {string&} filename 要删除的文件名称 60 | */ 61 | void destroy_file(const std::string& filename) { disk_manager_->destroy_file(filename); } 62 | 63 | // 注意这里打开文件,创建并返回了record file handle的指针 64 | /** 65 | * @description: 打开表的数据文件,并返回文件句柄 66 | * @param {string&} filename 要打开的文件名称 67 | * @return {unique_ptr} 文件句柄的指针 68 | */ 69 | std::unique_ptr open_file(const std::string& filename) { 70 | int fd = disk_manager_->open_file(filename); 71 | return std::make_unique(disk_manager_, buffer_pool_manager_, fd); 72 | } 73 | /** 74 | * @description: 关闭表的数据文件 75 | * @param {RmFileHandle*} file_handle 要关闭文件的句柄 76 | */ 77 | void close_file(const RmFileHandle* file_handle) { 78 | disk_manager_->write_page(file_handle->fd_, RM_FILE_HDR_PAGE, (char *)&file_handle->file_hdr_, 79 | sizeof(file_handle->file_hdr_)); 80 | // 缓冲区的所有页刷到磁盘,注意这句话必须写在close_file前面 81 | buffer_pool_manager_->flush_all_pages(file_handle->fd_); 82 | disk_manager_->close_file(file_handle->fd_); 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /src/record/rm_scan.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "rm_scan.h" 12 | #include "rm_file_handle.h" 13 | 14 | /** 15 | * @brief 初始化file_handle和rid 16 | * @param file_handle 17 | */ 18 | RmScan::RmScan(const RmFileHandle *file_handle) : file_handle_(file_handle) { 19 | // Todo: 20 | // 初始化file_handle和rid(指向第一个存放了记录的位置) 21 | 22 | } 23 | 24 | /** 25 | * @brief 找到文件中下一个存放了记录的位置 26 | */ 27 | void RmScan::next() { 28 | // Todo: 29 | // 找到文件中下一个存放了记录的非空闲位置,用rid_来指向这个位置 30 | 31 | } 32 | 33 | /** 34 | * @brief ​ 判断是否到达文件末尾 35 | */ 36 | bool RmScan::is_end() const { 37 | // Todo: 修改返回值 38 | 39 | return false; 40 | } 41 | 42 | /** 43 | * @brief RmScan内部存放的rid 44 | */ 45 | Rid RmScan::rid() const { 46 | return rid_; 47 | } -------------------------------------------------------------------------------- /src/record/rm_scan.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "rm_defs.h" 14 | 15 | class RmFileHandle; 16 | 17 | class RmScan : public RecScan { 18 | const RmFileHandle *file_handle_; 19 | Rid rid_; 20 | public: 21 | RmScan(const RmFileHandle *file_handle); 22 | 23 | void next() override; 24 | 25 | bool is_end() const override; 26 | 27 | Rid rid() const override; 28 | }; 29 | -------------------------------------------------------------------------------- /src/record_printer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "common/context.h" 19 | #include "common/config.h" 20 | 21 | #define RECORD_COUNT_LENGTH 40 22 | 23 | class RecordPrinter { 24 | static constexpr size_t COL_WIDTH = 16; 25 | size_t num_cols; 26 | public: 27 | RecordPrinter(size_t num_cols_) : num_cols(num_cols_) { 28 | assert(num_cols_ > 0); 29 | } 30 | 31 | void print_separator(Context *context) const { 32 | for (size_t i = 0; i < num_cols; i++) { 33 | // std::cout << '+' << std::string(COL_WIDTH + 2, '-'); 34 | std::string str = "+" + std::string(COL_WIDTH + 2, '-'); 35 | if(context->ellipsis_ == false && *context->offset_ + RECORD_COUNT_LENGTH + str.length() < BUFFER_LENGTH) { 36 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 37 | *(context->offset_) = *(context->offset_) + str.length(); 38 | } 39 | else { 40 | context->ellipsis_ = true; 41 | } 42 | } 43 | std::string str = "+\n"; 44 | if(context->ellipsis_ == false && *context->offset_ + RECORD_COUNT_LENGTH + str.length() < BUFFER_LENGTH) { 45 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 46 | *(context->offset_) = *(context->offset_) + str.length(); 47 | } 48 | else { 49 | context->ellipsis_ = true; 50 | } 51 | } 52 | 53 | void print_record(const std::vector &rec_str, Context *context) const { 54 | assert(rec_str.size() == num_cols); 55 | for (auto col: rec_str) { 56 | if (col.size() > COL_WIDTH) { 57 | col = col.substr(0, COL_WIDTH - 3) + "..."; 58 | } 59 | // std::cout << "| " << std::setw(COL_WIDTH) << col << ' '; 60 | std::stringstream ss; 61 | ss << "| " << std::setw(COL_WIDTH) << col << " "; 62 | if(context->ellipsis_ == false && *context->offset_ + RECORD_COUNT_LENGTH + ss.str().length() < BUFFER_LENGTH) { 63 | memcpy(context->data_send_ + *(context->offset_), ss.str().c_str(), ss.str().length()); 64 | *(context->offset_) = *(context->offset_) + ss.str().length(); 65 | } 66 | else { 67 | context->ellipsis_ = true; 68 | } 69 | } 70 | // std::cout << "|\n"; 71 | std::string str = "|\n"; 72 | if(context->ellipsis_ == false && *context->offset_ + RECORD_COUNT_LENGTH + str.length() < BUFFER_LENGTH) { 73 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 74 | *(context->offset_) = *(context->offset_) + str.length(); 75 | } 76 | } 77 | 78 | static void print_record_count(size_t num_rec, Context *context) { 79 | // std::cout << "Total record(s): " << num_rec << '\n'; 80 | std::string str = ""; 81 | if(context->ellipsis_ == true) { 82 | str = "... ...\n"; 83 | } 84 | str += "Total record(s): " + std::to_string(num_rec) + '\n'; 85 | memcpy(context->data_send_ + *(context->offset_), str.c_str(), str.length()); 86 | *(context->offset_) = *(context->offset_) + str.length(); 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /src/recovery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES log_manager.cpp log_recovery.cpp) 2 | add_library(recovery STATIC ${SOURCES}) 3 | add_library(recoverys SHARED ${SOURCES}) 4 | target_link_libraries(recovery system pthread) 5 | -------------------------------------------------------------------------------- /src/recovery/log_defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "defs.h" 14 | #include "storage/disk_manager.h" 15 | #include "common/config.h" 16 | 17 | #include 18 | #include 19 | 20 | static constexpr std::chrono::duration FLUSH_TIMEOUT = std::chrono::seconds(3); 21 | // the offset of log_type_ in log header 22 | static constexpr int OFFSET_LOG_TYPE = 0; 23 | // the offset of lsn_ in log header 24 | static constexpr int OFFSET_LSN = sizeof(int); 25 | // the offset of log_tot_len_ in log header 26 | static constexpr int OFFSET_LOG_TOT_LEN = OFFSET_LSN + sizeof(lsn_t); 27 | // the offset of log_tid_ in log header 28 | static constexpr int OFFSET_LOG_TID = OFFSET_LOG_TOT_LEN + sizeof(uint32_t); 29 | // the offset of prev_lsn_ in log header 30 | static constexpr int OFFSET_PREV_LSN = OFFSET_LOG_TID + sizeof(txn_id_t); 31 | // offset of log data 32 | static constexpr int OFFSET_LOG_DATA = OFFSET_PREV_LSN + sizeof(lsn_t); 33 | // sizeof log_header 34 | static constexpr int LOG_HEADER_SIZE = OFFSET_LOG_DATA; 35 | 36 | -------------------------------------------------------------------------------- /src/recovery/log_manager.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include 12 | #include "log_manager.h" 13 | 14 | /** 15 | * @description: 添加日志记录到日志缓冲区中,并返回日志记录号 16 | * @param {LogRecord*} log_record 要写入缓冲区的日志记录 17 | * @return {lsn_t} 返回该日志的日志记录号 18 | */ 19 | lsn_t LogManager::add_log_to_buffer(LogRecord* log_record) { 20 | 21 | } 22 | 23 | /** 24 | * @description: 把日志缓冲区的内容刷到磁盘中,由于目前只设置了一个缓冲区,因此需要阻塞其他日志操作 25 | */ 26 | void LogManager::flush_log_to_disk() { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/recovery/log_recovery.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "log_recovery.h" 12 | 13 | /** 14 | * @description: analyze阶段,需要获得脏页表(DPT)和未完成的事务列表(ATT) 15 | */ 16 | void RecoveryManager::analyze() { 17 | 18 | } 19 | 20 | /** 21 | * @description: 重做所有未落盘的操作 22 | */ 23 | void RecoveryManager::redo() { 24 | 25 | } 26 | 27 | /** 28 | * @description: 回滚未完成的事务 29 | */ 30 | void RecoveryManager::undo() { 31 | 32 | } -------------------------------------------------------------------------------- /src/recovery/log_recovery.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include "log_manager.h" 16 | #include "storage/disk_manager.h" 17 | #include "system/sm_manager.h" 18 | 19 | class RedoLogsInPage { 20 | public: 21 | RedoLogsInPage() { table_file_ = nullptr; } 22 | RmFileHandle* table_file_; 23 | std::vector redo_logs_; // 在该page上需要redo的操作的lsn 24 | }; 25 | 26 | class RecoveryManager { 27 | public: 28 | RecoveryManager(DiskManager* disk_manager, BufferPoolManager* buffer_pool_manager, SmManager* sm_manager) { 29 | disk_manager_ = disk_manager; 30 | buffer_pool_manager_ = buffer_pool_manager; 31 | sm_manager_ = sm_manager; 32 | } 33 | 34 | void analyze(); 35 | void redo(); 36 | void undo(); 37 | private: 38 | LogBuffer buffer_; // 读入日志 39 | DiskManager* disk_manager_; // 用来读写文件 40 | BufferPoolManager* buffer_pool_manager_; // 对页面进行读写 41 | SmManager* sm_manager_; // 访问数据库元数据 42 | }; -------------------------------------------------------------------------------- /src/replacer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES lru_replacer.cpp) 2 | add_library(lru_replacer STATIC ${SOURCES}) 3 | -------------------------------------------------------------------------------- /src/replacer/lru_replacer.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "lru_replacer.h" 12 | 13 | LRUReplacer::LRUReplacer(size_t num_pages) { max_size_ = num_pages; } 14 | 15 | LRUReplacer::~LRUReplacer() = default; 16 | 17 | /** 18 | * @description: 使用LRU策略删除一个victim frame,并返回该frame的id 19 | * @param {frame_id_t*} frame_id 被移除的frame的id,如果没有frame被移除返回nullptr 20 | * @return {bool} 如果成功淘汰了一个页面则返回true,否则返回false 21 | */ 22 | bool LRUReplacer::victim(frame_id_t* frame_id) { 23 | // C++17 std::scoped_lock 24 | // 它能够避免死锁发生,其构造函数能够自动进行上锁操作,析构函数会对互斥量进行解锁操作,保证线程安全。 25 | std::scoped_lock lock{latch_}; // 如果编译报错可以替换成其他lock 26 | 27 | // Todo: 28 | // 利用lru_replacer中的LRUlist_,LRUHash_实现LRU策略 29 | // 选择合适的frame指定为淘汰页面,赋值给*frame_id 30 | 31 | return true; 32 | } 33 | 34 | /** 35 | * @description: 固定指定的frame,即该页面无法被淘汰 36 | * @param {frame_id_t} 需要固定的frame的id 37 | */ 38 | void LRUReplacer::pin(frame_id_t frame_id) { 39 | std::scoped_lock lock{latch_}; 40 | // Todo: 41 | // 固定指定id的frame 42 | // 在数据结构中移除该frame 43 | } 44 | 45 | /** 46 | * @description: 取消固定一个frame,代表该页面可以被淘汰 47 | * @param {frame_id_t} frame_id 取消固定的frame的id 48 | */ 49 | void LRUReplacer::unpin(frame_id_t frame_id) { 50 | // Todo: 51 | // 支持并发锁 52 | // 选择一个frame取消固定 53 | } 54 | 55 | /** 56 | * @description: 获取当前replacer中可以被淘汰的页面数量 57 | */ 58 | size_t LRUReplacer::Size() { return LRUlist_.size(); } 59 | -------------------------------------------------------------------------------- /src/replacer/lru_replacer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "common/config.h" 18 | #include "replacer/replacer.h" 19 | #include "unordered_map" 20 | 21 | /* 22 | LRUReplacer实现了LRU替换策略 23 | */ 24 | class LRUReplacer : public Replacer { 25 | public: 26 | /** 27 | * @description: 创建一个新的LRUReplacer 28 | * @param {size_t} num_pages LRUReplacer最多需要存储的page数量 29 | */ 30 | explicit LRUReplacer(size_t num_pages); 31 | 32 | ~LRUReplacer(); 33 | 34 | bool victim(frame_id_t *frame_id); 35 | 36 | void pin(frame_id_t frame_id); 37 | 38 | void unpin(frame_id_t frame_id); 39 | 40 | size_t Size(); 41 | 42 | private: 43 | std::mutex latch_; // 互斥锁 44 | std::list LRUlist_; // 按加入的时间顺序存放unpinned pages的frame id,首部表示最近被访问 45 | std::unordered_map::iterator> LRUhash_; // frame_id_t -> unpinned pages的frame id 46 | size_t max_size_; // 最大容量(与缓冲池的容量相同) 47 | }; 48 | -------------------------------------------------------------------------------- /src/replacer/replacer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "common/config.h" 14 | 15 | /** 16 | * Replacer is an abstract class that tracks page usage. 17 | */ 18 | class Replacer { 19 | public: 20 | Replacer() = default; 21 | virtual ~Replacer() = default; 22 | 23 | /** 24 | * Remove the victim frame as defined by the replacement policy. 25 | * @param[out] frame_id id of frame that was removed, nullptr if no victim was found 26 | * @return true if a victim frame was found, false otherwise 27 | */ 28 | virtual bool victim(frame_id_t *frame_id) = 0; 29 | 30 | /** 31 | * Pins a frame, indicating that it should not be victimized until it is unpinned. 32 | * @param frame_id the id of the frame to pin 33 | */ 34 | virtual void pin(frame_id_t frame_id) = 0; 35 | 36 | /** 37 | * Unpins a frame, indicating that it can now be victimized. 38 | * @param frame_id the id of the frame to unpin 39 | */ 40 | virtual void unpin(frame_id_t frame_id) = 0; 41 | 42 | /** @return the number of elements in the replacer that can be victimized */ 43 | virtual size_t Size() = 0; 44 | }; 45 | -------------------------------------------------------------------------------- /src/storage/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES 2 | disk_manager.cpp 3 | buffer_pool_manager.cpp 4 | ../replacer/replacer.h 5 | ../replacer/lru_replacer.cpp 6 | ) 7 | add_library(storage STATIC ${SOURCES}) 8 | -------------------------------------------------------------------------------- /src/storage/buffer_pool_manager.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "buffer_pool_manager.h" 12 | 13 | /** 14 | * @description: 从free_list或replacer中得到可淘汰帧页的 *frame_id 15 | * @return {bool} true: 可替换帧查找成功 , false: 可替换帧查找失败 16 | * @param {frame_id_t*} frame_id 帧页id指针,返回成功找到的可替换帧id 17 | */ 18 | bool BufferPoolManager::find_victim_page(frame_id_t* frame_id) { 19 | // Todo: 20 | // 1 使用BufferPoolManager::free_list_判断缓冲池是否已满需要淘汰页面 21 | // 1.1 未满获得frame 22 | // 1.2 已满使用lru_replacer中的方法选择淘汰页面 23 | 24 | return false; 25 | } 26 | 27 | /** 28 | * @description: 更新页面数据, 如果为脏页则需写入磁盘,再更新为新页面,更新page元数据(data, is_dirty, page_id)和page table 29 | * @param {Page*} page 写回页指针 30 | * @param {PageId} new_page_id 新的page_id 31 | * @param {frame_id_t} new_frame_id 新的帧frame_id 32 | */ 33 | void BufferPoolManager::update_page(Page *page, PageId new_page_id, frame_id_t new_frame_id) { 34 | // Todo: 35 | // 1 如果是脏页,写回磁盘,并且把dirty置为false 36 | // 2 更新page table 37 | // 3 重置page的data,更新page id 38 | 39 | } 40 | 41 | /** 42 | * @description: 从buffer pool获取需要的页。 43 | * 如果页表中存在page_id(说明该page在缓冲池中),并且pin_count++。 44 | * 如果页表不存在page_id(说明该page在磁盘中),则找缓冲池victim page,将其替换为磁盘中读取的page,pin_count置1。 45 | * @return {Page*} 若获得了需要的页则将其返回,否则返回nullptr 46 | * @param {PageId} page_id 需要获取的页的PageId 47 | */ 48 | Page* BufferPoolManager::fetch_page(PageId page_id) { 49 | //Todo: 50 | // 1. 从page_table_中搜寻目标页 51 | // 1.1 若目标页有被page_table_记录,则将其所在frame固定(pin),并返回目标页。 52 | // 1.2 否则,尝试调用find_victim_page获得一个可用的frame,若失败则返回nullptr 53 | // 2. 若获得的可用frame存储的为dirty page,则须调用updata_page将page写回到磁盘 54 | // 3. 调用disk_manager_的read_page读取目标页到frame 55 | // 4. 固定目标页,更新pin_count_ 56 | // 5. 返回目标页 57 | return nullptr; 58 | } 59 | 60 | /** 61 | * @description: 取消固定pin_count>0的在缓冲池中的page 62 | * @return {bool} 如果目标页的pin_count<=0则返回false,否则返回true 63 | * @param {PageId} page_id 目标page的page_id 64 | * @param {bool} is_dirty 若目标page应该被标记为dirty则为true,否则为false 65 | */ 66 | bool BufferPoolManager::unpin_page(PageId page_id, bool is_dirty) { 67 | // Todo: 68 | // 0. lock latch 69 | // 1. 尝试在page_table_中搜寻page_id对应的页P 70 | // 1.1 P在页表中不存在 return false 71 | // 1.2 P在页表中存在,获取其pin_count_ 72 | // 2.1 若pin_count_已经等于0,则返回false 73 | // 2.2 若pin_count_大于0,则pin_count_自减一 74 | // 2.2.1 若自减后等于0,则调用replacer_的Unpin 75 | // 3 根据参数is_dirty,更改P的is_dirty_ 76 | return true; 77 | } 78 | 79 | /** 80 | * @description: 将目标页写回磁盘,不考虑当前页面是否正在被使用 81 | * @return {bool} 成功则返回true,否则返回false(只有page_table_中没有目标页时) 82 | * @param {PageId} page_id 目标页的page_id,不能为INVALID_PAGE_ID 83 | */ 84 | bool BufferPoolManager::flush_page(PageId page_id) { 85 | // Todo: 86 | // 0. lock latch 87 | // 1. 查找页表,尝试获取目标页P 88 | // 1.1 目标页P没有被page_table_记录 ,返回false 89 | // 2. 无论P是否为脏都将其写回磁盘。 90 | // 3. 更新P的is_dirty_ 91 | 92 | return true; 93 | } 94 | 95 | /** 96 | * @description: 创建一个新的page,即从磁盘中移动一个新建的空page到缓冲池某个位置。 97 | * @return {Page*} 返回新创建的page,若创建失败则返回nullptr 98 | * @param {PageId*} page_id 当成功创建一个新的page时存储其page_id 99 | */ 100 | Page* BufferPoolManager::new_page(PageId* page_id) { 101 | // 1. 获得一个可用的frame,若无法获得则返回nullptr 102 | // 2. 在fd对应的文件分配一个新的page_id 103 | // 3. 将frame的数据写回磁盘 104 | // 4. 固定frame,更新pin_count_ 105 | // 5. 返回获得的page 106 | return nullptr; 107 | } 108 | 109 | /** 110 | * @description: 从buffer_pool删除目标页 111 | * @return {bool} 如果目标页不存在于buffer_pool或者成功被删除则返回true,若其存在于buffer_pool但无法删除则返回false 112 | * @param {PageId} page_id 目标页 113 | */ 114 | bool BufferPoolManager::delete_page(PageId page_id) { 115 | // 1. 在page_table_中查找目标页,若不存在返回true 116 | // 2. 若目标页的pin_count不为0,则返回false 117 | // 3. 将目标页数据写回磁盘,从页表中删除目标页,重置其元数据,将其加入free_list_,返回true 118 | 119 | return true; 120 | } 121 | 122 | /** 123 | * @description: 将buffer_pool中的所有页写回到磁盘 124 | * @param {int} fd 文件句柄 125 | */ 126 | void BufferPoolManager::flush_all_pages(int fd) { 127 | 128 | } -------------------------------------------------------------------------------- /src/storage/buffer_pool_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "disk_manager.h" 21 | #include "errors.h" 22 | #include "page.h" 23 | #include "replacer/lru_replacer.h" 24 | #include "replacer/replacer.h" 25 | 26 | class BufferPoolManager { 27 | private: 28 | size_t pool_size_; // buffer_pool中可容纳页面的个数,即帧的个数 29 | Page *pages_; // buffer_pool中的Page对象数组,在构造空间中申请内存空间,在析构函数中释放,大小为BUFFER_POOL_SIZE 30 | std::unordered_map page_table_; // 帧号和页面号的映射哈希表,用于根据页面的PageId定位该页面的帧编号 31 | std::list free_list_; // 空闲帧编号的链表 32 | DiskManager *disk_manager_; 33 | Replacer *replacer_; // buffer_pool的置换策略,当前赛题中为LRU置换策略 34 | std::mutex latch_; // 用于共享数据结构的并发控制 35 | 36 | public: 37 | BufferPoolManager(size_t pool_size, DiskManager *disk_manager) 38 | : pool_size_(pool_size), disk_manager_(disk_manager) { 39 | // 为buffer pool分配一块连续的内存空间 40 | pages_ = new Page[pool_size_]; 41 | // 可以被Replacer改变 42 | if (REPLACER_TYPE.compare("LRU")) 43 | replacer_ = new LRUReplacer(pool_size_); 44 | else if (REPLACER_TYPE.compare("CLOCK")) 45 | replacer_ = new LRUReplacer(pool_size_); 46 | else { 47 | replacer_ = new LRUReplacer(pool_size_); 48 | } 49 | // 初始化时,所有的page都在free_list_中 50 | for (size_t i = 0; i < pool_size_; ++i) { 51 | free_list_.emplace_back(static_cast(i)); // static_cast转换数据类型 52 | } 53 | } 54 | 55 | ~BufferPoolManager() { 56 | delete[] pages_; 57 | delete replacer_; 58 | } 59 | 60 | /** 61 | * @description: 将目标页面标记为脏页 62 | * @param {Page*} page 脏页 63 | */ 64 | static void mark_dirty(Page* page) { page->is_dirty_ = true; } 65 | 66 | public: 67 | Page* fetch_page(PageId page_id); 68 | 69 | bool unpin_page(PageId page_id, bool is_dirty); 70 | 71 | bool flush_page(PageId page_id); 72 | 73 | Page* new_page(PageId* page_id); 74 | 75 | bool delete_page(PageId page_id); 76 | 77 | void flush_all_pages(int fd); 78 | 79 | private: 80 | bool find_victim_page(frame_id_t* frame_id); 81 | 82 | void update_page(Page* page, PageId new_page_id, frame_id_t new_frame_id); 83 | }; -------------------------------------------------------------------------------- /src/storage/disk_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "common/config.h" 24 | #include "errors.h" 25 | 26 | /** 27 | * @description: DiskManager的作用主要是根据上层的需要对磁盘文件进行操作 28 | */ 29 | class DiskManager { 30 | public: 31 | explicit DiskManager(); 32 | 33 | ~DiskManager() = default; 34 | 35 | void write_page(int fd, page_id_t page_no, const char *offset, int num_bytes); 36 | 37 | void read_page(int fd, page_id_t page_no, char *offset, int num_bytes); 38 | 39 | page_id_t allocate_page(int fd); 40 | 41 | void deallocate_page(page_id_t page_id); 42 | 43 | /*目录操作*/ 44 | bool is_dir(const std::string &path); 45 | 46 | void create_dir(const std::string &path); 47 | 48 | void destroy_dir(const std::string &path); 49 | 50 | /*文件操作*/ 51 | bool is_file(const std::string &path); 52 | 53 | void create_file(const std::string &path); 54 | 55 | void destroy_file(const std::string &path); 56 | 57 | int open_file(const std::string &path); 58 | 59 | void close_file(int fd); 60 | 61 | int get_file_size(const std::string &file_name); 62 | 63 | std::string get_file_name(int fd); 64 | 65 | int get_file_fd(const std::string &file_name); 66 | 67 | /*日志操作*/ 68 | int read_log(char *log_data, int size, int offset); 69 | 70 | void write_log(char *log_data, int size); 71 | 72 | void SetLogFd(int log_fd) { log_fd_ = log_fd; } 73 | 74 | int GetLogFd() { return log_fd_; } 75 | 76 | /** 77 | * @description: 设置文件已经分配的页面个数 78 | * @param {int} fd 文件对应的文件句柄 79 | * @param {int} start_page_no 已经分配的页面个数,即文件接下来从start_page_no开始分配页面编号 80 | */ 81 | void set_fd2pageno(int fd, int start_page_no) { fd2pageno_[fd] = start_page_no; } 82 | 83 | /** 84 | * @description: 获得文件目前已分配的页面个数,即如果文件要分配一个新页面,需要从fd2pagenp_[fd]开始分配 85 | * @return {page_id_t} 已分配的页面个数 86 | * @param {int} fd 文件对应的句柄 87 | */ 88 | page_id_t get_fd2pageno(int fd) { return fd2pageno_[fd]; } 89 | 90 | static constexpr int MAX_FD = 8192; 91 | 92 | private: 93 | // 文件打开列表,用于记录文件是否被打开 94 | std::unordered_map path2fd_; //哈希表 95 | std::unordered_map fd2path_; //哈希表 96 | 97 | int log_fd_ = -1; // WAL日志文件的文件句柄,默认为-1,代表未打开日志文件 98 | std::atomic fd2pageno_[MAX_FD]{}; // 文件中已经分配的页面个数,初始值为0 99 | }; -------------------------------------------------------------------------------- /src/storage/page.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "common/config.h" 14 | 15 | /** 16 | * @description: 存储层每个Page的id的声明 17 | */ 18 | struct PageId { 19 | int fd; // Page所在的磁盘文件开启后的文件描述符, 来定位打开的文件在内存中的位置 20 | page_id_t page_no = INVALID_PAGE_ID; 21 | 22 | friend bool operator==(const PageId &x, const PageId &y) { return x.fd == y.fd && x.page_no == y.page_no; } 23 | bool operator<(const PageId& x) const { 24 | if(fd < x.fd) return true; 25 | return page_no < x.page_no; 26 | } 27 | 28 | std::string toString() { 29 | return "{fd: " + std::to_string(fd) + " page_no: " + std::to_string(page_no) + "}"; 30 | } 31 | 32 | inline int64_t Get() const { 33 | return (static_cast(fd << 16) | page_no); 34 | } 35 | }; 36 | 37 | // PageId的自定义哈希算法, 用于构建unordered_map 38 | struct PageIdHash { 39 | size_t operator()(const PageId &x) const { return (x.fd << 16) | x.page_no; } 40 | }; 41 | 42 | template <> 43 | struct std::hash { 44 | size_t operator()(const PageId &obj) const { return std::hash()(obj.Get()); } 45 | }; 46 | 47 | /** 48 | * @description: Page类声明, Page是RMDB数据块的单位、是负责数据操作Record模块的操作对象, 49 | * Page对象在磁盘上有文件存储, 若在Buffer中则有帧偏移, 并非特指Buffer或Disk上的数据 50 | */ 51 | class Page { 52 | friend class BufferPoolManager; 53 | 54 | public: 55 | 56 | Page() { reset_memory(); } 57 | 58 | ~Page() = default; 59 | 60 | PageId get_page_id() const { return id_; } 61 | 62 | inline char *get_data() { return data_; } 63 | 64 | bool is_dirty() const { return is_dirty_; } 65 | 66 | static constexpr size_t OFFSET_PAGE_START = 0; 67 | static constexpr size_t OFFSET_LSN = 0; 68 | static constexpr size_t OFFSET_PAGE_HDR = 4; 69 | 70 | inline lsn_t get_page_lsn() { return *reinterpret_cast(get_data() + OFFSET_LSN) ; } 71 | 72 | inline void set_page_lsn(lsn_t page_lsn) { memcpy(get_data() + OFFSET_LSN, &page_lsn, sizeof(lsn_t)); } 73 | 74 | private: 75 | void reset_memory() { memset(data_, OFFSET_PAGE_START, PAGE_SIZE); } // 将data_的PAGE_SIZE个字节填充为0 76 | 77 | /** page的唯一标识符 */ 78 | PageId id_; 79 | 80 | /** The actual data that is stored within a page. 81 | * 该页面在bufferPool中的偏移地址 82 | */ 83 | char data_[PAGE_SIZE] = {}; 84 | 85 | /** 脏页判断 */ 86 | bool is_dirty_ = false; 87 | 88 | /** The pin count of this page. */ 89 | int pin_count_ = 0; 90 | }; -------------------------------------------------------------------------------- /src/system/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES sm_manager.cpp) 2 | add_library(system STATIC ${SOURCES}) 3 | target_link_libraries(system index record) 4 | -------------------------------------------------------------------------------- /src/system/sm.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "sm_manager.h" 14 | #include "sm_meta.h" 15 | #include "sm_defs.h" 16 | -------------------------------------------------------------------------------- /src/system/sm_defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | #pragma once 11 | 12 | #include "defs.h" 13 | #include 14 | -------------------------------------------------------------------------------- /src/system/sm_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include "index/ix.h" 14 | #include "record/rm_file_handle.h" 15 | #include "sm_defs.h" 16 | #include "sm_meta.h" 17 | #include "common/context.h" 18 | 19 | class Context; 20 | 21 | struct ColDef { 22 | std::string name; // Column name 23 | ColType type; // Type of column 24 | int len; // Length of column 25 | }; 26 | 27 | /* 系统管理器,负责元数据管理和DDL语句的执行 */ 28 | class SmManager { 29 | public: 30 | DbMeta db_; // 当前打开的数据库的元数据 31 | std::unordered_map> fhs_; // file name -> record file handle, 当前数据库中每张表的数据文件 32 | std::unordered_map> ihs_; // file name -> index file handle, 当前数据库中每个索引的文件 33 | private: 34 | DiskManager* disk_manager_; 35 | BufferPoolManager* buffer_pool_manager_; 36 | RmManager* rm_manager_; 37 | IxManager* ix_manager_; 38 | 39 | public: 40 | SmManager(DiskManager* disk_manager, BufferPoolManager* buffer_pool_manager, RmManager* rm_manager, 41 | IxManager* ix_manager) 42 | : disk_manager_(disk_manager), 43 | buffer_pool_manager_(buffer_pool_manager), 44 | rm_manager_(rm_manager), 45 | ix_manager_(ix_manager) {} 46 | 47 | ~SmManager() {} 48 | 49 | BufferPoolManager* get_bpm() { return buffer_pool_manager_; } 50 | 51 | RmManager* get_rm_manager() { return rm_manager_; } 52 | 53 | IxManager* get_ix_manager() { return ix_manager_; } 54 | 55 | bool is_dir(const std::string& db_name); 56 | 57 | void create_db(const std::string& db_name); 58 | 59 | void drop_db(const std::string& db_name); 60 | 61 | void open_db(const std::string& db_name); 62 | 63 | void close_db(); 64 | 65 | void flush_meta(); 66 | 67 | void show_tables(Context* context); 68 | 69 | void desc_table(const std::string& tab_name, Context* context); 70 | 71 | void create_table(const std::string& tab_name, const std::vector& col_defs, Context* context); 72 | 73 | void drop_table(const std::string& tab_name, Context* context); 74 | 75 | void create_index(const std::string& tab_name, const std::vector& col_names, Context* context); 76 | 77 | void drop_index(const std::string& tab_name, const std::vector& col_names, Context* context); 78 | 79 | void drop_index(const std::string& tab_name, const std::vector& col_names, Context* context); 80 | }; -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # storage test 2 | add_executable(disk_manager_test storage/disk_manager_test.cpp) 3 | target_link_libraries(disk_manager_test storage gtest_main) 4 | 5 | add_executable(lru_replacer_test storage/lru_replacer_test.cpp) 6 | target_link_libraries(lru_replacer_test lru_replacer gtest_main) 7 | 8 | add_executable(buffer_pool_manager_test storage/buffer_pool_manager_test.cpp) 9 | target_link_libraries(buffer_pool_manager_test storage gtest_main) 10 | 11 | add_executable(record_manager_test storage/record_manager_test.cpp) 12 | target_link_libraries(record_manager_test record gtest_main) 13 | 14 | # index test 15 | add_executable(b_plus_tree_insert_test index/b_plus_tree_insert_test.cpp) 16 | target_link_libraries(b_plus_tree_insert_test system index gtest_main) 17 | 18 | add_executable(b_plus_tree_delete_test index/b_plus_tree_delete_test.cpp) 19 | target_link_libraries(b_plus_tree_delete_test system index gtest_main) 20 | 21 | add_executable(b_plus_tree_concurrent_test index/b_plus_tree_concurrent_test.cpp) 22 | target_link_libraries(b_plus_tree_concurrent_test system index gtest_main) 23 | 24 | # query test 25 | add_executable(query_test query/query_test.cpp) 26 | 27 | # transaction test 28 | add_executable(transaction_test transaction/transaction_test.cpp) 29 | target_link_libraries(transaction_test readline) 30 | 31 | # regress test 32 | add_executable(regress_test regress/regress_test_main.cpp regress/regress_test.cpp) 33 | 34 | # concurrency test 35 | add_executable(concurrency_test concurrency/concurrency_test_main.cpp concurrency/concurrency_test.cpp regress/regress_test.cpp) 36 | 37 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/concurrency_read_test.sql: -------------------------------------------------------------------------------- 1 | preload 4 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (3, 'zhanghua', 88.5); 6 | 7 | txn1 4 8 | t1a begin; 9 | t1b select * from concurrency_test; 10 | t1c select * from concurrency_test where id = 2; 11 | t1d commit; 12 | 13 | txn2 4 14 | t2a begin; 15 | t2b select * from concurrency_test; 16 | t2c select * from concurrency_test where id = 3; 17 | t2d commit; 18 | 19 | permutation 8 20 | t1a 21 | t2a 22 | t1b 23 | t2b 24 | t2c 25 | t1c 26 | t1d 27 | t2d -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/concurrency_read_test_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 1 | xiaohong | 90.000000 | 5 | | 2 | xiaoming | 95.000000 | 6 | | 3 | zhanghua | 88.500000 | 7 | +------------------+------------------+------------------+ 8 | Total record(s): 3 9 | +------------------+------------------+------------------+ 10 | | id | name | score | 11 | +------------------+------------------+------------------+ 12 | | 1 | xiaohong | 90.000000 | 13 | | 2 | xiaoming | 95.000000 | 14 | | 3 | zhanghua | 88.500000 | 15 | +------------------+------------------+------------------+ 16 | Total record(s): 3 17 | +------------------+------------------+------------------+ 18 | | id | name | score | 19 | +------------------+------------------+------------------+ 20 | | 3 | zhanghua | 88.500000 | 21 | +------------------+------------------+------------------+ 22 | Total record(s): 1 23 | +------------------+------------------+------------------+ 24 | | id | name | score | 25 | +------------------+------------------+------------------+ 26 | | 2 | xiaoming | 95.000000 | 27 | +------------------+------------------+------------------+ 28 | Total record(s): 1 29 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/concurrency_test.sql: -------------------------------------------------------------------------------- 1 | -- 题目九:事务控制语句 2 | -- 测试点1: 并发读 3 | create table concurrency_test (id int, name char(8), score float); 4 | insert into concurrency_test values (1, 'xiaohong', 90.0); 5 | insert into concurrency_test values (2, 'xiaoming', 95.0); 6 | insert into concurrency_test values (3, 'zhanghua', 88.5); 7 | -- thread1: 8 | begin; 9 | select * from concurrency_test; 10 | commit; 11 | -- thread2: 12 | begin; 13 | select * from concurrency_test; 14 | commit; 15 | 16 | -- 测试点2: 脏写数据异常 17 | -- W1(x)W2(x)C1C2 18 | create table concurrency_test (id int, name char(8), score float); 19 | insert into concurrency_test values (1, 'xiaohong', 90.0); 20 | insert into concurrency_test values (2, 'xiaoming', 95.0); 21 | insert into concurrency_test values (3, 'zhanghua', 88.5); 22 | -- t1: 23 | begin; 24 | -- t2: 25 | begin; 26 | -- t1 27 | select * from concurrency_test where id = 1; 28 | update concurrency_test set score = 100.0 where id = 1; 29 | -- t2 30 | update concurrency_test set score = 60.0 where id = 1; 31 | -- 由于是no-wait,所以应该abort t2 32 | -- t1 33 | commit; 34 | select * from concurrency_test; 35 | 36 | -- 测试点3: 脏读数据异常 37 | -- W1(x)R2(x)A1 38 | create table concurrency_test (id int, name char(8), score float); 39 | insert into concurrency_test values (1, 'xiaohong', 90.0); 40 | insert into concurrency_test values (2, 'xiaoming', 95.0); 41 | insert into concurrency_test values (3, 'zhanghua', 88.5); 42 | -- t1 43 | begin; 44 | -- t2 45 | begin; 46 | -- t1 47 | select * from concurrency_test where id = 1; 48 | update concurrency_test set score = 100.0 where id = 1; 49 | -- t2 50 | select * from concurrency_test where id = 1; 51 | -- 由于是no-wait,所以应该abort t2 52 | -- t1 53 | commit; 54 | select * from concurrency_test; 55 | 56 | -- 测试点4: 丢失修改数据异常 57 | -- R1(x)R2(x)W1(x)C1W2(x)C2 58 | create table concurrency_test (id int, name char(8), score float); 59 | insert into concurrency_test values (1, 'xiaohong', 90.0); 60 | insert into concurrency_test values (2, 'xiaoming', 95.0); 61 | insert into concurrency_test values (3, 'zhanghua', 88.5); 62 | -- t1 63 | begin; 64 | -- t2; 65 | begin; 66 | select * from concurrency_test where id = 1; 67 | -- t1 68 | select * from concurrency_test where id = 1; 69 | update concurrency_test set score = 100.0 where id = 1; 70 | -- 由于是no-wait,所以应该abort t1 71 | -- t2 72 | update concurrency_test set score = 75.5 where id = 1; 73 | commit; 74 | select * from concurrency_test; 75 | 76 | -- 测试点5: 不可重复读数据异常 77 | -- R1(x)W2(x)C2R1(x) 78 | create table concurrency_test (id int, name char(8), score float); 79 | insert into concurrency_test values (1, 'xiaohong', 90.0); 80 | insert into concurrency_test values (2, 'xiaoming', 95.0); 81 | insert into concurrency_test values (3, 'zhanghua', 88.5); 82 | -- t1 83 | begin; 84 | -- t2 85 | begin; 86 | -- t1 87 | select * from concurrency_test where id = 2; 88 | -- t2 89 | update concurrency_test set score = 100.0 where id = 2; 90 | -- 由于是no-wait,所以应该abort t2 91 | -- t1 92 | select * from concurrency_test where id = 2; 93 | commit; 94 | select * from concurrency_test; 95 | 96 | -- 测试点6: 幻读数据异常 97 | create table concurrency_test (id int, name char(8), score float); 98 | insert into concurrency_test values (1, 'xiaohong', 90.0); 99 | insert into concurrency_test values (2, 'xiaoming', 95.0); 100 | insert into concurrency_test values (4, 'zhanghua', 88.5); 101 | create index concurrency_test (id); 102 | -- t1 103 | begin; 104 | select * from concurrency_test; 105 | -- t2 106 | begin; 107 | insert into concurrency_test values (5, 'xiaoyang', 100.0); 108 | -- 如果是加表级锁,根据no-wait规则,应该abort t2 109 | -- 如果是加间隙锁,这里应该是什么操作 110 | -- t1 111 | select * from concurrency_test; 112 | commit; 113 | -- t2,如果t2没有abort就应该执行下面这条语句,否则就不执行 114 | abort; 115 | -- t3 116 | begin; 117 | select * from concurrency_test; 118 | -- t4 119 | begin; 120 | insert into concurrency_test value (3, 'xiaoyang', 100.0); 121 | -- 如果是加表级锁,根据no-wait规则,应该abort t2 122 | -- 如果是加间隙锁,这里应该是什么操作 123 | -- t3 124 | select * from concurrency_test; 125 | commit; 126 | -- t4 如果t4没有abort则执行下面的操作,否则不执行 127 | abort; 128 | select * from concurrency_test; 129 | 130 | -- 由于不确定到底采用什么样的策略去处理间隙锁的死锁问题,因此可以只对t1和t3的select结果做检查 131 | -- mysql是采用死锁检测或者超时回滚的方法来处理间隙锁带来的死锁 132 | 133 | preload 3 134 | create ...; 135 | insert ...; 136 | insert ...; 137 | 138 | txn1 3 139 | t1a begin; 140 | t1b select ...; 141 | t1c commit; 142 | 143 | txn2 3 144 | t2a begin; 145 | t2b select ...; 146 | t2c commit; 147 | 148 | permutation 6 149 | t1a 150 | t2a 151 | t1b 152 | t2b 153 | t1c 154 | t2c 155 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/dirty_read_test.sql: -------------------------------------------------------------------------------- 1 | preload 4 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (3, 'zhanghua', 88.5); 6 | 7 | txn1 4 8 | t1a begin; 9 | t1b update concurrency_test set score = 100.0 where id = 2; 10 | t1c abort; 11 | t1d select * from concurrency_test where id = 2; 12 | 13 | txn2 4 14 | t2a begin; 15 | t2b select * from concurrency_test where id = 2; 16 | t2c commit; 17 | 18 | permutation 6 19 | t1a 20 | t2a 21 | t1b 22 | t2b 23 | t1c 24 | t1d -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/dirty_read_test_output.txt: -------------------------------------------------------------------------------- 1 | abort 2 | +------------------+------------------+------------------+ 3 | | id | name | score | 4 | +------------------+------------------+------------------+ 5 | | 2 | xiaoming | 95.000000 | 6 | +------------------+------------------+------------------+ 7 | Total record(s): 1 -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/dirty_write_test.sql: -------------------------------------------------------------------------------- 1 | preload 4 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (3, 'zhanghua', 88.5); 6 | 7 | txn1 5 8 | t1a begin; 9 | t1b select * from concurrency_test where id = 1; 10 | t1c update concurrency_test set score = 100.0 where id = 1; 11 | t1d commit; 12 | t1e select * from concurrency_test where id = 1; 13 | 14 | txn2 3 15 | t2a begin; 16 | t2b update concurrency_test set score = 60.0 where id = 1; 17 | t2c commit; 18 | 19 | permutation 7 20 | t1a 21 | t2a 22 | t1b 23 | t1c 24 | t2b 25 | t1d 26 | t1e 27 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/dirty_write_test_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 1 | xiaohong | 90.000000 | 5 | +------------------+------------------+------------------+ 6 | Total record(s): 1 7 | abort 8 | +------------------+------------------+------------------+ 9 | | id | name | score | 10 | +------------------+------------------+------------------+ 11 | | 1 | xiaohong | 100.000000 | 12 | +------------------+------------------+------------------+ 13 | Total record(s): 1 14 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/lost_update_test.sql: -------------------------------------------------------------------------------- 1 | preload 4 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (3, 'zhanghua', 88.5); 6 | 7 | txn1 4 8 | t1a begin; 9 | t1b select * from concurrency_test where id = 2; 10 | t1c update concurrency_test set score = 100.0 where id = 2; 11 | t1d commit; 12 | 13 | txn2 5 14 | t2a begin; 15 | t2b select * from concurrency_test where id = 2; 16 | t2c update concurrency_test set score = 75.5 where id = 2; 17 | t2d commit; 18 | t2e select * from concurrency_test where id = 2; 19 | 20 | permutation 8 21 | t1a 22 | t2a 23 | t2b 24 | t1b 25 | t1c 26 | t2c 27 | t2d 28 | t2e -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/lost_update_test_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 2 | xiaoming | 95.000000 | 5 | +------------------+------------------+------------------+ 6 | Total record(s): 1 7 | +------------------+------------------+------------------+ 8 | | id | name | score | 9 | +------------------+------------------+------------------+ 10 | | 2 | xiaoming | 95.000000 | 11 | +------------------+------------------+------------------+ 12 | Total record(s): 1 13 | abort 14 | +------------------+------------------+------------------+ 15 | | id | name | score | 16 | +------------------+------------------+------------------+ 17 | | 2 | xiaoming | 75.500000 | 18 | +------------------+------------------+------------------+ 19 | Total record(s): 1 -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_1.sql: -------------------------------------------------------------------------------- 1 | preload 5 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (4, 'zhanghua', 88.5); 6 | create index concurrency_test (id); 7 | 8 | txn1 4 9 | t1a begin; 10 | t1b select * from concurrency_test; 11 | t1c select * from concurrency_test; 12 | t1d commit; 13 | 14 | txn2 3 15 | t2a begin; 16 | t2b insert into concurrency_test values (3, 'xiaoyang', 100.0); 17 | t2c abort; 18 | 19 | permutation 6 20 | t1a 21 | t2a 22 | t1b 23 | t2b 24 | t1c 25 | t1d -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_1_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 1 | xiaohong | 90.000000 | 5 | | 2 | xiaoming | 95.000000 | 6 | | 4 | zhanghua | 88.500000 | 7 | +------------------+------------------+------------------+ 8 | Total record(s): 3 9 | abort 10 | +------------------+------------------+------------------+ 11 | | id | name | score | 12 | +------------------+------------------+------------------+ 13 | | 1 | xiaohong | 90.000000 | 14 | | 2 | xiaoming | 95.000000 | 15 | | 4 | zhanghua | 88.500000 | 16 | +------------------+------------------+------------------+ 17 | Total record(s): 3 -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_2.sql: -------------------------------------------------------------------------------- 1 | preload 5 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (4, 'zhanghua', 88.5); 6 | create index concurrency_test (id); 7 | 8 | txn1 4 9 | t1a begin; 10 | t1b select * from concurrency_test; 11 | t1c select * from concurrency_test; 12 | t1d commit; 13 | 14 | txn2 3 15 | t2a begin; 16 | t2b insert into concurrency_test values (5, 'xiaoyang', 100.0); 17 | t2c abort; 18 | 19 | permutation 6 20 | t1a 21 | t2a 22 | t1b 23 | t2b 24 | t1c 25 | t1d -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_2_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 1 | xiaohong | 90.000000 | 5 | | 2 | xiaoming | 95.000000 | 6 | | 4 | zhanghua | 88.500000 | 7 | +------------------+------------------+------------------+ 8 | Total record(s): 3 9 | abort 10 | +------------------+------------------+------------------+ 11 | | id | name | score | 12 | +------------------+------------------+------------------+ 13 | | 1 | xiaohong | 90.000000 | 14 | | 2 | xiaoming | 95.000000 | 15 | | 4 | zhanghua | 88.500000 | 16 | +------------------+------------------+------------------+ 17 | Total record(s): 3 -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_3.sql: -------------------------------------------------------------------------------- 1 | preload 5 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (4, 'zhanghua', 88.5); 6 | create index concurrency_test (id); 7 | 8 | txn1 4 9 | t1a begin; 10 | t1b select * from concurrency_test where id > 2 and id < 4; 11 | t1c select * from concurrency_test where id > 2 and id < 4; 12 | t1d commit; 13 | 14 | txn2 3 15 | t2a begin; 16 | t2b insert into concurrency_test values (3, 'xiaoyang', 100.0); 17 | t2c abort; 18 | 19 | permutation 6 20 | t1a 21 | t2a 22 | t1b 23 | t2b 24 | t1c 25 | t1d -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_3_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | +------------------+------------------+------------------+ 5 | Total record(s): 0 6 | abort 7 | +------------------+------------------+------------------+ 8 | | id | name | score | 9 | +------------------+------------------+------------------+ 10 | +------------------+------------------+------------------+ 11 | Total record(s): 0 -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_4.sql: -------------------------------------------------------------------------------- 1 | preload 10 2 | create table concurrency_test (id int, name char(8), score float); 3 | create index concurrency_test (id); 4 | insert into concurrency_test values (1, 'xiaohong', 90.0); 5 | insert into concurrency_test values (2, 'xiaoming', 95.0); 6 | insert into concurrency_test values (4, 'zhanghua', 88.5); 7 | insert into concurrency_test values (7, 'xiaoyang', 91.0); 8 | insert into concurrency_test values (10, 'wangming', 92.0); 9 | insert into concurrency_test values (8, 'wanghong', 93.0); 10 | insert into concurrency_test values (100, 'zhaoming', 94.0); 11 | insert into concurrency_test values (201, 'zhaohong', 95.0); 12 | 13 | txn1 6 14 | t1a begin; 15 | t1b select * from concurrency_test where id > 2 and id < 10; 16 | t1c select * from concurrency_test where id > 4 and id < 20; 17 | t1d select * from concurrency_test where id > 9 and id < 200; 18 | t1e select * from concurrency_test where id > 9 and id < 100; 19 | t1f commit; 20 | 21 | txn2 3 22 | t2a begin; 23 | t2b delete from concurrency_test where id = 7; 24 | t2c abort; 25 | 26 | txn3 3 27 | t3a begin; 28 | t3b insert into concurrency_test values (11, 'zhaoyang', 99.0); 29 | t3c abort; 30 | 31 | txn4 3 32 | t4a begin; 33 | t4b update concurrency_test set id = 13 where name = 'wanghong'; 34 | t4c abort; 35 | 36 | permutation 12 37 | t1a 38 | t2a 39 | t3a 40 | t4a 41 | t1b 42 | t2b 43 | t1c 44 | t3b 45 | t1d 46 | t4b 47 | t1e 48 | t1f -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/phantom_read_test_4_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 4 | zhanghua | 88.500000 | 5 | | 7 | xiaoyang | 91.000000 | 6 | | 8 | wanghong | 93.000000 | 7 | +------------------+------------------+------------------+ 8 | Total record(s): 3 9 | abort 10 | +------------------+------------------+------------------+ 11 | | id | name | score | 12 | +------------------+------------------+------------------+ 13 | | 7 | xiaoyang | 91.000000 | 14 | | 8 | wanghong | 93.000000 | 15 | | 10 | wangming | 92.000000 | 16 | +------------------+------------------+------------------+ 17 | Total record(s): 3 18 | abort 19 | +------------------+------------------+------------------+ 20 | | id | name | score | 21 | +------------------+------------------+------------------+ 22 | | 10 | wangming | 92.000000 | 23 | | 100 | zhaoming | 94.000000 | 24 | +------------------+------------------+------------------+ 25 | Total record(s): 2 26 | abort 27 | +------------------+------------------+------------------+ 28 | | id | name | score | 29 | +------------------+------------------+------------------+ 30 | | 10 | wangming | 92.000000 | 31 | +------------------+------------------+------------------+ 32 | Total record(s): 1 33 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/unrepeatable_read_test.sql: -------------------------------------------------------------------------------- 1 | preload 4 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (3, 'zhanghua', 88.5); 6 | 7 | txn1 4 8 | t1a begin; 9 | t1b select * from concurrency_test where id = 2; 10 | t1c select * from concurrency_test where id = 2; 11 | t1d commit; 12 | 13 | txn2 3 14 | t2a begin; 15 | t2b update concurrency_test set score = 100.0 where id = 2; 16 | t2c commit; 17 | 18 | permutation 6 19 | t1a 20 | t2a 21 | t1b 22 | t2b 23 | t1c 24 | t1d 25 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/unrepeatable_read_test_hard.sql: -------------------------------------------------------------------------------- 1 | preload 4 2 | create table concurrency_test (id int, name char(8), score float); 3 | insert into concurrency_test values (1, 'xiaohong', 90.0); 4 | insert into concurrency_test values (2, 'xiaoming', 95.0); 5 | insert into concurrency_test values (4, 'zhanghua', 88.5); 6 | 7 | txn1 4 8 | t1a begin; 9 | t1b select * from concurrency_test where name = 'xiaohong'; 10 | t1c select * from concurrency_test where name = 'xiaohong'; 11 | t1d commit; 12 | 13 | txn2 3 14 | t2a begin; 15 | t2b update concurrency_test set name = 'xiaohong' where id = 2; 16 | t2c abort; 17 | 18 | permutation 6 19 | t1a 20 | t2a 21 | t1b 22 | t2b 23 | t1c 24 | t1d 25 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/unrepeatable_read_test_hard_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 1 | xiaohong | 90.000000 | 5 | +------------------+------------------+------------------+ 6 | Total record(s): 1 7 | abort 8 | +------------------+------------------+------------------+ 9 | | id | name | score | 10 | +------------------+------------------+------------------+ 11 | | 1 | xiaohong | 90.000000 | 12 | +------------------+------------------+------------------+ 13 | Total record(s): 1 14 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_sql/unrepeatable_read_test_output.txt: -------------------------------------------------------------------------------- 1 | +------------------+------------------+------------------+ 2 | | id | name | score | 3 | +------------------+------------------+------------------+ 4 | | 2 | xiaoming | 95.000000 | 5 | +------------------+------------------+------------------+ 6 | Total record(s): 1 7 | abort 8 | +------------------+------------------+------------------+ 9 | | id | name | score | 10 | +------------------+------------------+------------------+ 11 | | 2 | xiaoming | 95.000000 | 12 | +------------------+------------------+------------------+ 13 | Total record(s): 1 -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_test.cpp: -------------------------------------------------------------------------------- 1 | #include "concurrency_test.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void TestCaseAnalyzer::analyze_operation(Operation* operation, const std::string& operation_line) { 9 | operation->name = operation_line.substr(0, operation_line.find(" ")); 10 | operation->sql = operation_line.substr(operation_line.find(" ") + 1); 11 | } 12 | 13 | void TestCaseAnalyzer::analyze_test_case() { 14 | std::string line; 15 | 16 | infile.open(infile_path); 17 | 18 | Operation* crash_operation = new Operation(); 19 | crash_operation->sql = "crash"; 20 | crash_operation->txn_id = -1; 21 | 22 | while(std::getline(infile, line)) { 23 | if(line.find("preload") != std::string::npos) { 24 | int count = atoi(line.substr(line.find(" ") + 1).c_str()); 25 | while(count) { 26 | --count; 27 | std::getline(infile, line); 28 | preload.push_back(line); 29 | } 30 | } 31 | else if(line.find("txn") != std::string::npos) { 32 | Transaction* txn = new Transaction(); 33 | 34 | transactions.push_back(txn); 35 | txn->txn_id = transactions.size() - 1; 36 | 37 | int count = atoi(line.substr(line.find(" ") + 1).c_str()); 38 | while(count) { 39 | --count; 40 | std::getline(infile, line); 41 | Operation* operation = new Operation(); 42 | txn->operations.push_back(operation); 43 | analyze_operation(operation, line); 44 | operation->txn_id = txn->txn_id; 45 | operation_map[operation->name] = operation; 46 | } 47 | } 48 | else if(line.find("permutation") != std::string::npos) { 49 | int count = atoi(line.substr(line.find(" ") + 1).c_str()); 50 | while(count) { 51 | --count; 52 | std::getline(infile, line); 53 | if(strcmp(line.c_str(), "crash") == 0) { 54 | // permutation->operations.push_back(crash_operation); 55 | break; 56 | } 57 | else { 58 | permutation->operations.push_back(operation_map[line]); 59 | } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_test.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | class Operation { 7 | public: 8 | std::string name; // 比如t1a, t1b, t2a 9 | std::string sql; 10 | int txn_id; 11 | }; 12 | 13 | class OperationPermutation { 14 | public: 15 | std::vector operations; 16 | }; 17 | 18 | class Transaction { 19 | public: 20 | std::vector operations; 21 | int txn_id; 22 | int sockfd; 23 | }; 24 | 25 | class TestCaseAnalyzer { 26 | public: 27 | TestCaseAnalyzer() { 28 | permutation = new OperationPermutation(); 29 | } 30 | void analyze_operation(Operation* operation, const std::string& operation_line); 31 | void analyze_test_case(); 32 | 33 | OperationPermutation* permutation; 34 | std::vector transactions; 35 | std::vector preload; 36 | std::string infile_path; 37 | std::fstream infile; 38 | std::unordered_map operation_map; 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_test.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import time; 3 | 4 | NUM_TESTS = 10 5 | 6 | TESTS = ["concurrency_read_test", 7 | "dirty_write_test", 8 | "dirty_read_test", 9 | "lost_update_test", 10 | "unrepeatable_read_test", 11 | "unrepeatable_read_test_hard"] 12 | 13 | CHECK_METHOD = ["dict_match", 14 | "dict_match", 15 | "dict_match", 16 | "dict_match", 17 | "dict_match", 18 | "dict_match"] 19 | 20 | 21 | FAILED_TESTS = [] 22 | 23 | def get_test_name(test_name): 24 | return "../src/test/concurrency/concurrency_sql/" + str(test_name) + ".sql" 25 | 26 | def get_output_name(test_name): 27 | return "../src/test/concurrency/concurrency_sql/" + str(test_name) + "_output.txt" 28 | 29 | def build(): 30 | # root 31 | os.chdir("../../../") 32 | if os.path.exists("./build"): 33 | os.system("rm -rf build") 34 | os.mkdir("./build") 35 | os.chdir("./build") 36 | os.system("cmake ..") 37 | os.system("make rmdb -j4") 38 | os.system("make concurrency_test -j4") 39 | os.chdir("..") 40 | 41 | def run(): 42 | os.chdir("./build") 43 | score = 0.0 44 | index = -1 45 | comment_str = "\n" 46 | 47 | for test_case in TESTS: 48 | print("-----------Concurrency Testing " + test_case + "...-----------") 49 | test_file = get_test_name(test_case) 50 | database_name = "concurrency_test_db" 51 | 52 | if os.path.exists(database_name): 53 | os.system("rm -rf " + database_name) 54 | 55 | os.system("./bin/rmdb " + database_name + " &") 56 | # ./bin/concurrency ../src/test/concurrency_test/xxx.sql 57 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 58 | time.sleep(3) 59 | os.system("./bin/concurrency_test " + test_file + " " + database_name + "/client_output.txt") 60 | 61 | # check result 62 | if CHECK_METHOD[index] == "dict_match": 63 | ansDict = {} 64 | standard_answer = get_output_name(test_case) 65 | hand0 = open(standard_answer, "r") 66 | for line in hand0: 67 | line = line.strip('\n') 68 | if line == "": 69 | continue 70 | num = ansDict.setdefault(line, 0) 71 | ansDict[line] = num + 1 72 | my_answer = database_name + "/client_output.txt" 73 | hand1 = open(my_answer, "r") 74 | for line in hand1: 75 | line = line.strip('\n') 76 | if line == "": 77 | continue 78 | num = ansDict.setdefault(line, 0) 79 | ansDict[line] = num - 1 80 | match = True 81 | for key, value in ansDict.items(): 82 | if value != 0: 83 | match = False 84 | comment_str += "In " + test_case + ", your answer mismatches standard answer.\n" 85 | break 86 | if match: 87 | score += 10 88 | else: 89 | FAILED_TESTS.append(test_case) 90 | else: 91 | res = os.system("diff " + database_name + "/client_output.txt " + get_output_name(test_case) + " -w") 92 | print("diff result: " + str(res)) 93 | # calculate the score 94 | if res == 0: 95 | score += 10 96 | else: 97 | FAILED_TESTS.append(test_case) 98 | comment_str += "In " + test_case + ", your answer mismatches standard answer.\n" 99 | 100 | # close server 101 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 102 | print("finish kill") 103 | # delete database 104 | # os.system("rm -rf ./" + database_name) 105 | # print("finish delete database") 106 | 107 | os.chdir("../../") 108 | 109 | 110 | if(len(FAILED_TESTS) != 0): 111 | print("\033[0;31;40mConcurrency Test Final Score:" + str(score)+ "\033[0m") 112 | print("Your program fails the following test cases: ") 113 | for failed_test in FAILED_TESTS: 114 | print("[" + failed_test + "] "), 115 | else: 116 | print("You have passed all basic test cases about concurrency control.") 117 | print("\033[0;31;40mConcurrency Test Final Score:" + str(score)+ "\033[0m") 118 | 119 | print(comment_str) 120 | 121 | if __name__ == "__main__": 122 | build() 123 | run() 124 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_test_bonus.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import time; 3 | 4 | NUM_TESTS = 10 5 | 6 | TESTS = ["phantom_read_test_1", 7 | "phantom_read_test_2", 8 | "phantom_read_test_3", 9 | "phantom_read_test_4"] 10 | 11 | CHECK_METHOD = ["diff_match", 12 | "diff_match", 13 | "diff_match", 14 | "diff_match"] 15 | 16 | 17 | FAILED_TESTS = [] 18 | 19 | def get_test_name(test_name): 20 | return "../src/test/concurrency/concurrency_sql/" + str(test_name) + ".sql" 21 | 22 | def get_output_name(test_name): 23 | return "../src/test/concurrency/concurrency_sql/" + str(test_name) + "_output.txt" 24 | 25 | def build(): 26 | # root 27 | os.chdir("../../../") 28 | if os.path.exists("./build"): 29 | os.system("rm -rf build") 30 | os.mkdir("./build") 31 | os.chdir("./build") 32 | os.system("cmake ..") 33 | os.system("make rmdb -j4") 34 | os.system("make concurrency_test -j4") 35 | os.chdir("..") 36 | 37 | def run(): 38 | os.chdir("./build") 39 | score = 0.0 40 | index = -1 41 | comment_str = "\n" 42 | 43 | for test_case in TESTS: 44 | print("-----------Concurrency Bonus Testing " + test_case + "...-----------") 45 | test_file = get_test_name(test_case) 46 | database_name = "concurrency_test_db" 47 | 48 | if os.path.exists(database_name): 49 | os.system("rm -rf " + database_name) 50 | 51 | os.system("./bin/rmdb " + database_name + " &") 52 | # ./bin/concurrency ../src/test/concurrency_test/xxx.sql 53 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 54 | time.sleep(3) 55 | os.system("./bin/concurrency_test " + test_file + " " + database_name + "/client_output.txt") 56 | 57 | # check result 58 | if CHECK_METHOD[index] == "dict_match": 59 | ansDict = {} 60 | standard_answer = get_output_name(test_case) 61 | hand0 = open(standard_answer, "r") 62 | for line in hand0: 63 | line = line.strip('\n') 64 | if line == "": 65 | continue 66 | num = ansDict.setdefault(line, 0) 67 | ansDict[line] = num + 1 68 | my_answer = database_name + "/client_output.txt" 69 | hand1 = open(my_answer, "r") 70 | for line in hand1: 71 | line = line.strip('\n') 72 | if line == "": 73 | continue 74 | num = ansDict.setdefault(line, 0) 75 | ansDict[line] = num - 1 76 | match = True 77 | for key, value in ansDict.items(): 78 | if value != 0: 79 | match = False 80 | comment_str += "In " + test_case + ", your answer mismatches standard answer.\n" 81 | break 82 | if match: 83 | score += 5 84 | else: 85 | FAILED_TESTS.append(test_case) 86 | else: 87 | res = os.system("diff " + database_name + "/client_output.txt " + get_output_name(test_case) + " -w") 88 | print("diff result: " + str(res)) 89 | # calculate the score 90 | if res == 0: 91 | score += 5 92 | else: 93 | FAILED_TESTS.append(test_case) 94 | comment_str += "In " + test_case + ", your answer mismatches standard answer.\n" 95 | 96 | # close server 97 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 98 | print("finish kill") 99 | # delete database 100 | # os.system("rm -rf ./" + database_name) 101 | # print("finish delete database") 102 | 103 | os.chdir("../../") 104 | 105 | 106 | if(len(FAILED_TESTS) != 0): 107 | print("\033[0;31;40mConcurrency Bonus Test Final Score:" + str(score)+ "\033[0m") 108 | print("Your program fails the following test cases: ") 109 | for failed_test in FAILED_TESTS: 110 | print("[" + failed_test + "] "), 111 | else: 112 | print("You have passed all Bonus test cases about concurrency control.") 113 | print("\033[0;31;40mConcurrency Bonus Test Final Score:" + str(score)+ "\033[0m") 114 | 115 | print(comment_str) 116 | 117 | if __name__ == "__main__": 118 | build() 119 | run() 120 | -------------------------------------------------------------------------------- /src/test/concurrency/concurrency_test_main.cpp: -------------------------------------------------------------------------------- 1 | #include "concurrency_test.h" 2 | #include "../regress/regress_test.h" 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | if(argc < 3) { 8 | fprintf(stderr, "Test_case and outfile_path needed.\n"); 9 | exit(1); 10 | } 11 | 12 | const char *unix_socket_path = nullptr; 13 | const char *server_host = "127.0.0.1"; 14 | int server_port = PORT_DEFAULT; 15 | int opt; 16 | 17 | while ((opt = getopt(argc, argv, "s:h:p:")) > 0) { 18 | switch (opt) { 19 | case 's': 20 | unix_socket_path = optarg; 21 | break; 22 | case 'p': 23 | char *ptr; 24 | server_port = (int)strtol(optarg, &ptr, 10); 25 | break; 26 | case 'h': 27 | server_host = optarg; 28 | break; 29 | default: 30 | break; 31 | } 32 | } 33 | 34 | TestCaseAnalyzer* analyzer = new TestCaseAnalyzer(); 35 | std::string outfile_path = argv[2]; 36 | std::fstream outfile; 37 | outfile.open(outfile_path, std::ios::out | std::ios::trunc); 38 | analyzer->infile_path = argv[1]; 39 | analyzer->analyze_test_case(); 40 | 41 | int preload_sockfd = connect_database(unix_socket_path, server_host, server_port); 42 | for(size_t i = 0; i < analyzer->preload.size(); ++i) { 43 | if(send_sql(preload_sockfd, analyzer->preload[i]) <= 0) 44 | break; 45 | } 46 | disconnect(preload_sockfd); 47 | 48 | for(size_t i = 0; i < analyzer->transactions.size(); ++i) { 49 | analyzer->transactions[i]->sockfd = connect_database(unix_socket_path, server_host, server_port); 50 | } 51 | 52 | OperationPermutation* permutation = analyzer->permutation; 53 | char recv_buf[MAX_MEM_BUFFER_SIZE]; 54 | for(size_t i = 0; i < permutation->operations.size(); ++i) { 55 | Transaction* txn = analyzer->transactions[permutation->operations[i]->txn_id]; 56 | send_recv_sql(txn->sockfd, permutation->operations[i]->sql, recv_buf); 57 | outfile << recv_buf; 58 | } 59 | 60 | outfile.close(); 61 | 62 | for(size_t i = 0; i < analyzer->transactions.size(); ++i) { 63 | disconnect(analyzer->transactions[i]->sockfd); 64 | } 65 | return 0; 66 | } -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_answer1.txt: -------------------------------------------------------------------------------- 1 | | Tables | 2 | | student | 3 | | grade | 4 | | Tables | 5 | | grade | 6 | failure 7 | failure 8 | | Tables | 9 | | grade | 10 | failure 11 | failure 12 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_answer2.txt: -------------------------------------------------------------------------------- 1 | | id | name | major | 2 | | 1 | TomTomTom | Computer ScienceComputer Science | 3 | | 2 | JerryJerr | Computer ScienceComputer Science | 4 | | 3 | JackJackJ | Electrical Engineeringer Science | 5 | | 3 | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 6 | | id | 7 | | 2 | 8 | | major | 9 | | Computer ScienceComputer Science | 10 | | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 11 | | name | major | 12 | | name | major | 13 | | JerryJerr | Computer ScienceComputer Science | 14 | | name | id | 15 | | KangKanga | 0 | 16 | | JerryJerr | 3 | 17 | failure 18 | failure 19 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_answer3.txt: -------------------------------------------------------------------------------- 1 | | id | name | major | 2 | | 1 | TomTomTom | Computer ScienceComputer Science | 3 | | 2 | JerryJerr | Electrical Engineering | 4 | | 3 | JackJackJ | Electrical Engineeringer Science | 5 | | 3 | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 6 | | id | name | major | 7 | | 0 | KangKanga | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 8 | failure 9 | | id | name | major | 10 | | 3 | JackJackJ | Electrical Engineeringer Science | 11 | | 3 | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 12 | | id | name | major | 13 | | 0 | KangKanga | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 14 | | 1 | TomTomTom | Computer ScienceComputer Science | 15 | | 2 | JerryJerr | Electrical Engineering | 16 | | 3 | JackJackJ | Electrical Engineeringer Science | 17 | | 3 | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 18 | | id | name | major | 19 | | 2 | 789123456 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | 20 | | id | name | major | 21 | | 0 | 789123456 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | 22 | | 1 | 789123456 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | 23 | | 2 | 789123456 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | 24 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_answer4.txt: -------------------------------------------------------------------------------- 1 | | id | name | major | 2 | | 0 | KangKanga | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 3 | | 1 | TomTomTom | Computer ScienceComputer Science | 4 | | 2 | JerryJerr | Computer ScienceComputer Science | 5 | | 3 | JackJackJ | Electrical Engineeringer Science | 6 | | 3 | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 7 | | 4 | JerryJerr | bbbbbbbbbbbbbbbcccccccccdddddddd | 8 | | 5 | JackJackJ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 9 | | 7 | RMDBrmdbr | 74125896332145698712365478996321 | 10 | | name | major | 11 | | KangKanga | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 12 | | TomTomTom | Computer ScienceComputer Science | 13 | | JerryJerr | Computer ScienceComputer Science | 14 | | JackJackJ | Electrical Engineeringer Science | 15 | | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 16 | | JerryJerr | bbbbbbbbbbbbbbbcccccccccdddddddd | 17 | | JackJackJ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 18 | | RMDBrmdbr | 74125896332145698712365478996321 | 19 | | id | name | major | 20 | | 0 | KangKanga | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 21 | | 1 | TomTomTom | Computer ScienceComputer Science | 22 | | 2 | JerryJerr | Computer ScienceComputer Science | 23 | | 3 | JerryJerr | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 24 | | 4 | JerryJerr | bbbbbbbbbbbbbbbcccccccccdddddddd | 25 | | 5 | JackJackJ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 26 | | 7 | RMDBrmdbr | 74125896332145698712365478996321 | 27 | | name | 28 | | JerryJerr | 29 | | name | 30 | | JackJackJ | 31 | | id | name | major | 32 | | 1 | TomTomTom | Computer ScienceComputer Science | 33 | | 2 | JerryJerr | Computer ScienceComputer Science | 34 | | 4 | JerryJerr | bbbbbbbbbbbbbbbcccccccccdddddddd | 35 | | 7 | RMDBrmdbr | 74125896332145698712365478996321 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_test1.sql: -------------------------------------------------------------------------------- 1 | -- Lab3-查询执行 测试点1: 尝试建表 2 | create table student (id int, name char(32), major char(32)); 3 | create table grade (course char(32), student_id int, score float); 4 | show tables; 5 | drop table student; 6 | show tables; 7 | create table grade (id int); 8 | drop table t; 9 | show tables; 10 | create index grade(id); 11 | create index grade(student_id); 12 | create index student(id); 13 | 14 | drop table grade; 15 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_test2.sql: -------------------------------------------------------------------------------- 1 | -- Lab3-查询执行 测试点2: 单表插入与条件查询 2 | create table student (id int, name char(9), major char(32)); 3 | insert into student values (0, 'KangKanga', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 4 | insert into student values (1, 'TomTomTom', 'Computer ScienceComputer Science'); 5 | insert into student values (2, 'JerryJerr', 'Computer ScienceComputer Science'); 6 | insert into student values (3, 'JackJackJ', 'Electrical Engineeringer Science'); 7 | insert into student values (3, 'JerryJerr', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 8 | select * from student where id >= 1; 9 | select id from student where id = 2; 10 | select major from student where name = 'JerryJerr'; 11 | select name,major from student where name = 'JerryJerr' and id = 0; 12 | select name,major from student where name = 'JerryJerr' and id = 2; 13 | select name,id from student where major = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; 14 | select myname from student; 15 | select name from student where myname = 'aaa'; 16 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_test3.sql: -------------------------------------------------------------------------------- 1 | -- Lab3-查询执行 测试点3: 单表更新与条件查询 2 | create table student (id int, name char(9), major char(32)); 3 | insert into student values (0, 'KangKanga', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 4 | insert into student values (1, 'TomTomTom', 'Computer ScienceComputer Science'); 5 | insert into student values (2, 'JerryJerr', 'Computer ScienceComputer Science'); 6 | insert into student values (3, 'JackJackJ', 'Electrical Engineeringer Science'); 7 | insert into student values (3, 'JerryJerr', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 8 | update student set major = 'Electrical Engineering' where id = 2; 9 | select * from student where id>=1; 10 | select * from student where id = 0; 11 | update student set id = 100 where name = 'JerryJerry'; 12 | select * from student where id > 2; 13 | select * from student where id < 101; 14 | update student set name = '789123456' , major = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' where id < 3; 15 | select * from student where id = 2; 16 | select * from student where name = '789123456'; 17 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_test4.sql: -------------------------------------------------------------------------------- 1 | -- Lab3-查询执行 测试点4: 单表删除与条件查询 2 | create table student (id int, name char(9), major char(32)); 3 | insert into student values (0, 'KangKanga', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 4 | insert into student values (1, 'TomTomTom', 'Computer ScienceComputer Science'); 5 | insert into student values (2, 'JerryJerr', 'Computer ScienceComputer Science'); 6 | insert into student values (3, 'JackJackJ', 'Electrical Engineeringer Science'); 7 | insert into student values (3, 'JerryJerr', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 8 | insert into student values (4, 'JerryJerr', 'bbbbbbbbbbbbbbbcccccccccdddddddd'); 9 | insert into student values (5, 'JackJackJ', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); 10 | insert into student values (-1, 'LiXianShe', 'c741258963qwertyuioplkjhgfdsazxc'); 11 | insert into student values (7, 'RMDBrmdbr', '74125896332145698712365478996321'); 12 | delete from student where name = 'Jack'; 13 | delete from student where id < 0; 14 | select * from student; 15 | select name,major from student where id > -5; 16 | delete from student where id = 3 and name = 'JackJackJ'; 17 | select * from student; 18 | select name from student where id = 3; 19 | select name from student where name = 'JackJackJ'; 20 | delete from student where major = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; 21 | select * from student where id>=1; 22 | -------------------------------------------------------------------------------- /src/test/query/query_sql/basic_query_test5.sql: -------------------------------------------------------------------------------- 1 | -- Lab3-查询执行 测试点5:连接查询 2 | create table student (id int, name char(9), major char(32)); 3 | create table grade (course char(9), student_id int, score float); 4 | insert into student values (1, 'TomTomTom', 'Computer ScienceComputer Science'); 5 | insert into student values (2, 'JerryJerr', 'Computer ScienceComputer Science'); 6 | insert into student values (3, 'JackJackJ', 'Electrical Engineeringer Science'); 7 | insert into grade values ('DataDataa', 1, 90.0); 8 | insert into grade values ('DataDatab', 1, 60.0); 9 | insert into grade values ('DataDatac', 1, 82.0); 10 | insert into grade values ('DataDatad', 1, 88.5); 11 | insert into grade values ('DataDataa', 2, 91.25); 12 | insert into grade values ('DataDatab', 2, 90.0); 13 | insert into grade values ('DataDatac', 2, 89.5); 14 | insert into grade values ('DataDatad', 2, 88.0); 15 | insert into grade values ('DataDataa', 3, 90.0); 16 | insert into grade values ('DataDatab', 3, 90.125); 17 | insert into grade values ('DataDatac', 3, 99.0); 18 | insert into grade values ('DataDatad', 3, 97.5); 19 | insert into grade values ('DataDataa', 4, 82.0); 20 | insert into grade values ('DataDatab', 4, 85.0); 21 | insert into grade values ('DataDatac', 4, 85.0); 22 | insert into grade values ('DataDatad', 4, 90.5); 23 | select * from student, grade; 24 | select id, name, major, course, score from student, grade where student.id = grade.student_id; 25 | -------------------------------------------------------------------------------- /src/test/query/query_test_basic.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import time; 3 | # test : basic_query 4 | NUM_TESTS = 5 5 | SCORES = [25, 15, 15, 15, 30] 6 | 7 | # current dir is root/build 8 | def get_test_name(index): 9 | return "../src/test/query/query_sql/basic_query_test"+str(index)+".sql" 10 | 11 | def get_output_name(index): 12 | return "../src/test/query/query_sql/basic_query_answer"+str(index)+".txt" 13 | 14 | def build(): 15 | # change dir to root 16 | os.chdir("../../../") 17 | if os.path.exists("./build"): 18 | os.system("rm -rf build") 19 | os.mkdir("./build") 20 | os.chdir("./build") 21 | os.system("cmake ..") 22 | os.system("make rmdb -j4") 23 | os.system("make query_test -j4") 24 | os.chdir("..") 25 | 26 | 27 | def run(): 28 | # dir is root/build 29 | os.chdir("./build") 30 | score = 0.0 31 | 32 | for i in range(NUM_TESTS): 33 | # if i == 0 : 34 | # continue 35 | 36 | test_file = get_test_name(i + 1) 37 | database_name = "query_test_db" 38 | 39 | if os.path.exists(database_name): 40 | os.system("rm -rf " + database_name) 41 | 42 | os.system("./bin/rmdb " + database_name + "&") 43 | # ./bin/query_test ../src/test/query_test/aggregate_test.sql 44 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 45 | time.sleep(3) 46 | ret = os.system("./bin/query_test " + test_file) 47 | if(ret != 0): 48 | print("Error. Stopping") 49 | exit(0) 50 | 51 | # check result 52 | ansDict={} 53 | standard_answer = get_output_name(i + 1) 54 | hand0 = open(standard_answer,"r") 55 | for line in hand0 : 56 | line = line.strip('\n') 57 | if line == "": 58 | continue 59 | num=ansDict.setdefault(line,0) 60 | ansDict[line]=num+1 61 | my_answer = database_name + "/output.txt" 62 | hand1 = open(my_answer,"r") 63 | for line in hand1 : 64 | line = line.strip('\n') 65 | if line == "": 66 | continue 67 | num=ansDict.setdefault(line,0) 68 | ansDict[line]=num-1 69 | match = True 70 | for key,value in ansDict.items(): 71 | if value != 0: 72 | match = False 73 | if value > 0: 74 | print('In basic query test'+str(i+1),'Mismatch,your answer lack items') 75 | else : 76 | print('In basic query test'+str(i+1),'Mismatch,your answer has redundant items') 77 | if match : 78 | score += SCORES[i] 79 | # close server 80 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 81 | print("finish kill") 82 | # delete database 83 | if i < 4: 84 | os.system("rm -rf ./" + database_name) 85 | print("finish delete database") 86 | 87 | os.chdir("../../") 88 | print("final score: " + str(score)) 89 | 90 | if __name__ == "__main__": 91 | build() 92 | run() 93 | -------------------------------------------------------------------------------- /src/test/query/query_unit_test.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import time; 3 | import sys; 4 | # test : basic_query 5 | NUM_TESTS = 5 6 | SCORES = [25, 15, 15, 15, 30] 7 | 8 | # current dir is root/build 9 | def get_test_name(index): 10 | return "../src/test/query/query_sql/basic_query_test"+str(index)+".sql" 11 | 12 | def get_output_name(index): 13 | return "../src/test/query/query_sql/basic_query_answer"+str(index)+".txt" 14 | 15 | def extract_index(test_file): 16 | # Extract the index from the test file name 17 | index_str = ''.join(c for c in test_file if c.isdigit()) 18 | if index_str: 19 | return int(index_str) 20 | else: 21 | print(f"Error: Could not extract index from the test file {test_file}.") 22 | sys.exit(1) 23 | 24 | def build(): 25 | # change dir to root 26 | os.chdir("../../../") 27 | if os.path.exists("./build"): 28 | os.system("rm -rf build") 29 | os.mkdir("./build") 30 | os.chdir("./build") 31 | os.system("cmake ..") 32 | os.system("make rmdb -j4") 33 | os.system("make query_test -j4") 34 | os.chdir("..") 35 | 36 | 37 | def run(test_file): 38 | # dir is root/build 39 | os.chdir("./build") 40 | score = 0.0 41 | 42 | test_index = extract_index(test_file) 43 | 44 | if not (1 <= test_index <= NUM_TESTS): 45 | print("Error: Invalid index. The index should be between 1 and {NUM_TEST}.") 46 | exit(0) 47 | 48 | test_file = get_test_name(test_index) 49 | database_name = "query_test_db" 50 | 51 | if os.path.exists(database_name): 52 | os.system("rm -rf " + database_name) 53 | 54 | os.system("./bin/rmdb " + database_name + "&") 55 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 56 | time.sleep(3) 57 | ret = os.system("./bin/query_test " + test_file) 58 | if(ret != 0): 59 | print("Error. Stopping") 60 | exit(0) 61 | 62 | # check result 63 | ansDict={} 64 | standard_answer = get_output_name(test_index) 65 | hand0 = open(standard_answer,"r") 66 | for line in hand0 : 67 | line = line.strip('\n') 68 | if line == "": 69 | continue 70 | num=ansDict.setdefault(line,0) 71 | ansDict[line]=num+1 72 | my_answer = database_name + "/output.txt" 73 | hand1 = open(my_answer,"r") 74 | for line in hand1 : 75 | line = line.strip('\n') 76 | if line == "": 77 | continue 78 | num=ansDict.setdefault(line,0) 79 | ansDict[line]=num-1 80 | match = True 81 | for key,value in ansDict.items(): 82 | if value != 0: 83 | match = False 84 | if value > 0: 85 | print('In basic query test'+str(test_index),'Mismatch,your answer lack items') 86 | else : 87 | print('In basic query test'+str(test_index),'Mismatch,your answer has redundant items') 88 | if match : 89 | score += SCORES[test_index-1] 90 | # close server 91 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 92 | print("finish kill") 93 | # delete database 94 | if test_index < 5: 95 | os.system("rm -rf ./" + database_name) 96 | print("finish delete database") 97 | 98 | os.chdir("../../") 99 | print("Unit Test Score: " + str(score)) 100 | 101 | if __name__ == "__main__": 102 | if len(sys.argv) != 2: 103 | print("Usage: python3 query_unit_test.py ") 104 | exit(0) 105 | 106 | build() 107 | run(sys.argv[1]) -------------------------------------------------------------------------------- /src/test/regress/regress_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "regress_test.h" 14 | 15 | int init_unix_sock(const char *unix_sock_path) { 16 | int sockfd = socket(PF_UNIX, SOCK_STREAM, 0); 17 | if (sockfd < 0) { 18 | fprintf(stderr, "failed to create unix socket. %s", strerror(errno)); 19 | return -1; 20 | } 21 | 22 | struct sockaddr_un sockaddr; 23 | memset(&sockaddr, 0, sizeof(sockaddr)); 24 | sockaddr.sun_family = PF_UNIX; 25 | snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", unix_sock_path); 26 | 27 | if (connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { 28 | fprintf(stderr, "failed to connect to server. unix socket path '%s'. error %s", sockaddr.sun_path, 29 | strerror(errno)); 30 | close(sockfd); 31 | return -1; 32 | } 33 | return sockfd; 34 | } 35 | 36 | int init_tcp_sock(const char *server_host, int server_port) { 37 | struct hostent *host; 38 | struct sockaddr_in serv_addr; 39 | 40 | if ((host = gethostbyname(server_host)) == NULL) { 41 | fprintf(stderr, "gethostbyname failed. errmsg=%d:%s\n", errno, strerror(errno)); 42 | return -1; 43 | } 44 | 45 | int sockfd; 46 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 47 | fprintf(stderr, "create socket error. errmsg=%d:%s\n", errno, strerror(errno)); 48 | return -1; 49 | } 50 | 51 | serv_addr.sin_family = AF_INET; 52 | serv_addr.sin_port = htons(server_port); 53 | serv_addr.sin_addr = *((struct in_addr *)host->h_addr); 54 | bzero(&(serv_addr.sin_zero), 8); 55 | 56 | if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1) { 57 | fprintf(stderr, "Failed to connect. errmsg=%d:%s\n", errno, strerror(errno)); 58 | close(sockfd); 59 | return -1; 60 | } 61 | return sockfd; 62 | } 63 | 64 | int connect_database(const char* unix_sockect_path, const char* server_host, int server_port) { 65 | int sockfd; 66 | 67 | if(unix_sockect_path != nullptr) { 68 | sockfd = init_unix_sock(unix_sockect_path); 69 | } 70 | else { 71 | sockfd = init_tcp_sock(server_host, server_port); 72 | } 73 | 74 | if(sockfd < 0) { 75 | exit(1); 76 | } 77 | 78 | return sockfd; 79 | } 80 | 81 | void disconnect(int sockfd) { 82 | close(sockfd); 83 | } 84 | 85 | int send_sql(int sockfd, const std::string& sql) { 86 | char recv_buf[MAX_MEM_BUFFER_SIZE]; 87 | 88 | return send_recv_sql(sockfd, sql, recv_buf); 89 | } 90 | int send_recv_sql(int sockfd, const std::string& sql, char* recv_buf) { 91 | int send_bytes; 92 | int recv_bytes; 93 | 94 | if((send_bytes = write(sockfd, sql.c_str(), sql.length() + 1)) == -1) { 95 | fprintf(stderr, "Send Error %d: %s\n", errno, strerror(errno)); 96 | exit(1); 97 | } 98 | 99 | memset(recv_buf, 0, MAX_MEM_BUFFER_SIZE); 100 | recv_bytes = recv(sockfd, recv_buf, MAX_MEM_BUFFER_SIZE, 0); 101 | 102 | if(recv_bytes < 0) { 103 | fprintf(stderr, "Connection was broken: %s\n", strerror(errno)); 104 | exit(1); 105 | } 106 | else if(recv_bytes == 0) { 107 | printf("Connection has been closed\n"); 108 | exit(1); 109 | } 110 | 111 | return recv_bytes; 112 | } 113 | 114 | void start_test(int sockfd, std::string infile) { 115 | std::ifstream test_input; 116 | std::string sql; 117 | char recv_buf[MAX_MEM_BUFFER_SIZE]; 118 | 119 | test_input.open(infile); 120 | 121 | while(std::getline(test_input, sql)) { 122 | memset(recv_buf, 0, sizeof(recv_buf)); 123 | if(send_recv_sql(sockfd, sql, recv_buf) <= 0) 124 | break; 125 | } 126 | } -------------------------------------------------------------------------------- /src/test/regress/regress_test.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAX_MEM_BUFFER_SIZE 8192 5 | #define PORT_DEFAULT 8765 6 | 7 | int init_unix_sock(const char *unix_sock_path); 8 | int init_tcp_sock(const char *server_host, int server_port); 9 | int connect_database(const char* unix_sockect_path, const char* server_host, int server_port); 10 | void disconnect(int sockfd); 11 | int send_sql(int sockfd, const std::string& sql); 12 | int send_recv_sql(int sockfd, const std::string& sql, char* recv_buf); 13 | void start_test(int sockfd, std::string infile); -------------------------------------------------------------------------------- /src/test/regress/regress_test_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "regress_test.h" 4 | 5 | int main(int argc, char** argv) { 6 | if(argc < 2) { 7 | fprintf(stderr, "Test_case needed.\n"); 8 | exit(1); 9 | } 10 | std::string infile = argv[1]; 11 | 12 | const char *unix_socket_path = nullptr; 13 | const char *server_host = "127.0.0.1"; 14 | int server_port = PORT_DEFAULT; 15 | int opt; 16 | 17 | while ((opt = getopt(argc, argv, "s:h:p:")) > 0) { 18 | switch (opt) { 19 | case 's': 20 | unix_socket_path = optarg; 21 | break; 22 | case 'p': 23 | char *ptr; 24 | server_port = (int)strtol(optarg, &ptr, 10); 25 | break; 26 | case 'h': 27 | server_host = optarg; 28 | break; 29 | default: 30 | break; 31 | } 32 | } 33 | 34 | int sockfd = connect_database(unix_socket_path, server_host, server_port); 35 | start_test(sockfd, infile); 36 | disconnect(sockfd); 37 | } -------------------------------------------------------------------------------- /src/test/transaction/transaction_test.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import time; 3 | import subprocess 4 | 5 | NUM_TESTS = 2 6 | 7 | TESTS = ["commit_test", 8 | "abort_test"] 9 | 10 | FAILED_TESTS = [] 11 | 12 | def get_test_name(test_case): 13 | return "../src/test/transaction/transaction_sql/" + str(test_case) + ".sql" 14 | 15 | def get_output_name(test_case): 16 | return "../src/test/transaction/transaction_sql/" + str(test_case) + "_output.txt" 17 | 18 | def build(): 19 | # root 20 | os.chdir("../../../") 21 | if os.path.exists("./build"): 22 | os.system("rm -rf build") 23 | os.mkdir("./build") 24 | os.chdir("./build") 25 | os.system("cmake ..") 26 | os.system("make rmdb -j4") 27 | os.system("make transaction_test -j4") 28 | os.chdir("..") 29 | 30 | def run(): 31 | os.chdir("./build") 32 | score = 0.0 33 | comment_str = "\n" 34 | 35 | for test_case in TESTS: 36 | print("-----------Transaction Testing " + test_case + "...-----------") 37 | test_file = get_test_name(test_case) 38 | database_name = "transaction_test_db" 39 | 40 | if os.path.exists(database_name): 41 | os.system("rm -rf " + database_name) 42 | 43 | os.system("./bin/rmdb " + database_name + " &") 44 | # ./bin/transaction_test ../src/test/transaction_test/commit_test.sql 45 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 46 | time.sleep(3) 47 | ret = os.system("./bin/transaction_test " + test_file) 48 | if(ret != 0): 49 | print("Error. Stopping") 50 | exit(0) 51 | # check result 52 | ansDict = {} 53 | standard_answer = get_output_name(test_case) 54 | my_answer = database_name+"/output.txt" 55 | try: 56 | hand0 = open(standard_answer, "r") 57 | except: 58 | FAILED_TESTS.append(test_case) 59 | comment_str += ("In Transaction_basic_test:" + 60 | str(test_case) + ", open standard_answer failed\n") 61 | subprocess.run( 62 | "ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9", shell=True) 63 | continue 64 | else: 65 | for line in hand0: 66 | line = line.strip('\n') 67 | if line == "": 68 | continue 69 | num = ansDict.setdefault(line, 0) 70 | ansDict[line] = num+1 71 | # try to get your answer 72 | try: 73 | hand1 = open(my_answer, "r") 74 | except: 75 | FAILED_TESTS.append(test_case) 76 | comment_str += ("In Transaction_basic_test:" + 77 | str(test_case) + ", cannot open output file\n") 78 | subprocess.run( 79 | "ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9", shell=True) 80 | continue 81 | else: 82 | for line in hand1: 83 | line = line.strip('\n') 84 | if line == "": 85 | continue 86 | num = ansDict.setdefault(line, 0) 87 | ansDict[line] = num-1 88 | # Dict match 89 | match = True 90 | for key, value in ansDict.items(): 91 | if value != 0: 92 | match = False 93 | comment_str += "In Transaction_basic_test:" + \ 94 | str(test_case)+", your answer mismatches standard answer\n" 95 | break 96 | if match: 97 | score += 20 98 | else: 99 | FAILED_TESTS.append(test_case) 100 | 101 | # close server 102 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 103 | print("finish kill") 104 | 105 | os.chdir("../../") 106 | if (len(FAILED_TESTS) != 0): 107 | print("\033[0;31;40mTransaction Test Final Score:" + str(score)+ "\033[0m") 108 | print("Your program fails the following test cases: ") 109 | for failed_test in FAILED_TESTS: 110 | print("[" + failed_test + "] "), 111 | else: 112 | print("You have passed all basic test cases about transaction!") 113 | print("\033[0;31;40mTransaction Test Final Score:" + str(score)+ "\033[0m") 114 | print(comment_str) 115 | 116 | if __name__ == "__main__": 117 | build() 118 | run() -------------------------------------------------------------------------------- /src/test/transaction/transaction_test_bonus.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import time; 3 | import subprocess 4 | 5 | NUM_TESTS = 2 6 | 7 | TESTS = ["commit_index_test", 8 | "abort_index_test"] 9 | 10 | FAILED_TESTS = [] 11 | 12 | def get_test_name(test_case): 13 | return "../src/test/transaction/transaction_sql/" + str(test_case) + ".sql" 14 | 15 | def get_output_name(test_case): 16 | return "../src/test/transaction/transaction_sql/" + str(test_case) + "_output.txt" 17 | 18 | def build(): 19 | # root 20 | os.chdir("../../../") 21 | if os.path.exists("./build"): 22 | os.system("rm -rf build") 23 | os.mkdir("./build") 24 | os.chdir("./build") 25 | os.system("cmake ..") 26 | os.system("make rmdb -j4") 27 | os.system("make transaction_test -j4") 28 | os.chdir("..") 29 | 30 | def run(): 31 | os.chdir("./build") 32 | score = 0.0 33 | comment_str = "\n" 34 | 35 | for test_case in TESTS: 36 | print("-----------Transaction Bonus Testing " + test_case + "...-----------") 37 | test_file = get_test_name(test_case) 38 | database_name = "transaction_test_db" 39 | 40 | if os.path.exists(database_name): 41 | os.system("rm -rf " + database_name) 42 | 43 | os.system("./bin/rmdb " + database_name + " &") 44 | # ./bin/transaction_test ../src/test/transaction_test/commit_test.sql 45 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 46 | time.sleep(3) 47 | ret = os.system("./bin/transaction_test " + test_file) 48 | if(ret != 0): 49 | print("Error. Stopping") 50 | exit(0) 51 | # check result 52 | ansDict = {} 53 | standard_answer = get_output_name(test_case) 54 | my_answer = database_name+"/output.txt" 55 | try: 56 | hand0 = open(standard_answer, "r") 57 | except: 58 | FAILED_TESTS.append(test_case) 59 | comment_str += ("In transaction_test_bonus:" + 60 | str(test_case) + ", open standard_answer failed\n") 61 | subprocess.run( 62 | "ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9", shell=True) 63 | continue 64 | else: 65 | for line in hand0: 66 | line = line.strip('\n') 67 | if line == "": 68 | continue 69 | num = ansDict.setdefault(line, 0) 70 | ansDict[line] = num+1 71 | # try to get your answer 72 | try: 73 | hand1 = open(my_answer, "r") 74 | except: 75 | FAILED_TESTS.append(test_case) 76 | comment_str += ("In transaction_test_bonus:" + 77 | str(test_case) + ", cannot open output file\n") 78 | subprocess.run( 79 | "ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9", shell=True) 80 | continue 81 | else: 82 | for line in hand1: 83 | line = line.strip('\n') 84 | if line == "": 85 | continue 86 | num = ansDict.setdefault(line, 0) 87 | ansDict[line] = num-1 88 | # Dict match 89 | match = True 90 | for key, value in ansDict.items(): 91 | if value != 0: 92 | match = False 93 | comment_str += "In transaction_test_bonus:" + \ 94 | str(test_case)+", your answer mismatches standard answer\n" 95 | break 96 | if match: 97 | score += 20 98 | else: 99 | FAILED_TESTS.append(test_case) 100 | 101 | # close server 102 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 103 | print("finish kill") 104 | 105 | os.chdir("../../") 106 | if (len(FAILED_TESTS) != 0): 107 | print("Your program fails the following test cases: ") 108 | for failed_test in FAILED_TESTS: 109 | print("[" + failed_test + "] "), 110 | else: 111 | print("You have passed all Bonus test cases about transaction!") 112 | print("\033[0;31;40mTransaction Bonus Test Final Score:" + str(score)+ "\033[0m") 113 | print(comment_str) 114 | 115 | if __name__ == "__main__": 116 | build() 117 | run() 118 | -------------------------------------------------------------------------------- /src/test/transaction/transaction_unit_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import subprocess 5 | 6 | NUM_TESTS = 1 7 | 8 | TESTS = ["commit_test", 9 | "abort_test", 10 | "commit_index_test", 11 | "abort_index_test"] 12 | 13 | FAILED_TESTS = [] 14 | 15 | def get_test_name(test_case): 16 | return "../src/test/transaction/transaction_sql/" + str(test_case) + ".sql" 17 | 18 | def get_output_name(test_case): 19 | return "../src/test/transaction/transaction_sql/" + str(test_case) + "_output.txt" 20 | 21 | def build(): 22 | # root 23 | os.chdir("../../../") 24 | if os.path.exists("./build"): 25 | os.system("rm -rf build") 26 | os.mkdir("./build") 27 | os.chdir("./build") 28 | os.system("cmake ..") 29 | os.system("make rmdb -j4") 30 | os.system("make transaction_test -j4") 31 | os.chdir("..") 32 | 33 | def run_test(test_case): 34 | if test_case not in TESTS: 35 | print(f"Test case '{test_case}' is not recognized.") 36 | return 37 | os.chdir("./build") 38 | score = 0.0 39 | comment_str = "\n" 40 | 41 | print("-----------Transaction Unit Testing " + test_case + "...-----------") 42 | test_file = get_test_name(test_case) 43 | database_name = "transaction_test_db" 44 | 45 | if os.path.exists(database_name): 46 | os.system("rm -rf " + database_name) 47 | 48 | os.system("./bin/rmdb " + database_name + " &") 49 | # ./bin/transaction_test ../src/test/transaction_test/commit_test.sql 50 | # The server takes a few seconds to establish the connection, so the client should wait for a while. 51 | time.sleep(3) 52 | ret = os.system("./bin/transaction_test " + test_file) 53 | if(ret != 0): 54 | print("Error. Stopping") 55 | exit(0) 56 | # check result 57 | ansDict = {} 58 | standard_answer = get_output_name(test_case) 59 | my_answer = database_name+"/output.txt" 60 | try: 61 | hand0 = open(standard_answer, "r") 62 | except: 63 | FAILED_TESTS.append(test_case) 64 | comment_str += ("In Transaction unit test:" + 65 | str(test_case) + ", open standard_answer failed\n") 66 | subprocess.run( 67 | "ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9", shell=True) 68 | return 69 | else: 70 | for line in hand0: 71 | line = line.strip('\n') 72 | if line == "": 73 | continue 74 | num = ansDict.setdefault(line, 0) 75 | ansDict[line] = num+1 76 | # try to get your answer 77 | try: 78 | hand1 = open(my_answer, "r") 79 | except: 80 | FAILED_TESTS.append(test_case) 81 | comment_str += ("In Transaction unit test:" + 82 | str(test_case) + ", cannot open output file\n") 83 | subprocess.run( 84 | "ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9", shell=True) 85 | return 86 | else: 87 | for line in hand1: 88 | line = line.strip('\n') 89 | if line == "": 90 | continue 91 | num = ansDict.setdefault(line, 0) 92 | ansDict[line] = num-1 93 | # Dict match 94 | match = True 95 | for key, value in ansDict.items(): 96 | if value != 0: 97 | match = False 98 | comment_str += "In Transaction unit test:" + \ 99 | str(test_case)+", your answer mismatches standard answer\n" 100 | break 101 | if match: 102 | score += 20 103 | else: 104 | FAILED_TESTS.append(test_case) 105 | 106 | # close server 107 | os.system("ps -ef | grep rmdb | grep -v grep | awk '{print $2}' | xargs kill -9") 108 | print("finish kill") 109 | 110 | os.chdir("../../") 111 | if (len(FAILED_TESTS) != 0): 112 | print(f"\033[0;31;40mTransaction Test {test_case} Final Score:" + str(score)+ "\033[0m") 113 | print("Your program fails the following test cases: ") 114 | for failed_test in FAILED_TESTS: 115 | print("[" + failed_test + "] "), 116 | else: 117 | print(f"You have passed unit test case {test_case} about transaction!") 118 | print(f"\033[0;31;40mTransaction Test {test_case} Final Score:" + str(score)+ "\033[0m") 119 | print(comment_str) 120 | 121 | if __name__ == "__main__": 122 | if len(sys.argv) != 2: 123 | print("Usage: python3 transaction_unit_test.py ") 124 | sys.exit(1) 125 | 126 | build() 127 | test_case_name = sys.argv[1] 128 | run_test(test_case_name) -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /src/transaction/concurrency/lock_manager.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "lock_manager.h" 12 | 13 | /** 14 | * @description: 申请行级共享锁 15 | * @return {bool} 加锁是否成功 16 | * @param {Transaction*} txn 要申请锁的事务对象指针 17 | * @param {Rid&} rid 加锁的目标记录ID 记录所在的表的fd 18 | * @param {int} tab_fd 19 | */ 20 | bool LockManager::lock_shared_on_record(Transaction* txn, const Rid& rid, int tab_fd) { 21 | 22 | return true; 23 | } 24 | 25 | /** 26 | * @description: 申请行级排他锁 27 | * @return {bool} 加锁是否成功 28 | * @param {Transaction*} txn 要申请锁的事务对象指针 29 | * @param {Rid&} rid 加锁的目标记录ID 30 | * @param {int} tab_fd 记录所在的表的fd 31 | */ 32 | bool LockManager::lock_exclusive_on_record(Transaction* txn, const Rid& rid, int tab_fd) { 33 | 34 | return true; 35 | } 36 | 37 | /** 38 | * @description: 申请表级读锁 39 | * @return {bool} 返回加锁是否成功 40 | * @param {Transaction*} txn 要申请锁的事务对象指针 41 | * @param {int} tab_fd 目标表的fd 42 | */ 43 | bool LockManager::lock_shared_on_table(Transaction* txn, int tab_fd) { 44 | 45 | return true; 46 | } 47 | 48 | /** 49 | * @description: 申请表级写锁 50 | * @return {bool} 返回加锁是否成功 51 | * @param {Transaction*} txn 要申请锁的事务对象指针 52 | * @param {int} tab_fd 目标表的fd 53 | */ 54 | bool LockManager::lock_exclusive_on_table(Transaction* txn, int tab_fd) { 55 | 56 | return true; 57 | } 58 | 59 | /** 60 | * @description: 申请表级意向读锁 61 | * @return {bool} 返回加锁是否成功 62 | * @param {Transaction*} txn 要申请锁的事务对象指针 63 | * @param {int} tab_fd 目标表的fd 64 | */ 65 | bool LockManager::lock_IS_on_table(Transaction* txn, int tab_fd) { 66 | 67 | return true; 68 | } 69 | 70 | /** 71 | * @description: 申请表级意向写锁 72 | * @return {bool} 返回加锁是否成功 73 | * @param {Transaction*} txn 要申请锁的事务对象指针 74 | * @param {int} tab_fd 目标表的fd 75 | */ 76 | bool LockManager::lock_IX_on_table(Transaction* txn, int tab_fd) { 77 | 78 | return true; 79 | } 80 | 81 | /** 82 | * @description: 释放锁 83 | * @return {bool} 返回解锁是否成功 84 | * @param {Transaction*} txn 要释放锁的事务对象指针 85 | * @param {LockDataId} lock_data_id 要释放的锁ID 86 | */ 87 | bool LockManager::unlock(Transaction* txn, LockDataId lock_data_id) { 88 | 89 | return true; 90 | } -------------------------------------------------------------------------------- /src/transaction/concurrency/lock_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include "transaction/transaction.h" 16 | 17 | static const std::string GroupLockModeStr[10] = {"NON_LOCK", "IS", "IX", "S", "X", "SIX"}; 18 | 19 | class LockManager { 20 | /* 加锁类型,包括共享锁、排他锁、意向共享锁、意向排他锁、SIX(意向排他锁+共享锁) */ 21 | enum class LockMode { SHARED, EXLUCSIVE, INTENTION_SHARED, INTENTION_EXCLUSIVE, S_IX }; 22 | 23 | /* 用于标识加锁队列中排他性最强的锁类型,例如加锁队列中有SHARED和EXLUSIVE两个加锁操作,则该队列的锁模式为X */ 24 | enum class GroupLockMode { NON_LOCK, IS, IX, S, X, SIX}; 25 | 26 | /* 事务的加锁申请 */ 27 | class LockRequest { 28 | public: 29 | LockRequest(txn_id_t txn_id, LockMode lock_mode) 30 | : txn_id_(txn_id), lock_mode_(lock_mode), granted_(false) {} 31 | 32 | txn_id_t txn_id_; // 申请加锁的事务ID 33 | LockMode lock_mode_; // 事务申请加锁的类型 34 | bool granted_; // 该事务是否已经被赋予锁 35 | }; 36 | 37 | /* 数据项上的加锁队列 */ 38 | class LockRequestQueue { 39 | public: 40 | std::list request_queue_; // 加锁队列 41 | std::condition_variable cv_; // 条件变量,用于唤醒正在等待加锁的申请,在no-wait策略下无需使用 42 | GroupLockMode group_lock_mode_ = GroupLockMode::NON_LOCK; // 加锁队列的锁模式 43 | }; 44 | 45 | public: 46 | LockManager() {} 47 | 48 | ~LockManager() {} 49 | 50 | bool lock_shared_on_record(Transaction* txn, const Rid& rid, int tab_fd); 51 | 52 | bool lock_exclusive_on_record(Transaction* txn, const Rid& rid, int tab_fd); 53 | 54 | bool lock_shared_on_table(Transaction* txn, int tab_fd); 55 | 56 | bool lock_exclusive_on_table(Transaction* txn, int tab_fd); 57 | 58 | bool lock_IS_on_table(Transaction* txn, int tab_fd); 59 | 60 | bool lock_IX_on_table(Transaction* txn, int tab_fd); 61 | 62 | bool unlock(Transaction* txn, LockDataId lock_data_id); 63 | 64 | private: 65 | std::mutex latch_; // 用于锁表的并发 66 | std::unordered_map lock_table_; // 全局锁表 67 | }; 68 | -------------------------------------------------------------------------------- /src/transaction/transaction.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "txn_defs.h" 21 | 22 | class Transaction { 23 | public: 24 | explicit Transaction(txn_id_t txn_id, IsolationLevel isolation_level = IsolationLevel::SERIALIZABLE) 25 | : state_(TransactionState::DEFAULT), isolation_level_(isolation_level), txn_id_(txn_id) { 26 | write_set_ = std::make_shared>(); 27 | lock_set_ = std::make_shared>(); 28 | index_latch_page_set_ = std::make_shared>(); 29 | index_deleted_page_set_ = std::make_shared>(); 30 | prev_lsn_ = INVALID_LSN; 31 | thread_id_ = std::this_thread::get_id(); 32 | } 33 | 34 | ~Transaction() = default; 35 | 36 | inline txn_id_t get_transaction_id() { return txn_id_; } 37 | 38 | inline std::thread::id get_thread_id() { return thread_id_; } 39 | 40 | inline void set_txn_mode(bool txn_mode) { txn_mode_ = txn_mode; } 41 | inline bool get_txn_mode() { return txn_mode_; } 42 | 43 | inline void set_start_ts(timestamp_t start_ts) { start_ts_ = start_ts; } 44 | inline timestamp_t get_start_ts() { return start_ts_; } 45 | 46 | inline IsolationLevel get_isolation_level() { return isolation_level_; } 47 | 48 | inline TransactionState get_state() { return state_; } 49 | inline void set_state(TransactionState state) { state_ = state; } 50 | 51 | inline lsn_t get_prev_lsn() { return prev_lsn_; } 52 | inline void set_prev_lsn(lsn_t prev_lsn) { prev_lsn_ = prev_lsn; } 53 | 54 | inline std::shared_ptr> get_write_set() { return write_set_; } 55 | inline void append_write_record(WriteRecord* write_record) { write_set_->push_back(write_record); } 56 | 57 | inline std::shared_ptr> get_index_deleted_page_set() { return index_deleted_page_set_; } 58 | inline void append_index_deleted_page(Page* page) { index_deleted_page_set_->push_back(page); } 59 | 60 | inline std::shared_ptr> get_index_latch_page_set() { return index_latch_page_set_; } 61 | inline void append_index_latch_page_set(Page* page) { index_latch_page_set_->push_back(page); } 62 | 63 | inline std::shared_ptr> get_lock_set() { return lock_set_; } 64 | 65 | private: 66 | bool txn_mode_; // 用于标识当前事务为显式事务还是单条SQL语句的隐式事务 67 | TransactionState state_; // 事务状态 68 | IsolationLevel isolation_level_; // 事务的隔离级别,默认隔离级别为可串行化 69 | std::thread::id thread_id_; // 当前事务对应的线程id 70 | lsn_t prev_lsn_; // 当前事务执行的最后一条操作对应的lsn,用于系统故障恢复 71 | txn_id_t txn_id_; // 事务的ID,唯一标识符 72 | timestamp_t start_ts_; // 事务的开始时间戳 73 | 74 | std::shared_ptr> write_set_; // 事务包含的所有写操作 75 | std::shared_ptr> lock_set_; // 事务申请的所有锁 76 | std::shared_ptr> index_latch_page_set_; // 维护事务执行过程中加锁的索引页面 77 | std::shared_ptr> index_deleted_page_set_; // 维护事务执行过程中删除的索引页面 78 | }; 79 | -------------------------------------------------------------------------------- /src/transaction/transaction_manager.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #include "transaction_manager.h" 12 | #include "record/rm_file_handle.h" 13 | #include "system/sm_manager.h" 14 | 15 | std::unordered_map TransactionManager::txn_map = {}; 16 | 17 | /** 18 | * @description: 事务的开始方法 19 | * @return {Transaction*} 开始事务的指针 20 | * @param {Transaction*} txn 事务指针,空指针代表需要创建新事务,否则开始已有事务 21 | * @param {LogManager*} log_manager 日志管理器指针 22 | */ 23 | Transaction * TransactionManager::begin(Transaction* txn, LogManager* log_manager) { 24 | // Todo: 25 | // 1. 判断传入事务参数是否为空指针 26 | // 2. 如果为空指针,创建新事务 27 | // 3. 把开始事务加入到全局事务表中 28 | // 4. 返回当前事务指针 29 | 30 | return nullptr; 31 | } 32 | 33 | /** 34 | * @description: 事务的提交方法 35 | * @param {Transaction*} txn 需要提交的事务 36 | * @param {LogManager*} log_manager 日志管理器指针 37 | */ 38 | void TransactionManager::commit(Transaction* txn, LogManager* log_manager) { 39 | // Todo: 40 | // 1. 如果存在未提交的写操作,提交所有的写操作 41 | // 2. 释放所有锁 42 | // 3. 释放事务相关资源,eg.锁集 43 | // 4. 把事务日志刷入磁盘中 44 | // 5. 更新事务状态 45 | 46 | } 47 | 48 | /** 49 | * @description: 事务的终止(回滚)方法 50 | * @param {Transaction *} txn 需要回滚的事务 51 | * @param {LogManager} *log_manager 日志管理器指针 52 | */ 53 | void TransactionManager::abort(Transaction * txn, LogManager *log_manager) { 54 | // Todo: 55 | // 1. 回滚所有写操作 56 | // 2. 释放所有锁 57 | // 3. 清空事务相关资源,eg.锁集 58 | // 4. 把事务日志刷入磁盘中 59 | // 5. 更新事务状态 60 | 61 | } -------------------------------------------------------------------------------- /src/transaction/transaction_manager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Renmin University of China 2 | RMDB is licensed under Mulan PSL v2. 3 | You can use this software according to the terms and conditions of the Mulan PSL v2. 4 | You may obtain a copy of Mulan PSL v2 at: 5 | http://license.coscl.org.cn/MulanPSL2 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 9 | See the Mulan PSL v2 for more details. */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | #include "transaction.h" 17 | #include "recovery/log_manager.h" 18 | #include "concurrency/lock_manager.h" 19 | #include "system/sm_manager.h" 20 | 21 | /* 系统采用的并发控制算法,当前题目中要求两阶段封锁并发控制算法 */ 22 | enum class ConcurrencyMode { TWO_PHASE_LOCKING = 0, BASIC_TO }; 23 | 24 | class TransactionManager{ 25 | public: 26 | explicit TransactionManager(LockManager *lock_manager, SmManager *sm_manager, 27 | ConcurrencyMode concurrency_mode = ConcurrencyMode::TWO_PHASE_LOCKING) { 28 | sm_manager_ = sm_manager; 29 | lock_manager_ = lock_manager; 30 | concurrency_mode_ = concurrency_mode; 31 | } 32 | 33 | ~TransactionManager() = default; 34 | 35 | Transaction* begin(Transaction* txn, LogManager* log_manager); 36 | 37 | void commit(Transaction* txn, LogManager* log_manager); 38 | 39 | void abort(Transaction* txn, LogManager* log_manager); 40 | 41 | ConcurrencyMode get_concurrency_mode() { return concurrency_mode_; } 42 | 43 | void set_concurrency_mode(ConcurrencyMode concurrency_mode) { concurrency_mode_ = concurrency_mode; } 44 | 45 | LockManager* get_lock_manager() { return lock_manager_; } 46 | 47 | /** 48 | * @description: 获取事务ID为txn_id的事务对象 49 | * @return {Transaction*} 事务对象的指针 50 | * @param {txn_id_t} txn_id 事务ID 51 | */ 52 | Transaction* get_transaction(txn_id_t txn_id) { 53 | if(txn_id == INVALID_TXN_ID) return nullptr; 54 | 55 | std::unique_lock lock(latch_); 56 | assert(TransactionManager::txn_map.find(txn_id) != TransactionManager::txn_map.end()); 57 | auto *res = TransactionManager::txn_map[txn_id]; 58 | lock.unlock(); 59 | assert(res != nullptr); 60 | assert(res->get_thread_id() == std::this_thread::get_id()); 61 | 62 | return res; 63 | } 64 | 65 | static std::unordered_map txn_map; // 全局事务表,存放事务ID与事务对象的映射关系 66 | 67 | private: 68 | ConcurrencyMode concurrency_mode_; // 事务使用的并发控制算法,目前只需要考虑2PL 69 | std::atomic next_txn_id_{0}; // 用于分发事务ID 70 | std::atomic next_timestamp_{0}; // 用于分发事务时间戳 71 | std::mutex latch_; // 用于txn_map的并发 72 | SmManager *sm_manager_; 73 | LockManager *lock_manager_; 74 | }; --------------------------------------------------------------------------------