├── .gitignore ├── CMakeLists.txt ├── doc ├── main.tex ├── structure.png └── structure.puml ├── readme.md ├── src ├── CMakeLists.txt ├── Constants.h ├── fileio │ ├── BufPageManager.cpp │ ├── BufPageManager.h │ ├── CMakeLists.txt │ ├── FileManager.cpp │ ├── FileManager.h │ ├── FileTable.h │ └── FindReplace.h ├── ix │ ├── CMakeLists.txt │ ├── ix.h │ ├── ix_error.cc │ ├── ix_indexhandle.cc │ ├── ix_indexscan.cc │ ├── ix_internal.cc │ ├── ix_internal.h │ ├── ix_manager.cc │ └── ix_test.cc ├── main.cpp ├── parser │ ├── CMakeLists.txt │ ├── Expr.cpp │ ├── Expr.h │ ├── Tree.cpp │ ├── Tree.h │ ├── lex.l │ └── parser.y ├── pf │ ├── CMakeLists.txt │ ├── pf.h │ ├── pf_buffermgr.cc │ ├── pf_buffermgr.h │ ├── pf_error.cc │ ├── pf_filehandle.cc │ ├── pf_hashtable.cc │ ├── pf_hashtable.h │ ├── pf_internal.h │ ├── pf_manager.cc │ ├── pf_pagehandle.cc │ └── pf_statistics.cc ├── ql │ ├── Aggregation.cpp │ ├── Aggregation.h │ ├── CMakeLists.txt │ ├── QueryManager.cpp │ ├── QueryManager.h │ ├── Table.cpp │ └── Table.h ├── rm │ ├── CMakeLists.txt │ ├── HeaderPage.h │ ├── RID.h │ ├── RM_FileHandle.cpp │ ├── RM_FileHandle.h │ ├── RM_FileScan.cpp │ ├── RM_FileScan.h │ ├── RM_Record.h │ ├── RecordManager.cpp │ └── RecordManager.h ├── sm │ ├── CMakeLists.txt │ ├── sm.h │ ├── sm_error.cc │ ├── sm_internal.cc │ ├── sm_internal.h │ └── sm_manager.cc └── utils │ ├── CMakeLists.txt │ ├── Date.cpp │ ├── Date.h │ ├── FuncTemplate.cpp │ ├── FuncTemplate.h │ ├── MyBitMap.cpp │ ├── MyBitMap.h │ ├── MyHashMap.h │ ├── MyLinkList.h │ ├── compare.h │ ├── pagedef.h │ └── printerr.h └── test ├── init.sh ├── large_dataset ├── .DS_Store ├── create.sql ├── customer.sql ├── food.sql ├── orders.sql └── restaurant.sql ├── small_dataset ├── create.sql ├── test_insert.sql └── test_select.sql ├── test.sql ├── test_delete.sql ├── test_insert.sql ├── test_select.sql └── test_update.sql /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build/ 3 | doc/main.pdf 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(MyDB) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | add_subdirectory(src) 7 | -------------------------------------------------------------------------------- /doc/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt,UTF8]{report} 2 | 3 | \usepackage{ctex} 4 | 5 | \usepackage{listings} 6 | \usepackage{xcolor} 7 | 8 | \usepackage{graphicx} 9 | \usepackage{amsmath} 10 | 11 | \usepackage[colorlinks, linkcolor=red, anchorcolor=blue, citecolor=green]{hyperref} 12 | 13 | \lstset{ 14 | basicstyle=\tt, 15 | %行号 16 | % numbers=left, 17 | % rulesepcolor=\color{red!20!green!20!blue!20}, 18 | % escapeinside=``, 19 | % xleftmargin=2em,xrightmargin=2em, aboveskip=1em, 20 | %背景框 21 | % framexleftmargin=1.5mm, 22 | % frame=shadowbox, 23 | %背景色 24 | % backgroundcolor=\color[RGB]{245,245,244}, 25 | %样式 26 | keywordstyle=\color{blue}\bfseries, 27 | identifierstyle=\bf, 28 | numberstyle=\color[RGB]{0,192,192}, 29 | commentstyle=\it\color[RGB]{96,96,96}, 30 | stringstyle=\rmfamily\slshape\color[RGB]{128,0,0}, 31 | %显示空格 32 | showstringspaces=false 33 | } 34 | 35 | \renewcommand\thesection{\arabic{section}} 36 | 37 | \title{数据库系统概论大作业报告} 38 | \author{杜政晓 2016011014\\ 39 | 吴一凡 2016011269} 40 | \begin{document} 41 | \maketitle 42 | 43 | \section{系统概述} 44 | Github地址\url{https://github.com/duzx16/MyDB} 45 | \subsection{必做功能} 46 | \begin{itemize} 47 | \item 基于页式文件的记录管理 48 | \item \emph{基于页式文件}的B+树索引 49 | \item 数据库,关系表,索引的创建和删除 50 | \item 关系表中数据的增删查改 51 | \end{itemize} 52 | 53 | \subsection{选做功能} 54 | \begin{itemize} 55 | \item 属性域约束(check)和外键约束 56 | \item 支持Date数据类型(支持对非法日期的检查,对两个日期的比较和对日期列的索引) 57 | \item 三个或以上表的连接,并支持使用索引加速 58 | \item 聚集查询函数AVG, SUM, MIN, MAX和分组查询GROUP BY 59 | \item 模糊查询,基于LIKE和"\%","\_"的通配查询 60 | \item 更灵活的查询语句,支持使用任意的逻辑表达式作为查询条件(详见Parser部分\ref{subsec:parser}) 61 | \end{itemize} 62 | 63 | 本数据库在检查中已经通过了基本功能测试和\textbf{全部的}扩展功能测试。 64 | 65 | \section{系统架构设计} 66 | \begin{figure}[!ht] 67 | \centering 68 | \includegraphics[width=0.9\textwidth]{structure} 69 | \caption{系统架构设计图} 70 | \label{fig:structure} 71 | \end{figure} 72 | 73 | 整个数据库系统的架构设计和CS346以及《数据库大作业详细说明》中一样。最底层是一个页式文件管理系统,负责以页为单位进行磁盘文件的读取,写入和缓存。建立在其上的是记录管理系统(负责管理数据表中的记录的创建,插入,删除,修改)和索引管理系统(负责管理数据表中属性索引的创建,插入,删除)。然后用户的指令由Parser解读,其中对数据表和索引的创建、删除的指令由系统管理模块处理,对数据的增删查改由查询处理模块处理。具体的架构图见图\ref{fig:structure}. 74 | 75 | \section{各模块详细设计} 76 | \subsection{记录管理模块} 77 | \subsubsection{文件结构设计} 78 | 每个文件只存储同一类长度相等的记录。文件由页(Page)组成,,页由大小等于记录长度的槽(Slot)组成。 79 | 每个文件的第一页作为文件头,保存了文件的一些元信息,比如记录长度、文件中的页数目、文件中的记录数目等。 80 | 文件中每一页的开头保存了该页的一些基本信息。比如该页的槽哪些是空的哪些是非空的。这里使用了Bitmap来表示槽的空与非空。此外,为了能够在插入记录的时候能够快速找到有空槽的页,使用了一个链表来存储有空槽的页。首页中保存了链表中的第一项的页码。每一页的开头4个字节指向的是链表中下一项的页码。比起在首页中用Bitmap来表示页是否非空,这种方式能够存储的页数是没有限制的。 81 | 82 | \subsubsection{模块具体实现} 83 | 84 | 因为在使用提供的页式文件管理系统的时候出现了奇怪的bug(具体表现是缓存的hash表有时候会突然“丢掉”某个已经打开的页,导致该页对应的页数变成了一个随机数)。而且该文件系统的功能不全(比如没有将某个页固定在缓存中不要换出的功能,在关闭文件的时候也不会写回它的所有页),所以我换用了CS346中提供的页式文件管理系统。 85 | RecordManager的创建、删除文件都使用文件管理系统的接口。打开文件除了调用接口外,还会将文件的首页中的信息复制出来,并设置modified为false。如果后续操作中改变了首页信息则将modified设置为true。当关闭文件的时候,如果发现modified为true,则将复制出来的信息写回文件的首页。 86 | RM\_FileHandle负责对文件中的记录进行管理。插入记录时,从首页中找到有空槽的页,然后进行插入。如果发现插入之后该页已经满了,则将首页上有空槽的页码换为链表中的下一项。在删除记录的时候,只需要将Bitmap的对应位设置为空,然后将该页的页码插入到链表的开头。 87 | 对文件的扫描由RM\_FileScan进行,其方法就是依次遍历每一页,每一页中遍历Bitmap中的每一个非空位对应的槽。 88 | 89 | \subsection{索引管理模块} 90 | 91 | 在CS346的模块设计中,索引管理模块维护的是属性值的指针以及该属性值对应的记录在记录文件中的RID的二元组。这样做主要意在实现记录管理模块及索引管理模块的分离。但如果按照这种设计的话,要将所有插入进来的属性值在索引文件中再存一遍。这样做不仅效率较低,也不方便实现,还会浪费空间。因此决定将索引管理模块的接口改为仅维护属性值对应的记录在记录文件中的RID,也就是说只要提供RID就可以进行插入和删除。但为了知道属性值的类型以及从记录文件中获取属性值的指针,在打开索引时还需要提供一个已经打开的RM\_FileHandle以及该属性值开头在所有属性值中的偏移量。 92 | 93 | 于是索引管理模块的功能为,在一个已经打开的记录文件以及属性的偏移量已知的情况下,可以基于RID来插入/删除索引,也可以给定一个条件(目前支持$<,\le,>,\ge,=,\neq,\text{NO\_OP}$这几种运算符)以及比较值来对所有符合条件的属性值对应的RID进行遍历。 94 | 95 | \subsubsection{文件结构设计} 96 | 97 | 将整个索引仅存在一个索引文件中,其中第一页存的是描述属性类型、属性字节数、B+树根节点的页码的索引信息。剩下的所有页要么存的是包含内部节点及叶子节点的B+树节点,要么存的是属性值同为某一个值的所有RID。由于在实现中将这些RID都只存在一页中,所以属性值相同的RID数目不能过多。 98 | \subsubsection{模块具体实现} 99 | 100 | 索引管理模块使用B+树对索引进行维护。对于B+树的讨论在这里就不再赘述了。值得注意的是为了实现简便,在对索引进行删除时并未进行下溢的节点合并处理。虽然在理论上可能有些小问题,但经过测试,这样的实现也是可以接受的。 101 | 102 | 在进行遍历时,首先找到B+树中最小的$\geq$比较值的属性值对应的RID的位置,随后根据运算符向左或者向右一步一步找到所有符合要求的RID。注意这里将所有符合要求的RID都预先存了下来,因此在遍历过程中插入或删除索引并不会影响索引的遍历。但由于索引是基于记录的,若要同时修改记录与索引,应先修改索引,再修改记录。 103 | 104 | \subsubsection{测试程序} 105 | 106 | 为了验证该模块的正确性,实现了一个ix\_test.cc来进行测试,如果不对的话就会assert退出。 107 | \subsection{系统管理模块} 108 | 109 | 系统管理模块作为页式文件系统、记录管理模块、索引管理模块的客户端,负责调用上述三个模块处理SQL解析器解析到的部分命令。这些命令有: 110 | 111 | \begin{itemize} 112 | \item 创建/删除数据库 113 | \item 打开/关闭数据库 114 | \item 创建/删除表 115 | \item 创建/删除索引 116 | \item 打印(所有)表信息 117 | \end{itemize} 118 | \subsubsection{文件结构设计} 119 | 120 | 每个数据库都对应一个与数据库名字相同的数据库主程序目录下的子文件夹,该数据库的所有信息都存在这个子文件夹下,其中包含一个TableList文件存数据库的所有表的名字,且对于每个表存表的信息文件(包括表的所有属性及约束,每个属性是否被索引)、表的记录文件、表的索引文件(每个属性的索引一个文件)。 121 | \subsubsection{模块具体实现} 122 | 123 | 创建/删除数据库实际上就是创建/删除文件夹,打开/关闭数据库实际上就是进入/退出子文件夹。创建表时我们需要将表的名字插入TableList文件,同时新建并修改表的信息文件,将表的属性与约束写入表的信息文件;删除表时则删除所有跟表有关的文件,并在TableList中删除表名。创建索引时调用索引管理模块的创建索引功能,并且打开记录文件遍历所有的RID并依次调用索引管理模块的插入索引功能进行插入,并将该属性修改为被索引;删除索引则正好相反。打印表信息只需打开表的信息文件并输出即可。 124 | \subsection{SQL解析器} 125 | \label{subsec:parser} 126 | Parser使用flex + bison (Yacc的升级版)实现, 其语法大致与本课程给出的Parser语法文件相同。但是进行了以下拓展: 127 | \begin{itemize} 128 | \item 在create table的时候, 除了主键和外键, 还可以指定属性域约束check in, 此外主键约束可以用多个域连接作为主键。 129 | \item select语句支持group by关键词, 以及SUM, AVG, MIN, MAX等函数。 130 | \item insert语句支持指定插入哪些列(未指定的列自动视为null)。 131 | \item where子句中支持任意的涉及四则运算的表达式, 只要最终结果是布尔类型(原语法只支持用and连接的column comp value的比较)。 增加like比较运算。 update的set子句的右半部分也可以是任意类型兼容的四则运算表达式。 132 | \end{itemize} 133 | 134 | Parser在完成解析之后会根据语句生成对应的类, 然后调用相应的模块进行执行。 135 | 136 | \subsection{查询处理模块} 137 | 查询解析模块是整个数据库中最重要也是最复杂的一个部分, 用户在使用的数据库的时候主要都是在和查询解析模块打交道。查询解析模块主要有三个类:Expr,Table和QueryManage。 138 | 139 | \subsubsection{Expr} 140 | 因为我们支持的表达式比较复杂,所以我们单独设计了Expr类,表示由代数运算(+-*/), 比较(<>=), 逻辑运算(AND OR)组成,以常量或者是表的列为叶子的抽象语法树. 其类型包括代数表达式, 比较表达式, 逻辑表达式, 常量, 关系表属性. 支持以下功能: 141 | \begin{itemize} 142 | \item 绑定到关系表的某项属性, 给定数据之后自底向上进行计算。 143 | \item 逻辑运算支持短路操作, 对于某些where子句可以提前计算出结果, 减少遍历的时间 144 | \item 进行必要的类型兼容检查, 并在必要时将整型数据向浮点类型提升 145 | \item 通过重载运算符在两个Expr之间进行简单的运算和比较(主要是方便聚合函数) 146 | \item 按照一定的规则转化为格式化的字符串。其中对于INT类型的属性,可以指定长度。 147 | \item 提供后序遍历的接口来扩展 148 | \item \textbf{支持字符串的like匹配:} 为扩展功能中的like关键字提供匹配。实现方法是将SQL语句中的"\_"和"\%"用正则表达式中的"\."和"\.\*"来替换,然后调用C++11中的regex模块进行正则表达式的匹配。 149 | \item \textbf{支持字符串向Date的转化:} 将一个xxxx-xx-xx类型的字符串转化为一个4字节整数,便于在数据库中存储。并进行日期合法性的检查。 150 | \end{itemize} 151 | 152 | \subsubsection{Table} 153 | Table模块在《数据库大作业详细说明》中给出的数据库架构是不存在的. 但是考虑到其他三个模块中都没有关系表这个概念, 而查询解析模块的四大功能中又要用到很多共同的功能, 所以在程序中加入了数据库中关系表对应的类Table. 通过调用其他三个模块来提供以下功能: 154 | \begin{itemize} 155 | \item 管理一个关系表相关的记录文件和索引文件handler, 避免重复打开和关闭。同时在析构的时候自动释放申请的资源。 156 | \item 查询关系表的属性域的信息, 如长度, 偏移量, 约束等。 157 | \item \textbf{检查记录是否符合关系表上的约束:} 其中,主键约束和外键约束通过对应属性的索引来检查当前值是否已经存在(因为主键一定有索引,外键一定是其他表的主键,所以不用担心索引未创建)。属性域约束只需要检查当前值是否等于给定的常量列表中的某一个值。 158 | \item 将insert的常量转化为连续的数据, 并进行约束检查。如果约束检查通过,则作为记录插入,并将属性值插入到已经创建的索引中。 159 | \item 根据set子句更新给定的记录,并进行约束检查。如果约束检查通过,则更新记录文件,并更新对应属性的索引(通过先删除再插入的方式)。 160 | \end{itemize} 161 | 162 | \subsubsection{QueryManager} 163 | \subsubsection{表的遍历} 164 | \label{subsubsec:iterate} 165 | 查询处理模块首先提供了一个基础的方法iterateTables, 用于提供对一个关系表或多个关系表的连接进行有筛选的遍历的接口, 通过传入一个回调函数来对记录进行操作。 166 | 167 | 这个函数有两个版本, 一个是对于最常用的单表遍历(update, delete都只会调用这个函数). 对于单表遍历来说, 利用索引的方式是找到那些where子句中必须满足的比较条件, 并且左边是某个有索引的属性域, 右边是一个常量, 然后就可以用索引模块提供的接口来直接查找符合这一条件的记录。如果找不到这样的比较条件,则直接全部遍历。将遍历到的记录的数据用Expr的calculate功能算出条件表达式的值,完成筛选。 168 | 169 | 另一个是对于任意多的表连接进行遍历(select可能会调用这个函数). 利用索引的方式同样是找到where子句中必须满足的比较条件, 左边是某个有索引的属性域, 右边的表达式不含有跟左边同一个关系表的属性, 并且出现的关系表尽可能少(常量是最好的情况, 不出现任何关系表). 然后在安排局部的遍历顺序的时候, 把出现在右边的关系表都安排在外层, 然后到了遍历左边的关系表的时候就可以利用其索引. 当然这种方法其实是一种贪心的方法, 一旦表的数量较多, 并不能保证效率是最高的. 不过考虑到我们只是一个教学用的数据库, 不会进行太多表的连接, 所以也可以满足要求了. 170 | 171 | \subsubsection{插入,修改与删除} 172 | 在四大功能中, insert不需要遍历, 直接调用Table的接口向记录文件和索引文件中插入就可以了。update和delete都只需要单表遍历, update和delete都首先将遍历到的记录的RID储存起来(避免在遍历表的过程中修改索引的结构),遍历结束后再统一调用Table的接口进行修改或删除。 173 | 174 | \subsubsection{聚集函数} 175 | 为了便于聚集函数的查询,我们实现了Aggregation类来实现聚集函数的功能。其提供的接口accumulate(const std::string \&group, const Expr \&value)表示遇到一个新的值。对于非group查询来说,直接统计对应的统计量(AVG, SUM, MIN, MAX),对于group查询来说,先将group attribute统一转化成字符串,然后利用C++的std::map来实现从group attribute到对应统计量的映射。 176 | 177 | \section{主要接口说明} 178 | \subsection{记录管理模块} 179 | 记录管理模块的核心是类RecordManager,用于实现记录管理中文件级别的操作,其接口包括: 180 | \begin{lstlisting}[language=C++] 181 | // Create a file with filename and record size 182 | int createFile (std::string filename, unsigned record_size); 183 | // Delete a file with filename 184 | int destroyFile(std::string filename); 185 | // Open a file with filename in file_handle 186 | int openFile(std::string filename, RM_FileHandle &file_handle); 187 | // Close the file opened in file_handle 188 | int closeFile(RM_FileHandle &file_handle); 189 | ​\end{lstlisting} 190 | 191 | 通过RecordManager打开的文件会返回一个RM\_FileHandle,用于实现对于单个文件中记录的操作 192 | \begin{lstlisting}[language=C++] 193 | // Get a record by rid 194 | int getRec(const RID &rid, RM_Record & record) const; 195 | // Insert a new record and return the rid 196 | RID insertRec(const char *pData); 197 | // Delete a record by rid 198 | int deleteRec(const RID &rid); 199 | // Update a record 200 | int updateRec(const RM_Record &rec); 201 | \end{lstlisting} 202 | 203 | 此外还有一个类RM\_FileScan用来进行对文件中记录的查询 204 | \begin{lstlisting}[language=C++] 205 | // Initialize file scan 206 | int openScan(const RM_FileHandle &fileHandle, 207 | AttrType attrType, 208 | int attrLength, 209 | int attrOffset, 210 | CompOp compOp, 211 | void *value 212 | ); 213 | // Get next matching record 214 | int getNextRec(RM_Record &rec); 215 | // Terminate file scan 216 | int closeScan(); 217 | \end{lstlisting} 218 | 219 | 注意在记录管理模块中我们只实现了对记录中单个属性值进行限制的查询。 220 | \subsection{索引管理模块} 221 | IX\_Manager类可以创建/删除索引,并可以在给定一个打开的RM\_FileHandle以及属性偏移量的情况下打开IX\_IndexHandle类型的索引,也可以关闭索引。 222 | \begin{lstlisting}[language=C++] 223 | // Create new index 224 | RC CreateIndex (const char *fileName, 225 | int indexNo, 226 | AttrType attrType, 227 | int attrLength); 228 | // Destroy index 229 | RC DestroyIndex (const char *fileName, 230 | int indexNo); 231 | // Open index 232 | RC OpenIndex (const char *fileName, 233 | int indexNo, 234 | IX_IndexHandle &indexHandle, 235 | RM_FileHandle &rmFileHandle, 236 | int _attrOffset); 237 | // Close index 238 | RC CloseIndex (IX_IndexHandle &indexHandle); 239 | \end{lstlisting} 240 | IX\_IndexScan类可以在给定一个打开的IX\_IndexHandle以及比较运算符与比较值的情况下打开,并通过GetNext对所有符合条件的RID进行遍历。 241 | \begin{lstlisting}[language=C++] 242 | // Initialize index scan 243 | RC OpenScan(IX_IndexHandle &indexHandle, 244 | CompOp compOp, 245 | const void *value, 246 | ClientHint pinHint = ClientHint::NO_HINT); 247 | // Get next matching entry 248 | RC GetNextEntry(RID &rid); 249 | // Terminate index scan 250 | RC CloseScan(); 251 | \end{lstlisting} 252 | \subsection{系统管理模块} 253 | 系统管理模块SM\_Manager暴露给调用者parser的接口有: 254 | \begin{lstlisting}[language=C++] 255 | RC CreateDb(const char *dbName); 256 | RC DropDb(const char *dbName); 257 | RC OpenDb(const char *dbName); 258 | RC CloseDb(); 259 | RC CreateTable(const char *tableName, 260 | ColumnDecsList *columns, 261 | TableConstraintList *tableConstraints); 262 | RC GetTableInfo(const char *tableName, 263 | ColumnDecsList &columns, 264 | TableConstraintList &tableConstraints); 265 | RC DropTable(const char *relName); 266 | RC CreateIndex(const char *relName, const char *attrName); 267 | RC DropIndex(const char *relName, const char *attrName); 268 | // Help for database 269 | RC Help(); 270 | // Help for relation 271 | RC Help(const char *relName); 272 | \end{lstlisting} 273 | \subsection{查询处理模块} 274 | 查询处理模块对外的类只有QueryManager,其public方法分别对应于数据库的增删查改 275 | \begin{lstlisting}[language=C++] 276 | // select attributes on relations 277 | // with (possibly empty) where condition and group attributes 278 | int exeSelect(AttributeList *attributes, IdentList *relations, 279 | Expr *whereClause, const std::string &groupAttrName); 280 | // insert into relation table(可以用columnList指定要插入的列) 281 | int exeInsert(std::string relation, IdentList *columnList, 282 | ConstValueLists *insertValueTree); 283 | // update relation table as setClauses with where condition 284 | int exeUpdate(std::string relation, SetClauseList *setClauses, 285 | Expr *whereClause); 286 | // delete the data in relation table with where condition 287 | int exeDelete(std::string relation, Expr *whereClause); 288 | 289 | \end{lstlisting} 290 | 291 | 292 | \section{实验结果} 293 | 程序的编译运行方法见readme.md。在本课程的检查中我们的数据库通过了基本功能检查和\textbf{全部的}扩展功能检查。 294 | 295 | 此外,基于本课程给出的dataset\_small,我们编写了4组测试语句。位于test文件夹下。从运行结果中可以看出我们的数据库完成了Section 1中描述的功能,且能在较短的时间内完成多表遍历等复杂的操作。 296 | 297 | \section{小组分工} 298 | \begin{itemize} 299 | \item 杜政晓:记录管理模块,查询处理模块,SQL parser 300 | \item 吴一凡:索引管理模块,系统管理模块 301 | \end{itemize} 302 | 303 | \section{参考文献} 304 | \begin{itemize} 305 | \item 《数据库大作业详细说明》 306 | \item CS346 RedBase Project \url{https://web.stanford.edu/class/cs346/2015/redbase.html} 307 | \item SimpleDB by Harry Chen \url{https://github.com/Harry-Chen/SimpleDB} 308 | \item database by 马也 \url{https://git.net9.org/maye9999/database} 309 | \item STX B+ Tree Template \url{https://panthema.net/2007/stx-btree} 310 | \end{itemize} 311 | \end{document} 312 | -------------------------------------------------------------------------------- /doc/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duzx16/MyDB/7e355398e8be089e2674fbad0fb12211432a22dd/doc/structure.png -------------------------------------------------------------------------------- /doc/structure.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | component User 4 | component Parser 5 | component QueryManager as qm 6 | component SM_Manager as sm 7 | component IX_Manager as ix 8 | component RecordManager as rm 9 | component PF_Manager as pf 10 | database Disk 11 | component Memory 12 | 13 | User --> Parser: 命令 14 | 15 | Parser --> sm: 创建删除 16 | Parser --> qm: 增删查改 17 | 18 | 19 | qm --> ix: 索引扫描,插入,删除 20 | qm --> rm: 记录读写 21 | 22 | ix --> pf: 文件IO 23 | rm --> pf: 文件IO 24 | 25 | ix .> rm: 记录访问 26 | 27 | sm --> rm: 创建删除记录文件 28 | sm --> ix: 创建删除索引文件 29 | 30 | pf --> Disk 31 | pf -> Memory 32 | 33 | @enduml -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # MyDB 2 | This is the project for the Database Course by Zhengxiao Du and Yifan Wu, Fall 2018, Tsinghua University 3 | 4 | ## Dependencies 5 | * bison and flex 6 | * CMake >= 3.12 7 | * gcc 8 | 9 | ## Environment 10 | Tested on MacOS Mojave 10.14.2, with Apple LLVM version 10.0.0. 11 | 12 | ## Build 13 | ```bash 14 | mkdir build && cd build 15 | cmake .. && make 16 | cd src # the executive name is 'MyDB' 17 | ``` -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(utils) 2 | add_subdirectory(pf) 3 | add_subdirectory(fileio) 4 | add_subdirectory(rm) 5 | add_subdirectory(ix) 6 | add_subdirectory(parser) 7 | add_subdirectory(sm) 8 | add_subdirectory(ql) 9 | 10 | add_definitions(-DDEBUG) 11 | add_definitions("-Wall -g") 12 | 13 | add_library(${CMAKE_PROJECT_NAME}_lib ${SOURCE} ${HEADERS}) 14 | add_executable(${CMAKE_PROJECT_NAME} main.cpp) 15 | target_link_libraries(${CMAKE_PROJECT_NAME} SqlParser ${CMAKE_PROJECT_NAME}_lib) 16 | -------------------------------------------------------------------------------- /src/Constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_CONSTANTS_H 6 | #define MYDB_CONSTANTS_H 7 | 8 | #include 9 | 10 | /* 11 | * 一个页面中的字节数 12 | */ 13 | typedef unsigned int *BufType; 14 | enum class AttrType { 15 | INT = 0, FLOAT, STRING, DATE, VARCHAR, BOOL, NO_ATTR 16 | }; 17 | 18 | enum class CompOp { 19 | EQ_OP = 0, LT_OP, GT_OP, LE_OP, GE_OP, NE_OP, IS_OP, ISNOT_OP, LIKE_OP, NO_OP 20 | }; 21 | enum class ArithOp { 22 | ADD_OP = 0, SUB_OP, MUL_OP, DIV_OP, MINUS_OP, NO_OP 23 | }; 24 | enum class LogicOp { 25 | AND_OP = 0, OR_OP, NOT_OP, NO_OP 26 | }; 27 | 28 | enum class ClientHint { 29 | NO_HINT 30 | }; 31 | enum class AggregationType { 32 | T_NONE = 0, 33 | T_AVG, 34 | T_SUM, 35 | T_MIN, 36 | T_MAX 37 | }; 38 | 39 | enum class ConstraintType { 40 | PRIMARY_CONSTRAINT, 41 | FOREIGN_CONSTRAINT, 42 | CHECK_CONSTRAINT 43 | }; 44 | 45 | #define MAX_NAME 25 46 | #define MAX_ATTRS 10 47 | #define MAX_STRING_LEN 256 48 | #define COLUMN_FLAG_NOTNULL 0x1 49 | 50 | struct AttrInfo { 51 | char attrName[MAX_NAME + 1]; 52 | int attrSize; 53 | int attrLength; 54 | AttrType attrType; 55 | int columnFlag; 56 | }; 57 | 58 | 59 | // 60 | // redbase.h 61 | // global declarations 62 | // 63 | #ifndef REDBASE_H 64 | #define REDBASE_H 65 | 66 | // 67 | // Return codes 68 | // 69 | typedef int RC; 70 | 71 | #define OK_RC 0 // OK_RC return code is guaranteed to always be 0 72 | 73 | #define START_PF_ERR (-1) 74 | #define END_PF_ERR (-100) 75 | #define START_RM_ERR (-101) 76 | #define END_RM_ERR (-200) 77 | #define START_IX_ERR (-201) 78 | #define END_IX_ERR (-300) 79 | #define START_SM_ERR (-301) 80 | #define END_SM_ERR (-400) 81 | #define START_QL_ERR (-401) 82 | #define END_QL_ERR (-500) 83 | 84 | #define START_PF_WARN 1 85 | #define END_PF_WARN 100 86 | #define START_RM_WARN 101 87 | #define END_RM_WARN 200 88 | #define START_IX_WARN 201 89 | #define END_IX_WARN 300 90 | #define START_SM_WARN 301 91 | #define END_SM_WARN 400 92 | #define START_QL_WARN 401 93 | #define END_QL_WARN 500 94 | 95 | // ALL_PAGES is defined and used by the ForcePages method defined in RM 96 | // and PF layers 97 | const int ALL_PAGES = -1; 98 | 99 | // 100 | // TRUE, FALSE and BOOLEAN 101 | // 102 | #ifndef BOOLEAN 103 | typedef char Boolean; 104 | #endif 105 | 106 | #ifndef FALSE 107 | #define FALSE 0 108 | #endif 109 | 110 | #ifndef TRUE 111 | #define TRUE 1 112 | #endif 113 | 114 | #ifndef NULL 115 | #define NULL 0 116 | #endif 117 | 118 | #endif 119 | 120 | #endif //MYDB_CONSTANTS_H 121 | -------------------------------------------------------------------------------- /src/fileio/BufPageManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/29. 3 | // 4 | 5 | #include "../utils/MyHashMap.h" 6 | #include "FindReplace.h" 7 | #include "../utils/pagedef.h" 8 | #include "../utils/MyLinkList.h" 9 | #include "BufPageManager.h" 10 | 11 | BufType BufPageManager::allocMem() 12 | { 13 | return new unsigned int[(PAGE_SIZE >> 2)]; 14 | } 15 | 16 | BufType BufPageManager::fetchPage(int typeID, int pageID, int &index) 17 | { 18 | BufType b; 19 | index = replace->find(); 20 | b = addr[index]; 21 | if (b == NULL) 22 | { 23 | b = allocMem(); 24 | addr[index] = b; 25 | } else 26 | { 27 | if (dirty[index]) 28 | { 29 | int k1, k2; 30 | hash->getKeys(index, k1, k2); 31 | fileManager->writePage(k1, k2, b, 0); 32 | dirty[index] = false; 33 | } 34 | } 35 | hash->replace(index, typeID, pageID); 36 | return b; 37 | } 38 | 39 | BufPageManager::BufPageManager(FileManager *fm) 40 | { 41 | int c = CAP; 42 | int m = MOD; 43 | last = -1; 44 | fileManager = fm; 45 | //bpl = new MyLinkList(CAP, MAX_FILE_NUM); 46 | dirty = new bool[CAP]; 47 | addr = new BufType[CAP]; 48 | hash = new MyHashMap(c, m); 49 | replace = new FindReplace(c); 50 | for (int i = 0; i < CAP; ++i) 51 | { 52 | dirty[i] = false; 53 | addr[i] = NULL; 54 | } 55 | } 56 | 57 | BufType BufPageManager::allocPage(int fileID, int pageID, int &index, bool ifRead) 58 | { 59 | BufType b = fetchPage(fileID, pageID, index); 60 | if (ifRead) 61 | { 62 | fileManager->readPage(fileID, pageID, b, 0); 63 | } 64 | return b; 65 | } 66 | 67 | BufType BufPageManager::getPage(int fileID, int pageID, int &index) 68 | { 69 | index = hash->findIndex(fileID, pageID); 70 | if (index != -1) 71 | { 72 | access(index); 73 | return addr[index]; 74 | } else 75 | { 76 | BufType b = fetchPage(fileID, pageID, index); 77 | fileManager->readPage(fileID, pageID, b, 0); 78 | return b; 79 | } 80 | } 81 | 82 | void BufPageManager::access(int index) 83 | { 84 | if (index == last) 85 | { 86 | return; 87 | } 88 | replace->access(index); 89 | last = index; 90 | } 91 | 92 | void BufPageManager::markDirty(int index) 93 | { 94 | dirty[index] = true; 95 | access(index); 96 | } 97 | 98 | void BufPageManager::release(int index) 99 | { 100 | dirty[index] = false; 101 | replace->free(index); 102 | hash->remove(index); 103 | } 104 | 105 | void BufPageManager::writeBack(int index) 106 | { 107 | if (dirty[index]) 108 | { 109 | int f, p; 110 | hash->getKeys(index, f, p); 111 | fileManager->writePage(f, p, addr[index], 0); 112 | dirty[index] = false; 113 | } 114 | replace->free(index); 115 | hash->remove(index); 116 | } 117 | 118 | void BufPageManager::close() 119 | { 120 | for (int i = 0; i < CAP; ++i) 121 | { 122 | writeBack(i); 123 | } 124 | } 125 | 126 | void BufPageManager::getKey(int index, int &fileID, int &pageID) 127 | { 128 | hash->getKeys(index, fileID, pageID); 129 | } 130 | 131 | BufPageManager &BufPageManager::getInstance() 132 | { 133 | static BufPageManager instance = BufPageManager(&FileManager::getInstance()); 134 | return instance; 135 | } 136 | 137 | BufPageManager::~BufPageManager() 138 | { 139 | close(); 140 | } 141 | 142 | void BufPageManager::closeFile(int fileID) 143 | { 144 | int file, page; 145 | for (int i = 0; i < CAP; ++i) 146 | { 147 | getKey(i, file, page); 148 | if (file == fileID) 149 | { 150 | writeBack(i); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/fileio/BufPageManager.h: -------------------------------------------------------------------------------- 1 | #ifndef BUF_PAGE_MANAGER 2 | #define BUF_PAGE_MANAGER 3 | 4 | #include "FileManager.h" 5 | 6 | class MyHashMap; 7 | class FindReplace; 8 | 9 | /* 10 | * BufPageManager 11 | * 实现了一个缓存的管理器 12 | */ 13 | struct BufPageManager { 14 | public: 15 | int last; 16 | FileManager *fileManager; 17 | MyHashMap *hash; 18 | FindReplace *replace; 19 | //MyLinkList* bpl; 20 | bool *dirty; 21 | /* 22 | * 缓存页面数组 23 | */ 24 | BufType *addr; 25 | 26 | BufType allocMem(); 27 | 28 | BufType fetchPage(int typeID, int pageID, int &index); 29 | 30 | /* 31 | * 构造函数 32 | * @参数fm:文件管理器,缓存管理器需要利用文件管理器与磁盘进行交互 33 | */ 34 | explicit BufPageManager(FileManager *fm); 35 | 36 | public: 37 | /* 38 | * @函数名allocPage 39 | * @参数fileID:文件id,数据库程序在运行时,用文件id来区分正在打开的不同的文件 40 | * @参数pageID:文件页号,表示在fileID指定的文件中,第几个文件页 41 | * @参数index:函数返回时,用来记录缓存页面数组中的下标 42 | * @参数ifRead:是否要将文件页中的内容读到缓存中 43 | * 返回:缓存页面的首地址 44 | * 功能:为文件中的某一个页面获取一个缓存中的页面 45 | * 缓存中的页面在缓存页面数组中的下标记录在index中 46 | * 并根据ifRead是否为true决定是否将文件中的内容写到获取的缓存页面中 47 | * 注意:在调用函数allocPage之前,调用者必须确信(fileID,pageID)指定的文件页面不存在缓存中 48 | * 如果确信指定的文件页面不在缓存中,那么就不用在hash表中进行查找,直接调用替换算法,节省时间 49 | */ 50 | BufType allocPage(int fileID, int pageID, int &index, bool ifRead = false); 51 | 52 | 53 | /* 54 | * @函数名getPage 55 | * @参数fileID:文件id 56 | * @参数pageID:文件页号 57 | * @参数index:函数返回时,用来记录缓存页面数组中的下标 58 | * 返回:缓存页面的首地址 59 | * 功能:为文件中的某一个页面在缓存中找到对应的缓存页面 60 | * 文件页面由(fileID,pageID)指定 61 | * 缓存中的页面在缓存页面数组中的下标记录在index中 62 | * 首先,在hash表中查找(fileID,pageID)对应的缓存页面, 63 | * 如果能找到,那么表示文件页面在缓存中 64 | * 如果没有找到,那么就利用替换算法获取一个页面 65 | */ 66 | BufType getPage(int fileID, int pageID, int &index); 67 | 68 | /* 69 | * @函数名access 70 | * @参数index:缓存页面数组中的下标,用来表示一个缓存页面 71 | * 功能:标记index代表的缓存页面被访问过,为替换算法提供信息 72 | */ 73 | void access(int index); 74 | 75 | /* 76 | * @函数名markDirty 77 | * @参数index:缓存页面数组中的下标,用来表示一个缓存页面 78 | * 功能:标记index代表的缓存页面被写过,保证替换算法在执行时能进行必要的写回操作, 79 | * 保证数据的正确性 80 | */ 81 | void markDirty(int index); 82 | 83 | /* 84 | * @函数名release 85 | * @参数index:缓存页面数组中的下标,用来表示一个缓存页面 86 | * 功能:将index代表的缓存页面归还给缓存管理器,在归还前,缓存页面中的数据不标记写回 87 | */ 88 | void release(int index); 89 | 90 | /* 91 | * @函数名writeBack 92 | * @参数index:缓存页面数组中的下标,用来表示一个缓存页面 93 | * 功能:将index代表的缓存页面归还给缓存管理器,在归还前,缓存页面中的数据需要根据脏页标记决定是否写到对应的文件页面中 94 | */ 95 | void writeBack(int index); 96 | 97 | /* 98 | * @函数名close 99 | * 功能:将所有缓存页面归还给缓存管理器,归还前需要根据脏页标记决定是否写到对应的文件页面中 100 | */ 101 | void close(); 102 | 103 | void closeFile(int fileID); 104 | 105 | /* 106 | * @函数名getKey 107 | * @参数index:缓存页面数组中的下标,用来指定一个缓存页面 108 | * @参数fileID:函数返回时,用于存储指定缓存页面所属的文件号 109 | * @参数pageID:函数返回时,用于存储指定缓存页面对应的文件页号 110 | */ 111 | void getKey(int index, int &fileID, int &pageID); 112 | 113 | static BufPageManager &getInstance(); 114 | 115 | ~BufPageManager(); 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/fileio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/BufPageManager.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/FileManager.h 5 | PARENT_SCOPE 6 | ) 7 | 8 | set(SOURCE 9 | ${SOURCE} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/BufPageManager.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/FileManager.cpp 12 | PARENT_SCOPE 13 | ) -------------------------------------------------------------------------------- /src/fileio/FileManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/29. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "../utils/MyBitMap.h" 13 | #include "FileManager.h" 14 | 15 | int FileManager::_createFile(const char *name) 16 | { 17 | FILE *f = fopen(name, "a+"); 18 | if (f == NULL) 19 | { 20 | cout << "fail" << endl; 21 | return -1; 22 | } 23 | fclose(f); 24 | return 0; 25 | } 26 | 27 | int FileManager::_openFile(const char *name, int fileID) 28 | { 29 | int f = open(name, O_RDWR); 30 | if (f == -1) 31 | { 32 | return -1; 33 | } 34 | fd[fileID] = f; 35 | return 0; 36 | } 37 | 38 | FileManager::FileManager() 39 | { 40 | fm = new MyBitMap(MAX_FILE_NUM, 1); 41 | tm = new MyBitMap(MAX_TYPE_NUM, 1); 42 | } 43 | 44 | FileManager &FileManager::getInstance() 45 | { 46 | static FileManager instance; 47 | return instance; 48 | } 49 | 50 | int FileManager::writePage(int fileID, int pageID, BufType buf, int off) 51 | { 52 | int f = fd[fileID]; 53 | off_t offset = pageID; 54 | offset = (offset << PAGE_SIZE_IDX); 55 | off_t error = lseek(f, offset, SEEK_SET); 56 | if (error != offset) 57 | { 58 | return -1; 59 | } 60 | printf("%lld\n", offset); 61 | BufType b = buf + off; 62 | error = write(f, (void *) b, PAGE_SIZE); 63 | return 0; 64 | } 65 | 66 | int FileManager::readPage(int fileID, int pageID, BufType buf, int off) 67 | { 68 | //int f = fd[fID[type]]; 69 | int f = fd[fileID]; 70 | off_t offset = pageID; 71 | offset = (offset << PAGE_SIZE_IDX); 72 | off_t error = lseek(f, offset, SEEK_SET); 73 | if (error != offset) 74 | { 75 | return -1; 76 | } 77 | BufType b = buf + off; 78 | error = read(f, (void *) b, PAGE_SIZE); 79 | return 0; 80 | } 81 | 82 | int FileManager::closeFile(int fileID) 83 | { 84 | fm->setBit(fileID, 1); 85 | int f = fd[fileID]; 86 | close(f); 87 | return 0; 88 | } 89 | 90 | int FileManager::createFile(const char *name) 91 | { 92 | return _createFile(name); 93 | } 94 | 95 | int FileManager::openFile(const char *name, int &fileID) 96 | { 97 | fileID = fm->findLeftOne(); 98 | fm->setBit(fileID, 0); 99 | return _openFile(name, fileID); 100 | } 101 | 102 | int FileManager::deleteFile(const char *name) 103 | { 104 | return remove(name); 105 | } 106 | 107 | int FileManager::newType() 108 | { 109 | int t = tm->findLeftOne(); 110 | tm->setBit(t, 0); 111 | return t; 112 | } 113 | 114 | void FileManager::closeType(int typeID) 115 | { 116 | tm->setBit(typeID, 1); 117 | } 118 | 119 | void FileManager::shutdown() 120 | { 121 | delete tm; 122 | delete fm; 123 | } 124 | 125 | FileManager::~FileManager() 126 | { 127 | this->shutdown(); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /src/fileio/FileManager.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_MANAGER 2 | #define FILE_MANAGER 3 | 4 | 5 | #include "../utils/pagedef.h" 6 | 7 | //#include "../MyLinkList.h" 8 | using namespace std; 9 | 10 | class MyBitMap; 11 | 12 | class FileManager { 13 | private: 14 | //FileTable* ftable; 15 | int fd[MAX_FILE_NUM]; 16 | MyBitMap *fm; 17 | MyBitMap *tm; 18 | 19 | int _createFile(const char *name); 20 | 21 | int _openFile(const char *name, int fileID); 22 | /* 23 | * FilManager构造函数 24 | */ 25 | FileManager(); 26 | 27 | public: 28 | 29 | static FileManager &getInstance(); 30 | 31 | /* 32 | * @函数名writePage 33 | * @参数fileID:文件id,用于区别已经打开的文件 34 | * @参数pageID:文件的页号 35 | * @参数buf:存储信息的缓存(4字节无符号整数数组) 36 | * @参数off:偏移量 37 | * 功能:将buf+off开始的2048个四字节整数(8kb信息)写入fileID和pageID指定的文件页中 38 | * 返回:成功操作返回0 39 | */ 40 | int writePage(int fileID, int pageID, BufType buf, int off); 41 | 42 | /* 43 | * @函数名readPage 44 | * @参数fileID:文件id,用于区别已经打开的文件 45 | * @参数pageID:文件页号 46 | * @参数buf:存储信息的缓存(4字节无符号整数数组) 47 | * @参数off:偏移量 48 | * 功能:将fileID和pageID指定的文件页中2048个四字节整数(8kb)读入到buf+off开始的内存中 49 | * 返回:成功操作返回0 50 | */ 51 | int readPage(int fileID, int pageID, BufType buf, int off); 52 | 53 | /* 54 | * @函数名closeFile 55 | * @参数fileID:用于区别已经打开的文件 56 | * 功能:关闭文件 57 | * 返回:操作成功,返回0 58 | */ 59 | int closeFile(int fileID); 60 | 61 | /* 62 | * @函数名createFile 63 | * @参数name:文件名 64 | * 功能:新建name指定的文件名 65 | * 返回:操作成功,返回true 66 | */ 67 | int createFile(const char *name); 68 | 69 | /* 70 | * @函数名openFile 71 | * @参数name:文件名 72 | * @参数fileID:函数返回时,如果成功打开文件,那么为该文件分配一个id,记录在fileID中 73 | * 功能:打开文件 74 | * 返回:如果成功打开,在fileID中存储为该文件分配的id,返回true,否则返回false 75 | */ 76 | int openFile(const char *name, int &fileID); 77 | 78 | int deleteFile(const char *name); 79 | 80 | int newType(); 81 | 82 | 83 | void closeType(int typeID); 84 | 85 | void shutdown(); 86 | 87 | ~FileManager(); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/fileio/FileTable.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_TABLE 2 | #define FILE_TABLE 3 | #include 4 | #include "../utils/MyBitMap.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | class FileTable { 11 | private: 12 | multiset isExist; 13 | multiset isOpen; 14 | vector fname; 15 | vector format; 16 | map nameToID; 17 | string* idToName; 18 | MyBitMap* ft, *ff; 19 | int n; 20 | void load() { 21 | ifstream fin("filenames"); 22 | fin >> n; 23 | for (int i = 0; i < n; ++ i) { 24 | string s, a; 25 | fin >> s; 26 | isExist.insert(s); 27 | fname.push_back(s); 28 | fin >> a; 29 | format.push_back(a); 30 | } 31 | fin.close(); 32 | } 33 | void save() { 34 | ofstream fout("filenames"); 35 | fout << fname.size() << endl; 36 | for (uint i = 0; i < fname.size(); ++ i) { 37 | fout << fname[i] << endl; 38 | fout << format[i] << endl; 39 | } 40 | fout.close(); 41 | } 42 | public: 43 | int newTypeID() { 44 | int k = ft->findLeftOne(); 45 | ft->setBit(k, 0); 46 | return k; 47 | } 48 | int newFileID(const string& name) { 49 | int k = ff->findLeftOne(); 50 | ff->setBit(k, 0); 51 | nameToID[name] = k; 52 | isOpen.insert(name); 53 | idToName[k] = name; 54 | return k; 55 | } 56 | bool ifexist(const string& name) { 57 | return (isExist.find(name) != isExist.end()); 58 | } 59 | void addFile(const string& name, const string& fm) { 60 | isExist.insert(name); 61 | fname.push_back(name); 62 | format.push_back(fm); 63 | } 64 | int getFileID(const string& name) { 65 | if (isOpen.find(name) == isOpen.end()) { 66 | return -1; 67 | } 68 | return nameToID[name]; 69 | } 70 | void freeTypeID(int typeID) { 71 | ft->setBit(typeID, 1); 72 | } 73 | void freeFileID(int fileID) { 74 | ff->setBit(fileID, 1); 75 | string name = idToName[fileID]; 76 | isOpen.erase(name); 77 | } 78 | string getFormat(string name) { 79 | for (uint i = 0; i < fname.size(); ++ i) { 80 | if (name == fname[i]) { 81 | return format[i]; 82 | } 83 | } 84 | return "-"; 85 | } 86 | FileTable(int fn, int tn) { 87 | load(); 88 | ft = new MyBitMap(tn, 1); 89 | ff = new MyBitMap(fn, 1); 90 | idToName = new string[fn]; 91 | isOpen.clear(); 92 | } 93 | ~FileTable() { 94 | save(); 95 | } 96 | }; 97 | #endif 98 | -------------------------------------------------------------------------------- /src/fileio/FindReplace.h: -------------------------------------------------------------------------------- 1 | #ifndef BUF_SEARCH 2 | #define BUF_SEARCH 3 | #include "../utils/MyLinkList.h" 4 | #include "../utils/MyHashMap.h" 5 | #include "../utils/pagedef.h" 6 | //template 7 | /* 8 | * FindReplace 9 | * 提供替换算法接口,这里实现的是栈式LRU算法 10 | */ 11 | class FindReplace { 12 | private: 13 | MyLinkList* list; 14 | int CAP_; 15 | public: 16 | /* 17 | * @函数名free 18 | * @参数index:缓存页面数组中页面的下标 19 | * 功能:将缓存页面数组中第index个页面的缓存空间回收 20 | * 下一次通过find函数寻找替换页面时,直接返回index 21 | */ 22 | void free(int index) { 23 | list->insertFirst(0, index); 24 | } 25 | /* 26 | * @函数名access 27 | * @参数index:缓存页面数组中页面的下标 28 | * 功能:将缓存页面数组中第index个页面标记为访问 29 | */ 30 | void access(int index) { 31 | list->insert(0, index); 32 | } 33 | /* 34 | * @函数名find 35 | * 功能:根据替换算法返回缓存页面数组中要被替换页面的下标 36 | */ 37 | int find() { 38 | int index = list->getFirst(0); 39 | list->del(index); 40 | list->insert(0, index); 41 | return index; 42 | } 43 | /* 44 | * 构造函数 45 | * @参数c:表示缓存页面的容量上限 46 | */ 47 | FindReplace(int c) { 48 | CAP_ = c; 49 | list = new MyLinkList(c, 1); 50 | for (int i = 0; i < CAP_; ++ i) { 51 | list->insert(0, i); 52 | } 53 | } 54 | }; 55 | #endif 56 | -------------------------------------------------------------------------------- /src/ix/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/ix.h 4 | 5 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_internal.h 6 | PARENT_SCOPE 7 | ) 8 | 9 | set(SOURCE 10 | ${SOURCE} 11 | 12 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_manager.cc 13 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_error.cc 14 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_indexhandle.cc 15 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_indexscan.cc 16 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_internal.cc 17 | ${CMAKE_CURRENT_SOURCE_DIR}/ix_test.cc 18 | PARENT_SCOPE 19 | ) 20 | -------------------------------------------------------------------------------- /src/ix/ix.h: -------------------------------------------------------------------------------- 1 | // 2 | // ix.h 3 | // 4 | // Index Manager Component Interface 5 | // 6 | 7 | #ifndef IX_H 8 | #define IX_H 9 | 10 | // Please do not include any other files than the ones below in this file. 11 | 12 | #include "../Constants.h" 13 | #include "../rm/RID.h" 14 | #include "../pf/pf.h" 15 | #include "ix_internal.h" 16 | #include "../rm/RM_FileHandle.h" 17 | 18 | class IX_IndexHandle; 19 | 20 | // 21 | // IX_Manager: provides IX index file management 22 | // 23 | class IX_Manager { 24 | PF_Manager *pfm; 25 | 26 | // RM_Manager rmm; 27 | 28 | public: 29 | ~IX_Manager (); // Destructor 30 | RC CreateIndex (const char *fileName, // Create new index 31 | int indexNo, 32 | AttrType attrType, 33 | int attrLength); 34 | RC DestroyIndex (const char *fileName, // Destroy index 35 | int indexNo); 36 | RC OpenIndex (const char *fileName, // Open index 37 | int indexNo, 38 | IX_IndexHandle &indexHandle, 39 | RM_FileHandle &rmFileHandle, 40 | int _attrOffset); 41 | RC CloseIndex (IX_IndexHandle &indexHandle); // Close index 42 | static RC indexAvailable(); 43 | static IX_Manager & getInstance(); 44 | private: 45 | explicit IX_Manager (PF_Manager *pfm); // Constructor 46 | char* GenFileName(const char* filename, int indexNo); 47 | }; 48 | 49 | // 50 | // IX_IndexHandle: IX Index File interface 51 | // 52 | class IX_IndexHandle { 53 | public: 54 | IX_IndexHandle (); // Constructor 55 | ~IX_IndexHandle (); // Destructor 56 | RC InsertEntry (const RID &rid); // Insert new index entry 57 | RC DeleteEntry (const RID &rid); // Delete index entry 58 | RC ForcePages (); // Copy index to disk 59 | 60 | RC init(const char* indexFileName, PF_Manager *_pfm, RM_FileHandle *_rmFileHandle, int _attrOffset); 61 | RC CloseIndex(); 62 | LeafNode FindLeafNode(void *pData); 63 | 64 | void PrintFullLinkList(); // print full link list just for attrtype = int 65 | 66 | void GetNextRIDPositionInfo(RIDPositionInfo &ridPositionInfo, int dir, bool EQ_OP); 67 | void GetGeqRIDPos(const void *pData, RIDPositionInfo &ridPositionInfo, bool returnFirstRID, bool LE); 68 | 69 | int cmp(const RID, const RID); 70 | int cmp(const void*, const void*); 71 | char* getValueFromRecRID(const RID rid); 72 | void checkLinkListIsValid(); 73 | private: 74 | 75 | IndexInfo *indexInfo; 76 | PF_FileHandle fileHandle; 77 | PF_Manager *pfm; 78 | RM_FileHandle *rmFileHandle; 79 | int attrOffset; 80 | PageNum pinnedPageList[MAX_DEPTH]; 81 | int pinnedPageNum; 82 | PageNum disposedPageList[MAX_DEPTH]; 83 | int disposedPageNum; 84 | 85 | RC insertIntoRIDPage(const RID rid, const PageNum pageNum); 86 | RC deleteFromRIDPage(const RID rid, const PageNum pageNum, int &lastRIDCount, RID &replacedRID); 87 | PageNum InsertEntryFromPage(RID rid, PageNum &pageNum, PageNum fatherPage, int nodePos); 88 | RC DeleteEntryFromPage(RID rid, PageNum& pageNum, PageNum fatherPageNum, int thisPos); 89 | PageNum FindLeafPageFromPage(void *pData, PageNum pageNum); 90 | 91 | int getRIDPageSize(const PageNum pageNum); 92 | int getLeafNodeSize(const PageNum pageNum); 93 | void getPageData(const PageNum pageNum, char*& pageData); 94 | void getLeafNode(const PageNum pageNum, LeafNode*& leafNode); 95 | void getInternalNode(const PageNum pageNum, InternalNode*& internalNode); 96 | PageNum allocateNewPage(PF_PageHandle &pageHandle); 97 | void getExistedPage(PageNum pageNum, PF_PageHandle &pageHandle); 98 | 99 | void addPinnedPage(const PageNum pageNum); 100 | void addDisposedPage(const PageNum pageNum); 101 | void unpinAllPages(); 102 | void disposeAllPages(); 103 | 104 | void assertIncrease(RID* ridList, int length); 105 | void assertIncrease(LeafNode* leafNode); 106 | void assertIncrease(InternalNode* internalNode); 107 | }; 108 | 109 | // 110 | // IX_IndexScan: condition-based scan of index entries 111 | // 112 | class IX_IndexScan { 113 | public: 114 | 115 | IX_IndexScan (); // Constructor 116 | ~IX_IndexScan (); // Destructor 117 | RC OpenScan (IX_IndexHandle &indexHandle, // Initialize index scan 118 | CompOp compOp, 119 | const void *value, 120 | ClientHint pinHint = ClientHint::NO_HINT); 121 | RC GetNextEntry (RID &rid); // Get next matching entry 122 | RC CloseScan (); // Terminate index scan 123 | private: 124 | IX_IndexHandle *indexHandle; 125 | RIDPositionInfo ridPositionInfo; 126 | int dir; 127 | const void *skipValue; 128 | bool EQ_OP; 129 | 130 | RIDList *ridHead, *tail; 131 | }; 132 | 133 | // 134 | // Print-error function 135 | // 136 | void IX_PrintError(RC rc); 137 | 138 | void IX_Test(); 139 | 140 | #define IX_EOF (START_IX_WARN + 0) 141 | #define IX_ENTRY_EXISTS (START_IX_WARN + 1) 142 | #define IX_ENTRY_DOES_NOT_EXIST (START_IX_WARN + 2) 143 | #define IX_SCAN_NOT_OPENED (START_IX_WARN + 3) 144 | #define IX_SCAN_NOT_CLOSED (START_IX_WARN + 4) 145 | #define IX_BUCKET_FULL (START_IX_WARN + 5) 146 | #define IX_LASTWARN IX_SCAN_NOT_CLOSED 147 | 148 | 149 | #define IX_ATTR_TOO_LARGE (START_IX_ERR - 0) 150 | #define IX_LASTERROR IX_ATTR_TOO_LARGE 151 | 152 | #endif // IX_H 153 | -------------------------------------------------------------------------------- /src/ix/ix_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: ix_error.cc 3 | // Description: IX_PrintError function 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ix.h" 10 | 11 | using namespace std; 12 | 13 | // 14 | // Error table 15 | // 16 | const char *IX_WarnMsg[] = { 17 | 18 | }; 19 | 20 | const char *IX_ErrorMsg[] = { 21 | 22 | }; 23 | 24 | // 25 | // IX_PrintError 26 | // 27 | // Desc: Send a message corresponding to a IX return code to cerr 28 | // In: rc - return code for which a message is desired 29 | // 30 | void IX_PrintError(RC rc) { 31 | // Check the return code is within proper limits 32 | if (rc >= START_IX_WARN && rc <= IX_LASTWARN) 33 | // Print warning 34 | cerr << "IX warning: " << IX_WarnMsg[rc - START_IX_WARN] << "\n"; 35 | // Error codes are negative, so invert everything 36 | else if ((-rc >= -START_IX_ERR) && -rc <= -IX_LASTERROR) { 37 | // Print error 38 | cerr << "IX error: " << IX_ErrorMsg[-rc + START_IX_ERR] << "\n"; 39 | } 40 | else if (rc == 0) 41 | cerr << "IX_PrintError called with return code of 0\n"; 42 | else { 43 | // Print error 44 | cerr << "rc was " << rc << endl; 45 | cerr << "START_IX_ERR was " << START_IX_ERR << endl; 46 | cerr << "IX_LASTERROR was " << IX_LASTERROR << endl; 47 | cerr << "IX error: " << rc << " is out of bounds\n"; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ix/ix_indexscan.cc: -------------------------------------------------------------------------------- 1 | #include "ix.h" 2 | #include "ix_internal.h" 3 | #include 4 | 5 | IX_IndexScan::IX_IndexScan() = default; 6 | 7 | IX_IndexScan::~IX_IndexScan() = default; 8 | 9 | RC IX_IndexScan::OpenScan(IX_IndexHandle &_indexHandle, CompOp compOp, const void *value, ClientHint clientHint) { 10 | indexHandle = &_indexHandle; 11 | EQ_OP = false; 12 | skipValue = nullptr; 13 | if (compOp == CompOp::EQ_OP) { 14 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, false, false); 15 | if (ridPositionInfo.ridPagePos != -1) { 16 | if (indexHandle->cmp(indexHandle->getValueFromRecRID(ridPositionInfo.leafNode.data[ridPositionInfo.posInLeaf].recRID), value) != 0) 17 | ridPositionInfo.ridPagePos = -1; 18 | } 19 | EQ_OP = true; 20 | } 21 | else if (compOp == CompOp::LT_OP) { 22 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, false, true); 23 | //if (indexHandle->cmp(ridPositionInfo.leafNode.data[ridPositionInfo.posInLeaf].pdata, value) == 0) 24 | // indexHandle->GetNextRIDPositionInfo(ridPositionInfo, -1, false); 25 | skipValue = value; 26 | dir = -1; 27 | } 28 | else if (compOp == CompOp::LE_OP) { 29 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, false, true); 30 | dir = -1; 31 | } 32 | else if (compOp == CompOp::GT_OP) { 33 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, false, false); 34 | //if (indexHandle->cmp(ridPositionInfo.leafNode.data[ridPositionInfo.posInLeaf].pdata, value) == 0) 35 | // indexHandle->GetNextRIDPositionInfo(ridPositionInfo, 1, false); 36 | skipValue = value; 37 | dir = 1; 38 | } 39 | else if (compOp == CompOp::GE_OP) { 40 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, false, false); 41 | dir = 1; 42 | } 43 | else if (compOp == CompOp::NE_OP) { 44 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, true, false); 45 | skipValue = value; 46 | dir = 1; 47 | } 48 | else if (compOp == CompOp::NO_OP) { 49 | indexHandle->GetGeqRIDPos(value, ridPositionInfo, true, false); 50 | dir = 1; 51 | } 52 | ridHead = nullptr; 53 | tail = nullptr; 54 | RID rid; 55 | while (ridPositionInfo.getCurRID(rid) != -1) { 56 | //printf("i = %d\n", i++); 57 | if (skipValue != nullptr && indexHandle->cmp(skipValue, ridPositionInfo.value) == 0) { 58 | indexHandle->GetNextRIDPositionInfo(ridPositionInfo, dir, EQ_OP); 59 | continue; 60 | } 61 | else if (compOp == CompOp::LE_OP || compOp == CompOp::LT_OP) { 62 | if (indexHandle->cmp(indexHandle->getValueFromRecRID(rid), value) > 0) { 63 | indexHandle->GetNextRIDPositionInfo(ridPositionInfo, dir, EQ_OP); 64 | continue; 65 | } 66 | } 67 | auto *p = new RIDList(); 68 | p->rid = rid; 69 | p->prev = tail; 70 | if (ridHead == nullptr) { 71 | ridHead = p; 72 | tail = p; 73 | } 74 | else { 75 | tail->next = p; 76 | tail = p; 77 | } 78 | indexHandle->GetNextRIDPositionInfo(ridPositionInfo, dir, EQ_OP); 79 | } 80 | return 0; 81 | } 82 | 83 | RC IX_IndexScan::GetNextEntry(RID &rid) { 84 | if (dir == 1) { 85 | if (ridHead == nullptr) { 86 | return IX_EOF; 87 | } 88 | rid = ridHead->rid; 89 | RIDList *p = ridHead->next; 90 | delete ridHead; 91 | ridHead = p; 92 | } 93 | else { 94 | if (tail == nullptr) { 95 | return IX_EOF; 96 | } 97 | rid = tail->rid; 98 | RIDList *p = tail->prev; 99 | delete tail; 100 | tail = p; 101 | } 102 | return 0; 103 | } 104 | 105 | RC IX_IndexScan::CloseScan() { 106 | return 0; 107 | } -------------------------------------------------------------------------------- /src/ix/ix_internal.cc: -------------------------------------------------------------------------------- 1 | #include "ix_internal.h" 2 | #include "ix.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | RC RIDPagePacket::insertRID(const RID rid) { 9 | for (int i = 0; i < size; ++i) { 10 | if (rid.getPageNum() == r[i].getPageNum() && rid.getSlotNum() == r[i].getSlotNum()) { 11 | return IX_ENTRY_EXISTS; 12 | } 13 | } 14 | if (size + 1 == RID_BUCKET_SIZE) { 15 | return IX_BUCKET_FULL; 16 | } 17 | else { 18 | r[size++] = rid; 19 | return 0; 20 | } 21 | } 22 | 23 | RC RIDPagePacket::deleteRID(const RID rid) { 24 | int pos = -1; 25 | for (int i = 0; i < size; ++i) { 26 | if (rid.getPageNum() == r[i].getPageNum() && rid.getSlotNum() == r[i].getSlotNum()) { 27 | pos = i; 28 | break; 29 | } 30 | } 31 | if (pos == -1) 32 | return IX_ENTRY_DOES_NOT_EXIST; 33 | for (int i = pos; i < size - 1; ++i) 34 | r[i] = r[i + 1]; 35 | --size; 36 | return 0; 37 | } 38 | 39 | void LeafNode::split(LeafNode* splitNode, PageNum newPage, PageNum thisPage) { 40 | 41 | assert(size == 2 * D + 1); 42 | size = D + 1; 43 | splitNode->size = D; 44 | for (int i = 0; i < splitNode->size; ++i) 45 | splitNode->data[i] = data[size + i]; 46 | 47 | splitNode->leftPage = thisPage; 48 | splitNode->rightPage = rightPage; 49 | rightPage = newPage; 50 | } 51 | 52 | void LeafNode::insertDataIntoPos(RID rid, PageNum pageNum, int pos) { 53 | for (int i = size; i >= pos + 1; --i) 54 | data[i] = data[i - 1]; 55 | data[pos].recRID = rid; 56 | data[pos].ridPageNum = pageNum; 57 | ++size; 58 | } 59 | 60 | void LeafNode::deleteDataAtPos(int pos) { 61 | for (int i = pos; i <= size - 2; ++i) 62 | data[i] = data[i + 1]; 63 | --size; 64 | } 65 | 66 | void InternalNode::InsertKeyAfterPos(RID rid, PageNum pageNum, int pos) { 67 | for (int i = keyCount + 1; i >= pos + 2; --i) { 68 | son[i] = son[i - 1]; 69 | recRID[i] = recRID[i - 1]; 70 | } 71 | son[pos + 1] = pageNum; 72 | recRID[pos + 1] = rid; 73 | ++keyCount; 74 | } 75 | 76 | 77 | void InternalNode::DeleteKeyAtPos(int pos) { 78 | for (int i = pos; i < keyCount; ++i) { 79 | son[i] = son[i + 1]; 80 | recRID[i] = recRID[i + 1]; 81 | } 82 | --keyCount; 83 | } 84 | void InternalNode::Split(InternalNode* splitNode) { 85 | assert(keyCount == 2 * D); 86 | keyCount = D; 87 | splitNode->keyCount = D - 1; 88 | splitNode->son[0] = son[D + 1]; 89 | splitNode->recRID[0] = recRID[D + 1]; 90 | for (int i = 1; i <= splitNode->keyCount; ++i) { 91 | splitNode->son[i] = son[D + 1 + i]; 92 | splitNode->recRID[i] = recRID[D + 1 + i]; 93 | } 94 | } 95 | 96 | void LineDebug(int line, const char *file, int err) { 97 | if (err != 0) { 98 | printf("line %d in file %s, err = %d\n", line, file, err); 99 | if (err < 0) { 100 | exit(0); 101 | } 102 | } 103 | } 104 | 105 | void LeafNode::operator = (const LeafNode &o) { 106 | size = o.size; 107 | for (int i = 0; i < size; ++i) 108 | data[i] = o.data[i]; 109 | leftPage = o.leftPage; 110 | rightPage = o.rightPage; 111 | } 112 | 113 | void RIDPagePacket::operator = (const RIDPagePacket &o) { 114 | size = o.size; 115 | for (int i = 0; i < size; ++i) 116 | r[i] = o.r[i]; 117 | } 118 | 119 | RIDList::RIDList() { 120 | next = prev = NULL; 121 | } 122 | 123 | int RIDPositionInfo::getCurRID(RID &rid) { 124 | if (ridPagePos == -1) 125 | return -1; 126 | rid = ridPagePacket.r[ridPagePos]; 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /src/ix/ix_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef IX_INTERNAL_H 2 | #define IX_INTERNAL_H 3 | 4 | #include "../Constants.h" 5 | #include "../rm/RID.h" 6 | #include "../pf/pf.h" 7 | 8 | 9 | const int D = (4050 - 16) / (sizeof(RID) + sizeof(PageNum)) / 4 - 3; 10 | //const int D = 2; 11 | const int RID_BUCKET_SIZE = 4000 / sizeof(RID); 12 | #define MAX_DEPTH 1010 13 | #define INTERNAL_NODE 0 14 | #define LEAF_NODE 1 15 | #define LDB(opt) LineDebug(__LINE__, __FILE__, opt) 16 | struct IndexInfo { 17 | AttrType attrType; 18 | int attrLength; 19 | PageNum rootPageNum; 20 | }; 21 | struct LeafData { 22 | //void *pdata; 23 | RID recRID; 24 | PageNum ridPageNum; 25 | void init() { ridPageNum = -1; } 26 | }; 27 | struct LeafNode { 28 | int size; 29 | LeafData data[D * 2 + 1]; 30 | PageNum leftPage, rightPage; 31 | void init() { size = 0; leftPage = rightPage = -1; } 32 | //void insertDataIntoPos(void *pData, PageNum pageNum, int pos); 33 | void insertDataIntoPos(RID rid, PageNum pageNum, int pos); 34 | void deleteDataAtPos(int pos); 35 | void split(LeafNode* splitNode, PageNum newPage, PageNum thisPage); 36 | void operator = (const LeafNode &); 37 | }; 38 | struct InternalNode { 39 | int keyCount; 40 | PageNum son[2 * D + 2]; 41 | RID recRID[2 * D + 2]; 42 | void init() { keyCount = 0; for (int i = 0; i <= 2 * D; ++i) son[i] = -1; } 43 | //void InsertKeyAfterPos(void *pData, PageNum pageNum, int pos); 44 | void InsertKeyAfterPos(RID rid, PageNum pageNum, int pos); 45 | void DeleteKeyAtPos(int pos); 46 | void Split(InternalNode* splitNode); 47 | }; 48 | struct NodePagePacket { 49 | int nodeType; 50 | /* 0 -> InternalNode 1 -> LeafNode */ 51 | LeafNode leafNode; 52 | InternalNode internalNode; 53 | }; 54 | struct RIDPagePacket { 55 | int size; 56 | RID r[RID_BUCKET_SIZE]; 57 | RC insertRID(const RID rid); 58 | RC deleteRID(const RID rid); 59 | void operator = (const RIDPagePacket &); 60 | }; 61 | struct RIDPositionInfo { 62 | LeafNode leafNode; 63 | int posInLeaf; 64 | RIDPagePacket ridPagePacket; 65 | int ridPagePos; 66 | void *value; 67 | int getCurRID(RID &rid); 68 | }; 69 | struct RIDList { 70 | RID rid; 71 | RIDList *next, *prev; 72 | RIDList(); 73 | }; 74 | 75 | void LineDebug(int line, const char *file, int err); 76 | #endif 77 | -------------------------------------------------------------------------------- /src/ix/ix_manager.cc: -------------------------------------------------------------------------------- 1 | #include "ix.h" 2 | #include "ix_internal.h" 3 | #include 4 | #include 5 | 6 | IX_Manager::IX_Manager(PF_Manager *pfm) { 7 | this->pfm = pfm; 8 | } 9 | 10 | IX_Manager::~IX_Manager() {} 11 | 12 | char* IX_Manager::GenFileName(const char* filename, int indexNo) { 13 | int len = strlen(filename); 14 | char *file = new char[len + 15]; 15 | strcpy(file, filename); 16 | file[len++] = '.'; 17 | if (indexNo == 0) 18 | file[len++] = '0'; 19 | while (indexNo > 0) { 20 | file[len++] = '0' + (indexNo % 10); 21 | indexNo /= 10; 22 | } 23 | file[len++] = '\0'; 24 | return file; 25 | } 26 | 27 | RC IX_Manager::CreateIndex(const char* filename, int indexNo, AttrType attrType, int attrLength) { 28 | // gen filename 29 | RC rc; 30 | char *file = GenFileName(filename, indexNo); 31 | // open file and allocate page 0 : IndexInfo 32 | if ((rc = pfm->CreateFile(file)) != 0) 33 | return rc; 34 | PF_FileHandle fileHandle; 35 | if ((rc = pfm->OpenFile(file, fileHandle)) != 0) 36 | return rc; 37 | PF_PageHandle pageHandle; 38 | LDB(fileHandle.AllocatePage(pageHandle)); 39 | char *pageData; 40 | LDB(pageHandle.GetData(pageData)); 41 | PageNum pageNum; 42 | LDB(pageHandle.GetPageNum(pageNum)); 43 | auto *indexInfo = (IndexInfo*) pageData; 44 | indexInfo->attrType = attrType; 45 | indexInfo->attrLength = attrLength; 46 | indexInfo->rootPageNum = 1; 47 | LDB(fileHandle.MarkDirty(pageNum)); 48 | LDB(fileHandle.ForcePages(pageNum)); 49 | LDB(fileHandle.UnpinPage(pageNum)); 50 | 51 | // allocate page 1 : LeafNode 52 | LDB(fileHandle.AllocatePage(pageHandle)); 53 | LDB(pageHandle.GetPageNum(pageNum)); 54 | LDB(pageHandle.GetData(pageData)); 55 | auto * nodePagePacket = (NodePagePacket*) pageData; 56 | nodePagePacket->nodeType = LEAF_NODE; 57 | (nodePagePacket->leafNode).init(); 58 | LDB(fileHandle.MarkDirty(pageNum)); 59 | LDB(fileHandle.ForcePages(pageNum)); 60 | LDB(fileHandle.UnpinPage(pageNum)); 61 | 62 | LDB(pfm->CloseFile(fileHandle)); 63 | delete[] file; 64 | return 0; 65 | } 66 | 67 | RC IX_Manager::DestroyIndex(const char* filename, int indexNo) { 68 | // gen filename 69 | RC rc; 70 | char *file = GenFileName(filename, indexNo); 71 | if ((rc = pfm->DestroyFile(file)) != 0) 72 | return rc; 73 | delete[] file; 74 | return 0; 75 | } 76 | 77 | 78 | RC IX_Manager::OpenIndex(const char* filename, int indexNo, IX_IndexHandle &indexHandle, RM_FileHandle &rmFileHandle, int _attrOffset) { 79 | RC rc; 80 | if ((rc = indexHandle.init(GenFileName(filename, indexNo), this->pfm, &rmFileHandle, _attrOffset)) != 0) 81 | return rc; 82 | return 0; 83 | } 84 | 85 | RC IX_Manager::CloseIndex(IX_IndexHandle &indexHandle) { 86 | RC rc; 87 | if ((rc = indexHandle.CloseIndex()) != 0) 88 | return rc; 89 | return 0; 90 | } 91 | 92 | RC IX_Manager::indexAvailable() { 93 | return 1; 94 | } 95 | 96 | IX_Manager &IX_Manager::getInstance() { 97 | static IX_Manager manager(&PF_Manager::getInstance()); 98 | return manager; 99 | } 100 | -------------------------------------------------------------------------------- /src/ix/ix_test.cc: -------------------------------------------------------------------------------- 1 | #include "ix.h" 2 | #include "ix_internal.h" 3 | #include "../rm/RecordManager.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | char recFile[110] = "TestRecordFile"; 10 | char indFile[110] = "TestIndexFile"; 11 | int v[100010]; 12 | RID r[100010]; 13 | std::pair stp[100010]; 14 | const int n = 10000; 15 | const int m = n / 2; 16 | const int half_m = m / 2; 17 | const int gap = n / 10; 18 | int validID[100010]; 19 | void checkResultList(IX_IndexHandle &indexHandle, CompOp compOp, void *value) { 20 | RID rid; 21 | for (int i = 0; i < n; ++i) 22 | stp[i] = std::make_pair(v[i], i); 23 | std::sort(stp, stp + n); 24 | int count = 0; 25 | for (int i = 0; i < n; ++i) { 26 | bool cond = false; 27 | switch (compOp) { 28 | case CompOp::LE_OP: { 29 | cond = indexHandle.cmp((void*)(&(stp[i].first)), value) <= 0; 30 | break; 31 | } 32 | case CompOp::LT_OP: { 33 | cond = indexHandle.cmp((void*)(&(stp[i].first)), value) < 0; 34 | break; 35 | } 36 | case CompOp::GE_OP: { 37 | cond = indexHandle.cmp((void*)(&(stp[i].first)), value) >= 0; 38 | break; 39 | } 40 | case CompOp::GT_OP: { 41 | cond = indexHandle.cmp((void*)(&(stp[i].first)), value) > 0; 42 | break; 43 | } 44 | case CompOp::NO_OP: { 45 | cond = true; 46 | break; 47 | } 48 | case CompOp::EQ_OP: { 49 | cond = indexHandle.cmp((void*)(&(stp[i].first)), value) == 0; 50 | break; 51 | } 52 | case CompOp::NE_OP: { 53 | cond = indexHandle.cmp((void*)(&(stp[i].first)), value) != 0; 54 | break; 55 | } 56 | default: { 57 | break; 58 | } 59 | } 60 | if (cond) 61 | validID[count++] = stp[i].second; 62 | } 63 | int pointer = 0; 64 | IX_IndexScan indexScan; 65 | LDB(indexScan.OpenScan(indexHandle, compOp, value, ClientHint::NO_HINT)); 66 | while (indexScan.GetNextEntry(rid) != IX_EOF) { 67 | //printf("expected = %d get = %d\n", v[validID[pointer]], *(int*)indexHandle.getValueFromRecRID(rid)); 68 | assert(v[validID[pointer++]] == *(int*)indexHandle.getValueFromRecRID(rid)); 69 | } 70 | assert(count == pointer); 71 | } 72 | 73 | void IX_Test() { 74 | int seed = time(NULL); 75 | //int seed = 1547018327; 76 | printf("seed = %d\n", seed); 77 | //srand(time(NULL)); 78 | srand(seed); 79 | PF_Manager &pfManager = PF_Manager::getInstance(); 80 | IX_Manager *ixm = &IX_Manager::getInstance(); 81 | RecordManager *rmm = &RecordManager::getInstance(); 82 | LDB(rmm->destroyFile(recFile)); 83 | LDB(rmm->createFile(recFile, sizeof (int) + 1)); 84 | RM_FileHandle rmFileHandle; 85 | // insert into rec file 86 | LDB(rmm->openFile(recFile, rmFileHandle)); 87 | RID rid; 88 | for (int i = 0; i < n; ++i) { 89 | v[i] = rand() % m + 1; 90 | //v[i] = 100000 + i; 91 | rid = rmFileHandle.insertRec((char*)(v + i)); 92 | r[i] = rid; 93 | //printf("i = %d, vi = %d, ri = (%ld, %u)\n", i, v[i], r[i].getPageNum(), r[i].getSlotNum()); 94 | } 95 | LDB(rmm->closeFile(rmFileHandle)); 96 | // insert into index 97 | IX_IndexHandle indexHandle; 98 | LDB(ixm->DestroyIndex(indFile, 0)); 99 | LDB(ixm->CreateIndex(indFile, 0, AttrType::INT, 4)); 100 | LDB(rmm->openFile(recFile, rmFileHandle)); 101 | LDB(ixm->OpenIndex(indFile, 0, indexHandle, rmFileHandle, 0)); 102 | RM_FileScan rmFileScan; 103 | LDB(rmFileScan.openScan(rmFileHandle, AttrType::INT, 4, 0, CompOp::ISNOT_OP, nullptr)); 104 | RM_Record rmRecord; 105 | int count = 0; 106 | while (rmFileScan.getNextRec(rmRecord) == 0) { 107 | if (count % gap == 0) 108 | printf("insert index #%d\n", count); 109 | rid = rmRecord.getRID(); 110 | char *value = rmRecord.getData(); 111 | assert(*(int*)value == v[count]); 112 | assert(rid.getPageNum() == r[count].getPageNum() && rid.getSlotNum() == r[count].getSlotNum()); 113 | count++; 114 | indexHandle.InsertEntry(rid); 115 | //indexHandle.checkLinkListIsValid(); 116 | } 117 | checkResultList(indexHandle, CompOp::LE_OP, new int(half_m)); 118 | checkResultList(indexHandle, CompOp::LT_OP, new int(half_m)); 119 | checkResultList(indexHandle, CompOp::GE_OP, new int(half_m)); 120 | checkResultList(indexHandle, CompOp::GT_OP, new int(half_m)); 121 | printf("test after insertion passed...\n"); 122 | LDB(rmFileScan.closeScan()); 123 | LDB(ixm->CloseIndex(indexHandle)); 124 | LDB(rmm->closeFile(rmFileHandle)); 125 | 126 | LDB(rmm->openFile(recFile, rmFileHandle)); 127 | LDB(ixm->OpenIndex(indFile, 0, indexHandle, rmFileHandle, 0)); 128 | for (int i = 0; i < n; ++i) { 129 | if (i % gap == 0) 130 | printf("update entry #%d\n", i); 131 | /* 132 | if (i == 17) { 133 | printf("i see you~\n"); 134 | } 135 | */ 136 | // gen update pos and value 137 | //int pos = rand() % n; 138 | //int value = rand(); 139 | int pos = i; 140 | int value = -v[i]; 141 | //printf("set pos %d = %d\n", pos, value); 142 | // remove old index 143 | LDB(indexHandle.DeleteEntry(r[pos])); 144 | // get record and modify 145 | RM_Record rec; 146 | LDB(rmFileHandle.getRec(r[pos], rec)); 147 | char *recData = rec.getData(); 148 | int *intRecData = (int*) recData; 149 | *intRecData = value; 150 | v[pos] = value; 151 | // update record 152 | LDB(rmFileHandle.updateRec(rec)); 153 | // check value is updated 154 | LDB(rmFileHandle.getRec(r[pos], rec)); 155 | recData = rec.getData(); 156 | assert(*(int*)recData == v[pos]); 157 | 158 | /* 159 | // delete record 160 | LDB(rmFileHandle.deleteRec(rid)); 161 | // insert record 162 | v[pos] = value; 163 | LDB(rmFileHandle.insertRec((char*)(v + pos), rid); 164 | r[pos] = rid; 165 | */ 166 | 167 | // insert new index 168 | LDB(indexHandle.InsertEntry(r[pos])); 169 | //checkResultList(indexHandle); 170 | } 171 | // check result is correct 172 | checkResultList(indexHandle, CompOp::LE_OP, new int(-m * 2)); 173 | checkResultList(indexHandle, CompOp::LE_OP, new int(0)); 174 | checkResultList(indexHandle, CompOp::GE_OP, new int(-m * 2)); 175 | checkResultList(indexHandle, CompOp::GE_OP, new int(0)); 176 | checkResultList(indexHandle, CompOp::EQ_OP, new int(-half_m)); 177 | checkResultList(indexHandle, CompOp::NE_OP, new int(-half_m)); 178 | checkResultList(indexHandle, CompOp::NO_OP, NULL); 179 | printf("test after update passed...\n"); 180 | LDB(ixm->CloseIndex(indexHandle)); 181 | LDB(rmm->closeFile(rmFileHandle)); 182 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/RecordManager.h" 2 | #include "rm/RM_FileHandle.h" 3 | #include "rm/RM_FileScan.h" 4 | #include "fileio/BufPageManager.h" 5 | #include "utils/MyBitMap.h" 6 | #include "ql/QueryManager.h" 7 | #include 8 | #include 9 | #include "ix/ix.h" 10 | 11 | char start_parse(const char *expr_input); 12 | 13 | int yyparse(); 14 | 15 | 16 | //int main() 17 | //{ 18 | // MyBitMap::initConst(); 19 | // chdir("/Users/Duzx/Downloads/DB_test"); 20 | // RecordManager &rm = RecordManager::getInstance(); 21 | // RM_FileHandle file_handle; 22 | // rm.createFile("persons", 10); 23 | // rm.openFile("persons", file_handle); 24 | // RM_Record record; 25 | // std::vector rid1, rid2; 26 | // for (int j = 0; j < 2000; ++j) 27 | // { 28 | // RID rid = file_handle.insertRec(std::to_string(j).c_str()); 29 | // file_handle.getRec(rid, record); 30 | // if(j % 2 != 0) { 31 | // rid1.push_back(rid); 32 | // } 33 | // else if (j % 6 == 0){ 34 | // rid2.push_back(rid); 35 | // } 36 | // } 37 | // for(auto &it: rid1){ 38 | // file_handle.deleteRec(it); 39 | // } 40 | // for(auto &it: rid2){ 41 | // file_handle.updateRec(RM_Record{"test", 10, it}); 42 | // } 43 | // RM_FileScan fileScan; 44 | // fileScan.openScan(file_handle, AttrType::NO_ATTR, 0, 0, CompOp::NO_OP, nullptr); 45 | // int i = 0; 46 | // while(true) 47 | // { 48 | // int rc = fileScan.getNextRec(record); 49 | // if(rc) { 50 | // break; 51 | // } 52 | // printf(record.getData()); 53 | // printf("\n"); 54 | // i += 1; 55 | // } 56 | // rm.closeFile(file_handle); 57 | //} 58 | 59 | int main(int args, char **argv) { 60 | MyBitMap::initConst(); 61 | 62 | /* 63 | IX_Test(); 64 | printf("test successfully\n"); 65 | return 0; 66 | */ 67 | 68 | if (args > 1) { 69 | for (int i = 0; i < args - 1; ++i) { 70 | if (freopen(argv[i + 1], "r", stdin)) 71 | yyparse(); 72 | else { 73 | fprintf(stderr, "Open file %s failed\n", argv[i + 1]); 74 | return -1; 75 | } 76 | } 77 | } 78 | else { 79 | // freopen("../test/small_dataset/create.sql", "r", stdin); 80 | int rc = yyparse(); 81 | while(rc) { 82 | rc = yyparse(); 83 | } 84 | return 0; 85 | } 86 | } -------------------------------------------------------------------------------- /src/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(BISON REQUIRED) 2 | find_package(FLEX REQUIRED) 3 | 4 | BISON_TARGET(Parser ${CMAKE_CURRENT_SOURCE_DIR}/parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.cpp 5 | VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/parser.output) 6 | FLEX_TARGET(Lexer ${CMAKE_CURRENT_SOURCE_DIR}/lex.l ${CMAKE_CURRENT_BINARY_DIR}/parser.yy.cpp) 7 | 8 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 10 | 11 | ADD_FLEX_BISON_DEPENDENCY(Lexer Parser) 12 | 13 | 14 | add_library(SqlParser 15 | ${BISON_Parser_OUTPUT_SOURCE} 16 | ${FLEX_Lexer_OUTPUTS} 17 | ${BISON_Parser_OUTPUT_HEADER} 18 | ) 19 | 20 | set(SOURCE 21 | ${SOURCE} 22 | ${CMAKE_CURRENT_SOURCE_DIR}/Expr.cpp 23 | ${CMAKE_CURRENT_SOURCE_DIR}/Tree.cpp 24 | PARENT_SCOPE 25 | ) 26 | 27 | set(HEADERS 28 | ${HEADERS} 29 | ${CMAKE_CURRENT_SOURCE_DIR}/Expr.h 30 | ${CMAKE_CURRENT_SOURCE_DIR}/Tree.h 31 | PARENT_SCOPE 32 | ) -------------------------------------------------------------------------------- /src/parser/Expr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018-12-09. 3 | // 4 | 5 | #ifndef MYDB_EXPR_H 6 | #define MYDB_EXPR_H 7 | 8 | #include "../Constants.h" 9 | #include 10 | #include 11 | #include 12 | 13 | #define EXPR_NO_SUCH_TABLE -1 14 | #define EXPR_NO_SUCH_ATTRIBUTE -2 15 | #define EXPR_AMBIGUOUS -3 16 | 17 | struct AttrBindException { 18 | std::string table; 19 | std::string attribute; 20 | int type; 21 | }; 22 | 23 | enum class NodeType { 24 | ARITH_NODE, 25 | COMP_NODE, 26 | LOGIC_NODE, 27 | CONST_NODE, 28 | ATTR_NODE 29 | }; 30 | 31 | struct BindAttribute { 32 | std::string attrName; 33 | std::string tableName; 34 | int attrSize; 35 | int attrLength; 36 | AttrType attrType = AttrType::NO_ATTR; 37 | bool notNull{}; 38 | bool withIndex; 39 | int attrOffset{}; 40 | }; 41 | 42 | class AttributeNode; 43 | 44 | class ColumnDecsList; 45 | 46 | class TableConstraintList; 47 | 48 | class Expr { 49 | public: 50 | 51 | Expr(); 52 | 53 | explicit Expr(int i); 54 | 55 | explicit Expr(float f); 56 | 57 | explicit Expr(const char *s, bool is_date = false); 58 | 59 | explicit Expr(bool b); 60 | 61 | Expr(Expr *left, ArithOp op, Expr *right = nullptr); 62 | 63 | Expr(Expr *left, CompOp op, Expr *right); 64 | 65 | Expr(Expr *left, LogicOp op, Expr *right); 66 | 67 | explicit Expr(const AttributeNode *); 68 | 69 | explicit Expr(const BindAttribute &attribute); 70 | 71 | const void *getValue(); 72 | 73 | void postorder(std::function callback, std::function stop_condition = nullptr); 74 | 75 | void calculate(const char *data, const std::string &relationName = ""); 76 | 77 | void type_check(); 78 | 79 | void convert_to_float(); 80 | 81 | void init_calculate(const std::string &tableName = ""); 82 | 83 | std::string to_string() const; 84 | 85 | bool is_true(); 86 | 87 | bool operator<(const Expr &expr) const; 88 | 89 | Expr &operator+=(const Expr &expr); 90 | 91 | void assign(const Expr &expr); 92 | 93 | ~Expr(); 94 | 95 | union { 96 | int i; 97 | float f; 98 | bool b; 99 | } value; 100 | 101 | std::string value_s = ""; 102 | 103 | const AttributeNode *attribute = nullptr; 104 | BindAttribute attrInfo; 105 | bool is_null = true; 106 | bool calculated = false; 107 | int tableIndex = -1; 108 | int columnIndex = -1; 109 | 110 | Expr *left = nullptr, *right = nullptr; 111 | union { 112 | ArithOp arith = ArithOp::NO_OP; 113 | CompOp comp; 114 | LogicOp logic; 115 | AttrType constType; 116 | } oper; 117 | 118 | NodeType nodeType = NodeType::CONST_NODE; 119 | AttrType dataType; 120 | }; 121 | 122 | 123 | #endif //MYDB_EXPR_H 124 | -------------------------------------------------------------------------------- /src/parser/Tree.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // 4 | // Created by Zhengxiao Du on 11/24/18. 5 | // 6 | 7 | #ifndef DATABASE_TREE_H 8 | #define DATABASE_TREE_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include "../Constants.h" 14 | #include "Expr.h" 15 | 16 | class Tree; 17 | 18 | //Sys Statement 19 | class ShowDatabase; 20 | 21 | // Table Statement 22 | class Select; 23 | 24 | class Insert; 25 | 26 | class Update; 27 | 28 | class Delete; 29 | 30 | class CreateTable; 31 | 32 | class DropTable; 33 | 34 | // Database statement 35 | class CreateDatabase; 36 | 37 | class UseDatabase; 38 | 39 | class DropDatabase; 40 | 41 | // Index Statement 42 | class CreateIndex; 43 | 44 | class DropIndex; 45 | 46 | // DataType 47 | class ColumnDecsList; 48 | 49 | class ColumnNode; 50 | 51 | class AttributeList; 52 | 53 | class AttributeNode; 54 | 55 | class IdentList; 56 | 57 | class ConstValueList; 58 | 59 | class ConstValueLists; 60 | 61 | class SetClauseList; 62 | 63 | class TableConstraint; 64 | 65 | class TableConstraintList; 66 | 67 | class Tree { 68 | public: 69 | virtual void visit() { 70 | assert(false); 71 | } 72 | 73 | virtual ~Tree() = default; 74 | 75 | static void setInstance(Tree *t) { 76 | delete tree; 77 | 78 | tree = t; 79 | } 80 | 81 | static void run() { 82 | if (tree != nullptr) 83 | tree->visit(); 84 | } 85 | 86 | static Tree *tree; 87 | }; 88 | // 89 | //struct AttrValue { 90 | // AttrType type; 91 | // int i; 92 | // float f; 93 | // std::string s; 94 | // bool isNull; 95 | // 96 | // // Definitions are in Tree.cpp 97 | // bool operator==(const AttrValue &val) const; 98 | // 99 | // bool operator!=(const AttrValue &val) const; 100 | // 101 | // bool operator>=(const AttrValue &val) const; 102 | // 103 | // bool operator<=(const AttrValue &val) const; 104 | // 105 | // bool operator>(const AttrValue &val) const; 106 | // 107 | // bool operator<(const AttrValue &val) const; 108 | //}; 109 | 110 | 111 | class ShowDatabase : public Tree { 112 | public: 113 | explicit ShowDatabase(std::string DBname) : DBname(std::move(DBname)) {}; 114 | 115 | void visit() override; 116 | 117 | std::string DBname; 118 | }; 119 | 120 | 121 | class Select : public Tree { 122 | public: 123 | Select(AttributeList *attributes, 124 | IdentList *relations, 125 | Expr *whereClause, 126 | const char *groupAttrName = ""); 127 | 128 | Select(IdentList *relations, Expr *whereClause); 129 | 130 | ~Select() override; 131 | 132 | void visit() override; 133 | 134 | AttributeList *attributes; 135 | IdentList *relations; 136 | Expr *whereClause; 137 | std::string groupAttrName; 138 | }; 139 | 140 | 141 | class Insert : public Tree { 142 | public: 143 | Insert(const char *relationName, IdentList * columnList, ConstValueLists *insertValueTree); 144 | 145 | ~Insert() override; 146 | 147 | void visit() override; 148 | 149 | std::string relationName; 150 | IdentList * columnList; 151 | ConstValueLists *insertValueTree; 152 | }; 153 | 154 | 155 | class Update : public Tree { 156 | public: 157 | Update(std::string relationName, 158 | SetClauseList *setClauses, 159 | Expr *whereClause); 160 | 161 | ~Update() override; 162 | 163 | void visit() override; 164 | 165 | std::string relationName; 166 | SetClauseList *setClauses; 167 | Expr *whereClause; 168 | }; 169 | 170 | 171 | class Delete : public Tree { 172 | public: 173 | Delete(const char *relationName, Expr *whereClause); 174 | 175 | ~Delete() override; 176 | 177 | void visit() override; 178 | 179 | std::string relationName; 180 | Expr *whereClause; 181 | }; 182 | 183 | 184 | class UseDatabase : public Tree { 185 | public: 186 | explicit UseDatabase(const char *dbName); 187 | 188 | void visit() override; 189 | 190 | std::string dbName; 191 | }; 192 | 193 | 194 | class CreateDatabase : public Tree { 195 | public: 196 | explicit CreateDatabase(const char *dbName); 197 | 198 | ~CreateDatabase() override; 199 | 200 | void visit() override; 201 | 202 | std::string dbName; 203 | }; 204 | 205 | class DescTable : public Tree { 206 | public: 207 | explicit DescTable(const char *relName); 208 | 209 | ~DescTable() override; 210 | 211 | void visit() override; 212 | 213 | std::string tableName; 214 | }; 215 | 216 | 217 | class CreateTable : public Tree { 218 | public: 219 | CreateTable(const char *tableName, ColumnDecsList *columns, TableConstraintList *tableConstraints); 220 | 221 | ~CreateTable() override; 222 | 223 | void visit() override; 224 | 225 | std::string tableName; 226 | ColumnDecsList *columns; 227 | TableConstraintList *tableConstraints; 228 | }; 229 | 230 | 231 | class DropDatabase : public Tree { 232 | public: 233 | explicit DropDatabase(const char *dbName); 234 | 235 | ~DropDatabase() override; 236 | 237 | void visit() override; 238 | 239 | std::string dbName; 240 | }; 241 | 242 | 243 | class DropTable : public Tree { 244 | public: 245 | explicit DropTable(const char *tableName); 246 | 247 | ~DropTable() override; 248 | 249 | void visit() override; 250 | 251 | std::string tableName; 252 | }; 253 | 254 | 255 | class CreateIndex : public Tree { 256 | public: 257 | CreateIndex(const char *relName, AttributeNode *attr); 258 | 259 | ~CreateIndex() override; 260 | 261 | void visit() override; 262 | 263 | std::string relName; 264 | AttributeNode *attribute; 265 | }; 266 | 267 | 268 | class DropIndex : public Tree { 269 | public: 270 | DropIndex(const char *relName, AttributeNode *attr); 271 | 272 | ~DropIndex() override; 273 | 274 | void visit() override; 275 | 276 | std::string tableName; 277 | AttributeNode *attribute; 278 | }; 279 | 280 | class ColumnDecsList : public Tree { 281 | public: 282 | ColumnDecsList(); 283 | 284 | ~ColumnDecsList() override; 285 | 286 | void addColumn(ColumnNode *); 287 | 288 | int getColumnCount(); 289 | 290 | AttrInfo *getAttrInfos(); 291 | 292 | void deleteAttrInfos(); 293 | 294 | std::pair findColumn(std::string name); 295 | 296 | std::vector columns; 297 | AttrInfo *attrInfos = nullptr; 298 | }; 299 | 300 | 301 | class ColumnNode : public Tree { 302 | public: 303 | ColumnNode(const char *columnName, AttrType type, int length = 4, 304 | int columnFlag = 0); 305 | 306 | ~ColumnNode() override; 307 | 308 | AttrInfo getAttrInfo() const; 309 | 310 | friend class ColumnDecsList; 311 | 312 | std::string columnName; 313 | AttrType type; 314 | int size; 315 | int length; 316 | int columnFlag; 317 | }; 318 | 319 | // For select attribute 320 | class AttributeNode : public Tree { 321 | public: 322 | AttributeNode() = default; 323 | AttributeNode(const char *relationName, const char *attributeName, 324 | AggregationType aggregationType = AggregationType::T_NONE); 325 | 326 | explicit AttributeNode(const char *attributeName, AggregationType aggregationType = AggregationType::T_NONE); 327 | 328 | ~AttributeNode() override; 329 | 330 | bool operator==(const AttributeNode &attribute) const; 331 | 332 | struct AttributeDescriptor { 333 | std::string relName; 334 | std::string attrName; 335 | AggregationType aggregationType; 336 | 337 | explicit AttributeDescriptor(std::string relName = "", 338 | std::string attrName = "", 339 | AggregationType aggregationType = AggregationType::T_NONE) : 340 | relName(std::move(relName)), attrName(std::move(attrName)), aggregationType(aggregationType) {} 341 | }; 342 | 343 | AttributeDescriptor getDescriptor() const; 344 | 345 | std::string table; 346 | std::string attribute; 347 | AggregationType aggregationType = AggregationType::T_NONE; 348 | }; 349 | 350 | 351 | class AttributeList : public Tree { 352 | public: 353 | AttributeList(); 354 | 355 | ~AttributeList() override; 356 | 357 | void addAttribute(AttributeNode *attribute); 358 | 359 | std::vector getDescriptors() const; 360 | 361 | std::vector attributes; 362 | }; 363 | 364 | 365 | class IdentList : public Tree { 366 | public: 367 | IdentList(); 368 | 369 | ~IdentList() override; 370 | 371 | void addIdent(const char *ident); 372 | 373 | std::vector idents; 374 | }; 375 | 376 | class ConstValueList : public Tree { 377 | public: 378 | ConstValueList(); 379 | 380 | ~ConstValueList() override; 381 | 382 | void addConstValue(Expr *constValue); 383 | 384 | // std::vector getConstValues(); 385 | 386 | std::vector constValues; 387 | }; 388 | 389 | // For insert values 390 | class ConstValueLists : public Tree { 391 | public: 392 | explicit ConstValueLists(); 393 | 394 | ~ConstValueLists() override; 395 | 396 | void addConstValues(ConstValueList *constValuesTree); 397 | 398 | std::vector values; 399 | }; 400 | 401 | class SetClauseList : public Tree { 402 | public: 403 | SetClauseList(); 404 | 405 | void addSetClause(AttributeNode *, Expr *); 406 | 407 | std::vector> clauses; 408 | }; 409 | 410 | class TableConstraint : public Tree { 411 | public: 412 | explicit TableConstraint(IdentList *column_list); 413 | 414 | TableConstraint(const char *column_name, const char *table, const char *column); 415 | 416 | TableConstraint(const char *column_name, ConstValueList *const_values); 417 | 418 | ~TableConstraint() override; 419 | 420 | ConstraintType type; 421 | ConstValueList *const_values = nullptr; 422 | IdentList *column_list = nullptr; 423 | std::string column_name; 424 | std::string foreign_table; 425 | std::string foreign_column; 426 | }; 427 | 428 | class TableConstraintList : public Tree { 429 | public: 430 | TableConstraintList() = default; 431 | 432 | ~TableConstraintList() override; 433 | 434 | void addTbDec(TableConstraint *dec); 435 | 436 | std::vector tbDecs; 437 | }; 438 | 439 | 440 | #endif //DATABASE_TREE_H 441 | -------------------------------------------------------------------------------- /src/parser/lex.l: -------------------------------------------------------------------------------- 1 | %option caseless 2 | 3 | %{ 4 | #include "Tree.h" 5 | #include "parser.tab.hpp" 6 | #ifdef __STRICT_ANSI__ 7 | #undef __STRICT_ANSI__ 8 | #include 9 | #define __STRICT_ANSI__ 10 | #else 11 | #include 12 | #endif 13 | #include 14 | 15 | char *get_text(const char *text, bool strip = false) { 16 | int length = strlen(text); 17 | char *buffer; 18 | if(not strip) { 19 | buffer = new char[length + 1]; 20 | memcpy(buffer, text, (length + 1) * sizeof(char)); 21 | } else { 22 | buffer = new char[length - 1]; 23 | memcpy(buffer, text + 1, (length - 1) * sizeof(char)); 24 | buffer[length - 2] = 0; 25 | } 26 | 27 | return buffer; 28 | } 29 | 30 | void yyerror(const char *); 31 | 32 | %} 33 | 34 | 35 | DIGIT [0-9] 36 | INTEGER {DIGIT}+ 37 | FLOAT {DIGIT}+\.{DIGIT}* 38 | IDENTIFIER [a-z][a-z0-9_]* 39 | WHITESPACE [ \t\n] 40 | DATE_FORMAT '[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}' 41 | STRING '(\\\\|\\n|\\r|\\\"|\\0|\\'|[^'\\])*' 42 | 43 | 44 | %x SQL_COMMENT 45 | 46 | %% 47 | 48 | "--" {BEGIN(SQL_COMMENT);} 49 | "\n" {BEGIN(INITIAL);} 50 | . {} 51 | 52 | "," { /*printf(", "); */ return yytext[0]; } 53 | "(" { /*printf("( "); */ return yytext[0]; } 54 | ")" { /*printf(") "); */ return yytext[0]; } 55 | "." { /*printf(". "); */ return yytext[0]; } 56 | ";" { /*printf("; "); */ return yytext[0]; } 57 | "+" { /*printf("+ "); */ return yytext[0]; } 58 | "-" { /*printf("- "); */ return yytext[0]; } 59 | "/" { /*printf("/ "); */ return yytext[0]; } 60 | "*" { /*printf("* "); */ return yytext[0]; } 61 | QUIT { /*printf("QUIT");*/return QUIT; } 62 | CREATE { /*printf("CREATE "); */ return CREATE; } 63 | DROP { /*printf("DROP "); */ return DROP; } 64 | USE { /*printf("USE "); */ return USE; } 65 | SHOW { /*printf("SHOW "); */ return SHOW; } 66 | DATABASES { /*printf("DATABASES "); */ return DATABASES; } 67 | DATABASE { /*printf("DATABASE "); */ return DATABASE; } 68 | TABLE { /*printf("TABLE "); */ return TABLE; } 69 | TABLES { /*printf("TABLES "); */ return TABLES; } 70 | SELECT { /*printf("SELECT "); */ return SELECT; } 71 | DELETE { /*printf("DELETE "); */ return DELETE; } 72 | INSERT { /*printf("INSERT "); */ return INSERT; } 73 | UPDATE { /*printf("UPDATE "); */ return UPDATE; } 74 | VALUES { /*printf("VALUES "); */ return VALUES; } 75 | REFERENCES { /*printf("REFERENCES "); */ return REFERENCES; } 76 | SET { /*printf("SET "); */ return SET; } 77 | FROM { /*printf("FROM "); */ return FROM; } 78 | INTO { /*printf("INTO "); */ return INTO; } 79 | WHERE { /*printf("WHERE "); */ return WHERE; } 80 | SUM { /*printf("SUM "); */ return SUM; } 81 | AVG { /*printf("AVG "); */ return AVG; } 82 | MIN { /*printf("MIN "); */ return MIN; } 83 | MAX { /*printf("MAX "); */ return MAX; } 84 | AND { /*printf("AND "); */ return AND; } 85 | OR { /*printf("OR "); */ return OR; } 86 | "NOT NULL" { /*printf("NOT_NULL "); */ return NOTNULL; } 87 | "PRIMARY KEY" { /*printf("PRIMARY_KEY "); */ return PRIMARY; } 88 | "FOREIGN KEY" { /*printf("FOREIGN_KEY "); */ return FOREIGN; } 89 | "GROUP BY" { /*printf("GROUP BY ")*/ ; return GROUP; } 90 | LIKE { /*printf("LIKE "); */ return LIKE; } 91 | DESC { /*printf("DESC "); */ return DESC; } 92 | INDEX { /*printf("INDEX "); */ return INDEX; } 93 | CHECK { /*printf("CHECK "); */ return CHECK;} 94 | IN { /*printf("IN "); */ return IN; } 95 | IS { /*printf("IS "); */ return IS; } 96 | NULL { /*printf("NULL "); */ return T_NULL; } 97 | int { /*printf("int "); */ return KINT; } 98 | float { /*printf("float "); */ return KFLOAT; } 99 | char { /*printf("char "); */ return KCHAR; } 100 | varchar { /*printf("varchar "); */ return KVARCHAR; } 101 | date { /*printf("date "); */ return KDATE; } 102 | {FLOAT} { /*printf("FLOAT "); */ sscanf(yytext, "%f", &yylval.fvalue); return FLOAT; } 103 | {INTEGER} { /*printf("INTEGER "); */ sscanf(yytext, "%d", &yylval.ivalue); return INTEGER; } 104 | {DATE_FORMAT} { yylval.string = get_text(yytext, true); return DATE_FORMAT; } 105 | {IDENTIFIER} { yylval.string = get_text(yytext, false); /*printf("IDENTIFIER(%s) ", yylval.string);*/ return IDENTIFIER; } 106 | "=" { /*printf("= ");*/ return EQ; } 107 | ">" { /*printf("> ");*/ return GT; } 108 | "<" { /*printf("< ");*/ return LT; } 109 | ">=" { /*printf(">= ");*/ return GE; } 110 | "<=" { /*printf("<= ");*/ return LE; } 111 | "<>" { /*printf("<> ");*/ return NE; } 112 | 113 | {STRING} { yylval.string = get_text(yytext, false); /*printf("STRING(%s) ", yylval.string);*/ return STRING; } 114 | {WHITESPACE} {} 115 | . { yyerror((std::string("invalid char ") + yytext).c_str()); } 116 | 117 | %% 118 | 119 | int yywrap() { 120 | return 1; 121 | } -------------------------------------------------------------------------------- /src/pf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/pf.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_buffermgr.h 5 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_hashtable.h 6 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_internal.h 7 | PARENT_SCOPE 8 | ) 9 | 10 | set(SOURCE 11 | ${SOURCE} 12 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_buffermgr.cc 13 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_error.cc 14 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_filehandle.cc 15 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_hashtable.cc 16 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_manager.cc 17 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_pagehandle.cc 18 | ${CMAKE_CURRENT_SOURCE_DIR}/pf_statistics.cc 19 | PARENT_SCOPE 20 | ) -------------------------------------------------------------------------------- /src/pf/pf.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf.h 3 | // Description: Paged File component interface 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // Jason McHugh (mchughj@cs.stanford.edu) 7 | // 8 | // 1997: Default page size is now 4k. 9 | // Some additional constants used for the statistics. 10 | // 1998: PageNum is now an int instead of short int. 11 | // Allow chunks from the buffer manager to not be associated with 12 | // a particular file. Allows students to use main memory chunks 13 | // that are associated with (and limited by) the buffer. 14 | // 2005: Added GetLastPage and GetPrevPage for rocking 15 | 16 | #ifndef PF_H 17 | #define PF_H 18 | 19 | #include "../Constants.h" 20 | 21 | // 22 | // PageNum: uniquely identifies a page in a file 23 | // 24 | typedef long PageNum; 25 | 26 | // Page Size 27 | // 28 | // Each page stores some header information. The PF_PageHdr is defined 29 | // in pf_internal.h and contains the information that we would store. 30 | // Unfortunately, we cannot use sizeof(PF_PageHdr) here, but it is an 31 | // int and we simply use that. 32 | // 33 | const int PF_PAGE_SIZE = (4096 - sizeof(int)); 34 | 35 | // 36 | // PF_PageHandle: PF page interface 37 | // 38 | class PF_PageHandle { 39 | friend class PF_FileHandle; 40 | public: 41 | PF_PageHandle (); // Default constructor 42 | ~PF_PageHandle (); // Destructor 43 | 44 | // Copy constructor 45 | PF_PageHandle (const PF_PageHandle &pageHandle); 46 | // Overloaded = 47 | PF_PageHandle& operator=(const PF_PageHandle &pageHandle); 48 | 49 | RC GetData (char *&pData) const; // Set pData to point to 50 | // the page contents 51 | RC GetPageNum (PageNum &pageNum) const; // Return the page number 52 | private: 53 | int pageNum; // page number 54 | char *pPageData; // pointer to page data 55 | }; 56 | 57 | // 58 | // PF_FileHdr: Header structure for files 59 | // 60 | struct PF_FileHdr { 61 | int firstFree; // first free page in the linked list 62 | int numPages; // # of pages in the file 63 | }; 64 | 65 | // 66 | // PF_FileHandle: PF File interface 67 | // 68 | class PF_BufferMgr; 69 | 70 | class PF_FileHandle { 71 | friend class PF_Manager; 72 | public: 73 | PF_FileHandle (); // Default constructor 74 | ~PF_FileHandle (); // Destructor 75 | 76 | // Copy constructor 77 | PF_FileHandle (const PF_FileHandle &fileHandle); 78 | 79 | // Overload = 80 | PF_FileHandle& operator=(const PF_FileHandle &fileHandle); 81 | 82 | // Get the first page 83 | RC GetFirstPage(PF_PageHandle &pageHandle) const; 84 | // Get the next page after current 85 | RC GetNextPage (PageNum current, PF_PageHandle &pageHandle) const; 86 | // Get a specific page 87 | RC GetThisPage (PageNum pageNum, PF_PageHandle &pageHandle) const; 88 | // Get the last page 89 | RC GetLastPage(PF_PageHandle &pageHandle) const; 90 | // Get the prev page after current 91 | RC GetPrevPage (PageNum current, PF_PageHandle &pageHandle) const; 92 | 93 | RC AllocatePage(PF_PageHandle &pageHandle); // Allocate a new page 94 | RC DisposePage (PageNum pageNum); // Dispose of a page 95 | RC MarkDirty (PageNum pageNum) const; // Mark page as dirty 96 | RC UnpinPage (PageNum pageNum) const; // Unpin the page 97 | 98 | // Flush pages from buffer pool. Will write dirty pages to disk. 99 | RC FlushPages () const; 100 | 101 | // Force a page or pages to disk (but do not remove from the buffer pool) 102 | RC ForcePages (PageNum pageNum=ALL_PAGES) const; 103 | private: 104 | 105 | // IsValidPageNum will return TRUE if page number is valid and FALSE 106 | // otherwise 107 | int IsValidPageNum (PageNum pageNum) const; 108 | 109 | PF_BufferMgr *pBufferMgr; // pointer to buffer manager 110 | PF_FileHdr hdr; // file header 111 | int bFileOpen; // file open flag 112 | int bHdrChanged; // dirty flag for file hdr 113 | int unixfd; // OS file descriptor 114 | }; 115 | 116 | // 117 | // PF_Manager: provides PF file management 118 | // 119 | class PF_Manager { 120 | public: 121 | PF_Manager (); // Constructor 122 | ~PF_Manager (); // Destructor 123 | RC CreateFile (const char *fileName); // Create a new file 124 | RC DestroyFile (const char *fileName); // Delete a file 125 | 126 | // Open and close file methods 127 | RC OpenFile (const char *fileName, PF_FileHandle &fileHandle); 128 | RC CloseFile (PF_FileHandle &fileHandle); 129 | 130 | // Three methods that manipulate the buffer manager. The calls are 131 | // forwarded to the PF_BufferMgr instance and are called by parse.y 132 | // when the user types in a system command. 133 | RC ClearBuffer (); 134 | RC PrintBuffer (); 135 | RC ResizeBuffer (int iNewSize); 136 | 137 | // Three Methods for manipulating raw memory buffers. These memory 138 | // locations are handled by the buffer manager, but are not 139 | // associated with a particular file. These should be used if you 140 | // want to create structures in memory that are bounded by the size 141 | // of the buffer pool. 142 | // 143 | // Note: If you lose the pointer to the buffer that is passed back 144 | // from AllocateBlock or do not call DisposeBlock with that pointer 145 | // then the internal memory will always remain "pinned" in the buffer 146 | // pool and you will have lost a slot in the buffer pool. 147 | 148 | // Return the size of the block that can be allocated. 149 | RC GetBlockSize (int &length) const; 150 | 151 | // Allocate a memory chunk that lives in buffer manager 152 | RC AllocateBlock (char *&buffer); 153 | // Dispose of a memory chunk managed by the buffer manager. 154 | RC DisposeBlock (char *buffer); 155 | 156 | static PF_Manager & getInstance(); 157 | 158 | private: 159 | PF_BufferMgr *pBufferMgr; // page-buffer manager 160 | }; 161 | 162 | // 163 | // Print-error function and PF return code defines 164 | // 165 | void PF_PrintError(RC rc); 166 | 167 | #define PF_PAGEPINNED (START_PF_WARN + 0) // page pinned in buffer 168 | #define PF_PAGENOTINBUF (START_PF_WARN + 1) // page isn't pinned in buffer 169 | #define PF_INVALIDPAGE (START_PF_WARN + 2) // invalid page number 170 | #define PF_FILEOPEN (START_PF_WARN + 3) // file is open 171 | #define PF_CLOSEDFILE (START_PF_WARN + 4) // file is closed 172 | #define PF_PAGEFREE (START_PF_WARN + 5) // page already free 173 | #define PF_PAGEUNPINNED (START_PF_WARN + 6) // page already unpinned 174 | #define PF_EOF (START_PF_WARN + 7) // end of file 175 | #define PF_TOOSMALL (START_PF_WARN + 8) // Resize buffer too small 176 | #define PF_LASTWARN PF_TOOSMALL 177 | 178 | #define PF_NOMEM (START_PF_ERR - 0) // no memory 179 | #define PF_NOBUF (START_PF_ERR - 1) // no buffer space 180 | #define PF_INCOMPLETEREAD (START_PF_ERR - 2) // incomplete read from file 181 | #define PF_INCOMPLETEWRITE (START_PF_ERR - 3) // incomplete write to file 182 | #define PF_HDRREAD (START_PF_ERR - 4) // incomplete read of header 183 | #define PF_HDRWRITE (START_PF_ERR - 5) // incomplete write to header 184 | 185 | // Internal errors 186 | #define PF_PAGEINBUF (START_PF_ERR - 6) // new page already in buffer 187 | #define PF_HASHNOTFOUND (START_PF_ERR - 7) // hash table entry not found 188 | #define PF_HASHPAGEEXIST (START_PF_ERR - 8) // page already in hash table 189 | #define PF_INVALIDNAME (START_PF_ERR - 9) // invalid PC file name 190 | 191 | // Error in UNIX system call or library routine 192 | #define PF_UNIX (START_PF_ERR - 10) // Unix error 193 | #define PF_LASTERROR PF_UNIX 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /src/pf/pf_buffermgr.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_buffermgr.h 3 | // Description: PF_BufferMgr class interface 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // Jason McHugh (mchughj@cs.stanford.edu) 7 | // 8 | // 1997: When requesting a page from the buffer manager the page requested 9 | // is now promoted to the MRU slot. 10 | // 1998: Allow chunks from the buffer manager to not be associated with 11 | // a particular file. Allows students to use main memory chunks that 12 | // are associated with (and limited by) the buffer. 13 | // 14 | 15 | #ifndef PF_BUFFERMGR_H 16 | #define PF_BUFFERMGR_H 17 | 18 | #include "pf_internal.h" 19 | #include "pf_hashtable.h" 20 | 21 | // 22 | // Defines 23 | // 24 | 25 | // INVALID_SLOT is used within the PF_BufferMgr class which tracks a list 26 | // of PF_BufPageDesc. Inside the PF_BufPageDesc are integer "pointers" to 27 | // next and prev items. INVALID_SLOT is used to indicate no previous or 28 | // next. 29 | #define INVALID_SLOT (-1) 30 | 31 | // 32 | // PF_BufPageDesc - struct containing data about a page in the buffer 33 | // 34 | struct PF_BufPageDesc { 35 | char *pData; // page contents 36 | int next; // next in the linked list of buffer pages 37 | int prev; // prev in the linked list of buffer pages 38 | int bDirty; // TRUE if page is dirty 39 | short int pinCount; // pin count 40 | PageNum pageNum; // page number for this page 41 | int fd; // OS file descriptor of this page 42 | }; 43 | 44 | // 45 | // PF_BufferMgr - manage the page buffer 46 | // 47 | class PF_BufferMgr { 48 | public: 49 | 50 | PF_BufferMgr (int numPages); // Constructor - allocate 51 | // numPages buffer pages 52 | ~PF_BufferMgr (); // Destructor 53 | 54 | // Read pageNum into buffer, point *ppBuffer to location 55 | RC GetPage (int fd, PageNum pageNum, char **ppBuffer, 56 | int bMultiplePins = TRUE); 57 | // Allocate a new page in the buffer, point *ppBuffer to its location 58 | RC AllocatePage (int fd, PageNum pageNum, char **ppBuffer); 59 | 60 | RC MarkDirty (int fd, PageNum pageNum); // Mark page dirty 61 | RC UnpinPage (int fd, PageNum pageNum); // Unpin page from the buffer 62 | RC FlushPages (int fd); // Flush pages for file 63 | 64 | // Force a page to the disk, but do not remove from the buffer pool 65 | RC ForcePages (int fd, PageNum pageNum); 66 | 67 | 68 | // Remove all entries from the Buffer Manager. 69 | RC ClearBuffer (); 70 | // Display all entries in the buffer 71 | RC PrintBuffer (); 72 | 73 | // Attempts to resize the buffer to the new size 74 | RC ResizeBuffer (int iNewSize); 75 | 76 | // Three Methods for manipulating raw memory buffers. These memory 77 | // locations are handled by the buffer manager, but are not 78 | // associated with a particular file. These should be used if you 79 | // want memory that is bounded by the size of the buffer pool. 80 | 81 | // Return the size of the block that can be allocated. 82 | RC GetBlockSize (int &length) const; 83 | 84 | // Allocate a memory chunk that lives in buffer manager 85 | RC AllocateBlock (char *&buffer); 86 | // Dispose of a memory chunk managed by the buffer manager. 87 | RC DisposeBlock (char *buffer); 88 | 89 | private: 90 | RC InsertFree (int slot); // Insert slot at head of free 91 | RC LinkHead (int slot); // Insert slot at head of used 92 | RC Unlink (int slot); // Unlink slot 93 | RC InternalAlloc(int &slot); // Get a slot to use 94 | 95 | // Read a page 96 | RC ReadPage (int fd, PageNum pageNum, char *dest); 97 | 98 | // Write a page 99 | RC WritePage (int fd, PageNum pageNum, char *source); 100 | 101 | // Init the page desc entry 102 | RC InitPageDesc (int fd, PageNum pageNum, int slot); 103 | 104 | PF_BufPageDesc *bufTable; // info on buffer pages 105 | PF_HashTable hashTable; // Hash foreign_table object 106 | int numPages; // # of pages in the buffer 107 | int pageSize; // Size of pages in the buffer 108 | int first; // MRU page slot 109 | int last; // LRU page slot 110 | int free; // head of free list 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/pf/pf_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_error.cc 3 | // Description: PF_PrintError function 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include "pf_internal.h" 12 | 13 | using namespace std; 14 | 15 | // 16 | // Error foreign_table 17 | // 18 | static char *PF_WarnMsg[] = { 19 | (char*)"page pinned in buffer", 20 | (char*)"page is not in the buffer", 21 | (char*)"invalid page number", 22 | (char*)"file open", 23 | (char*)"invalid file descriptor (file closed)", 24 | (char*)"page already free", 25 | (char*)"page already unpinned", 26 | (char*)"end of file", 27 | (char*)"attempting to resize the buffer too small", 28 | (char*)"invalid filename" 29 | }; 30 | 31 | static char *PF_ErrorMsg[] = { 32 | (char*)"no memory", 33 | (char*)"no buffer space", 34 | (char*)"incomplete read of page from file", 35 | (char*)"incomplete write of page to file", 36 | (char*)"incomplete read of header from file", 37 | (char*)"incomplete write of header from file", 38 | (char*)"new page to be allocated already in buffer", 39 | (char*)"hash foreign_table entry not found", 40 | (char*)"page already in hash foreign_table", 41 | (char*)"invalid file name" 42 | }; 43 | 44 | // 45 | // PF_PrintError 46 | // 47 | // Desc: Send a message corresponding to a PF return code to cerr 48 | // Assumes PF_UNIX is last valid PF return code 49 | // In: rc - return code for which a message is desired 50 | // 51 | void PF_PrintError(RC rc) 52 | { 53 | // Check the return code is within proper limits 54 | if (rc >= START_PF_WARN && rc <= PF_LASTWARN) 55 | // Print warning 56 | cerr << "PF warning: " << PF_WarnMsg[rc - START_PF_WARN] << "\n"; 57 | // Error codes are negative, so invert everything 58 | else if (-rc >= -START_PF_ERR && -rc < -PF_LASTERROR) 59 | // Print error 60 | cerr << "PF error: " << PF_ErrorMsg[-rc + START_PF_ERR] << "\n"; 61 | else if (rc == PF_UNIX) 62 | #ifdef PC 63 | cerr << "OS error\n"; 64 | #else 65 | cerr << strerror(errno) << "\n"; 66 | #endif 67 | else if (rc == 0) 68 | cerr << "PF_PrintError called with return code of 0\n"; 69 | else 70 | cerr << "PF error: " << rc << " is out of bounds\n"; 71 | } 72 | -------------------------------------------------------------------------------- /src/pf/pf_filehandle.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_filehandle.cc 3 | // Description: PF_FileHandle class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include 9 | #include 10 | #include "pf_internal.h" 11 | #include "pf_buffermgr.h" 12 | 13 | // 14 | // PF_FileHandle 15 | // 16 | // Desc: Default constructor for a file handle object 17 | // A File object provides access to an open file. 18 | // It is used to allocate, dispose and fetch pages. 19 | // It is constructed here but must be passed to PF_Manager::OpenFile() in 20 | // order to be used to access the pages of a file. 21 | // It should be passed to PF_Manager::CloseFile() to close the file. 22 | // A file handle object contains a pointer to the file data stored 23 | // in the file foreign_table managed by PF_Manager. It passes the file's unix 24 | // file descriptor to the buffer manager to access pages of the file. 25 | // 26 | PF_FileHandle::PF_FileHandle() 27 | { 28 | // Initialize local variables 29 | bFileOpen = FALSE; 30 | pBufferMgr = NULL; 31 | } 32 | 33 | // 34 | // ~PF_FileHandle 35 | // 36 | // Desc: Destroy the file handle object 37 | // If the file handle object refers to an open file, the file will 38 | // NOT be closed. 39 | // 40 | PF_FileHandle::~PF_FileHandle() 41 | { 42 | // Don't need to do anything 43 | } 44 | 45 | // 46 | // PF_FileHandle 47 | // 48 | // Desc: copy constructor 49 | // In: fileHandle - file handle object from which to construct this object 50 | // 51 | PF_FileHandle::PF_FileHandle(const PF_FileHandle &fileHandle) 52 | { 53 | // Just copy the data members since there is no memory allocation involved 54 | this->pBufferMgr = fileHandle.pBufferMgr; 55 | this->hdr = fileHandle.hdr; 56 | this->bFileOpen = fileHandle.bFileOpen; 57 | this->bHdrChanged = fileHandle.bHdrChanged; 58 | this->unixfd = fileHandle.unixfd; 59 | } 60 | 61 | // 62 | // operator= 63 | // 64 | // Desc: overload = operator 65 | // If this file handle object refers to an open file, the file will 66 | // NOT be closed. 67 | // In: fileHandle - file handle object to set this object equal to 68 | // Ret: reference to *this 69 | // 70 | PF_FileHandle& PF_FileHandle::operator= (const PF_FileHandle &fileHandle) 71 | { 72 | // Test for self-assignment 73 | if (this != &fileHandle) { 74 | 75 | // Just copy the members since there is no memory allocation involved 76 | this->pBufferMgr = fileHandle.pBufferMgr; 77 | this->hdr = fileHandle.hdr; 78 | this->bFileOpen = fileHandle.bFileOpen; 79 | this->bHdrChanged = fileHandle.bHdrChanged; 80 | this->unixfd = fileHandle.unixfd; 81 | } 82 | 83 | // Return a reference to this 84 | return (*this); 85 | } 86 | 87 | // 88 | // GetFirstPage 89 | // 90 | // Desc: Get the first page in a file 91 | // The file handle must refer to an open file 92 | // Out: pageHandle - becomes a handle to the first page of the file 93 | // The referenced page is pinned in the buffer pool. 94 | // Ret: PF return code 95 | // 96 | RC PF_FileHandle::GetFirstPage(PF_PageHandle &pageHandle) const 97 | { 98 | return (GetNextPage((PageNum)-1, pageHandle)); 99 | } 100 | 101 | // 102 | // GetLastPage 103 | // 104 | // Desc: Get the last page in a file 105 | // The file handle must refer to an open file 106 | // Out: pageHandle - becomes a handle to the last page of the file 107 | // The referenced page is pinned in the buffer pool. 108 | // Ret: PF return code 109 | // 110 | RC PF_FileHandle::GetLastPage(PF_PageHandle &pageHandle) const 111 | { 112 | return (GetPrevPage((PageNum)hdr.numPages, pageHandle)); 113 | } 114 | 115 | // 116 | // GetNextPage 117 | // 118 | // Desc: Get the next (valid) page after current 119 | // The file handle must refer to an open file 120 | // In: current - get the next valid page after this page number 121 | // current can refer to a page that has been disposed 122 | // Out: pageHandle - becomes a handle to the next page of the file 123 | // The referenced page is pinned in the buffer pool. 124 | // Ret: PF_EOF, or another PF return code 125 | // 126 | RC PF_FileHandle::GetNextPage(PageNum current, PF_PageHandle &pageHandle) const 127 | { 128 | int rc; // return code 129 | 130 | // File must be open 131 | if (!bFileOpen) 132 | return (PF_CLOSEDFILE); 133 | 134 | // Validate page number (note that -1 is acceptable here) 135 | if (current != -1 && !IsValidPageNum(current)) 136 | return (PF_INVALIDPAGE); 137 | 138 | // Scan the file until a valid used page is found 139 | for (current++; current < hdr.numPages; current++) { 140 | 141 | // If this is a valid (used) page, we're done 142 | if (!(rc = GetThisPage(current, pageHandle))) 143 | return (0); 144 | 145 | // If unexpected error, return it 146 | if (rc != PF_INVALIDPAGE) 147 | return (rc); 148 | } 149 | 150 | // No valid (used) page found 151 | return (PF_EOF); 152 | } 153 | 154 | // 155 | // GetPrevPage 156 | // 157 | // Desc: Get the prev (valid) page after current 158 | // The file handle must refer to an open file 159 | // In: current - get the prev valid page before this page number 160 | // current can refer to a page that has been disposed 161 | // Out: pageHandle - becomes a handle to the prev page of the file 162 | // The referenced page is pinned in the buffer pool. 163 | // Ret: PF_EOF, or another PF return code 164 | // 165 | RC PF_FileHandle::GetPrevPage(PageNum current, PF_PageHandle &pageHandle) const 166 | { 167 | int rc; // return code 168 | 169 | // File must be open 170 | if (!bFileOpen) 171 | return (PF_CLOSEDFILE); 172 | 173 | // Validate page number (note that hdr.numPages is acceptable here) 174 | if (current != hdr.numPages && !IsValidPageNum(current)) 175 | return (PF_INVALIDPAGE); 176 | 177 | // Scan the file until a valid used page is found 178 | for (current--; current >= 0; current--) { 179 | 180 | // If this is a valid (used) page, we're done 181 | if (!(rc = GetThisPage(current, pageHandle))) 182 | return (0); 183 | 184 | // If unexpected error, return it 185 | if (rc != PF_INVALIDPAGE) 186 | return (rc); 187 | } 188 | 189 | // No valid (used) page found 190 | return (PF_EOF); 191 | } 192 | 193 | // 194 | // GetThisPage 195 | // 196 | // Desc: Get a specific page in a file 197 | // The file handle must refer to an open file 198 | // In: pageNum - the number of the page to get 199 | // Out: pageHandle - becomes a handle to the this page of the file 200 | // this function modifies local var's in pageHandle 201 | // The referenced page is pinned in the buffer pool. 202 | // Ret: PF return code 203 | // 204 | RC PF_FileHandle::GetThisPage(PageNum pageNum, PF_PageHandle &pageHandle) const 205 | { 206 | int rc; // return code 207 | char *pPageBuf; // address of page in buffer pool 208 | 209 | // File must be open 210 | if (!bFileOpen) 211 | return (PF_CLOSEDFILE); 212 | 213 | // Validate page number 214 | if (!IsValidPageNum(pageNum)) 215 | return (PF_INVALIDPAGE); 216 | 217 | // Get this page from the buffer manager 218 | if ((rc = pBufferMgr->GetPage(unixfd, pageNum, &pPageBuf))) 219 | return (rc); 220 | 221 | // If the page is valid, then set pageHandle to this page and return ok 222 | if (((PF_PageHdr*)pPageBuf)->nextFree == PF_PAGE_USED) { 223 | 224 | // Set the pageHandle local variables 225 | pageHandle.pageNum = pageNum; 226 | pageHandle.pPageData = pPageBuf + sizeof(PF_PageHdr); 227 | 228 | // Return ok 229 | return (0); 230 | } 231 | 232 | // If the page is *not* a valid one, then unpin the page 233 | if ((rc = UnpinPage(pageNum))) 234 | return (rc); 235 | 236 | return (PF_INVALIDPAGE); 237 | } 238 | 239 | // 240 | // AllocatePage 241 | // 242 | // Desc: Allocate a new page in the file (may get a page which was 243 | // previously disposed) 244 | // The file handle must refer to an open file 245 | // Out: pageHandle - becomes a handle to the newly-allocated page 246 | // this function modifies local var's in pageHandle 247 | // Ret: PF return code 248 | // 249 | RC PF_FileHandle::AllocatePage(PF_PageHandle &pageHandle) 250 | { 251 | int rc; // return code 252 | int pageNum; // new-page number 253 | char *pPageBuf; // address of page in buffer pool 254 | 255 | // File must be open 256 | if (!bFileOpen) 257 | return (PF_CLOSEDFILE); 258 | 259 | // If the free list isn't empty... 260 | if (hdr.firstFree != PF_PAGE_LIST_END) { 261 | pageNum = hdr.firstFree; 262 | 263 | // Get the first free page into the buffer 264 | if ((rc = pBufferMgr->GetPage(unixfd, 265 | pageNum, 266 | &pPageBuf))) 267 | return (rc); 268 | 269 | // Set the first free page to the next page on the free list 270 | hdr.firstFree = ((PF_PageHdr*)pPageBuf)->nextFree; 271 | } 272 | else { 273 | 274 | // The free list is empty... 275 | pageNum = hdr.numPages; 276 | 277 | // Allocate a new page in the file 278 | if ((rc = pBufferMgr->AllocatePage(unixfd, 279 | pageNum, 280 | &pPageBuf))) 281 | return (rc); 282 | 283 | // Increment the number of pages for this file 284 | hdr.numPages++; 285 | } 286 | 287 | // Mark the header as changed 288 | bHdrChanged = TRUE; 289 | 290 | // Mark this page as used 291 | ((PF_PageHdr *)pPageBuf)->nextFree = PF_PAGE_USED; 292 | 293 | // Zero out the page data 294 | memset(pPageBuf + sizeof(PF_PageHdr), 0, PF_PAGE_SIZE); 295 | 296 | // Mark the page dirty because we changed the next pointer 297 | if ((rc = MarkDirty(pageNum))) 298 | return (rc); 299 | 300 | // Set the pageHandle local variables 301 | pageHandle.pageNum = pageNum; 302 | pageHandle.pPageData = pPageBuf + sizeof(PF_PageHdr); 303 | 304 | // Return ok 305 | return (0); 306 | } 307 | 308 | // 309 | // DisposePage 310 | // 311 | // Desc: Dispose of a page 312 | // The file handle must refer to an open file 313 | // PF_PageHandle objects referring to this page should not be used 314 | // after making this call. 315 | // In: pageNum - number of page to dispose 316 | // Ret: PF return code 317 | // 318 | RC PF_FileHandle::DisposePage(PageNum pageNum) 319 | { 320 | int rc; // return code 321 | char *pPageBuf; // address of page in buffer pool 322 | 323 | // File must be open 324 | if (!bFileOpen) 325 | return (PF_CLOSEDFILE); 326 | 327 | // Validate page number 328 | if (!IsValidPageNum(pageNum)) 329 | return (PF_INVALIDPAGE); 330 | 331 | // Get the page (but don't re-pin it if it's already pinned) 332 | if ((rc = pBufferMgr->GetPage(unixfd, 333 | pageNum, 334 | &pPageBuf, 335 | FALSE))) 336 | return (rc); 337 | 338 | // Page must be valid (used) 339 | if (((PF_PageHdr *)pPageBuf)->nextFree != PF_PAGE_USED) { 340 | 341 | // Unpin the page 342 | if ((rc = UnpinPage(pageNum))) 343 | return (rc); 344 | 345 | // Return page already free 346 | return (PF_PAGEFREE); 347 | } 348 | 349 | // Put this page onto the free list 350 | ((PF_PageHdr *)pPageBuf)->nextFree = hdr.firstFree; 351 | hdr.firstFree = pageNum; 352 | bHdrChanged = TRUE; 353 | 354 | // Mark the page dirty because we changed the next pointer 355 | if ((rc = MarkDirty(pageNum))) 356 | return (rc); 357 | 358 | // Unpin the page 359 | if ((rc = UnpinPage(pageNum))) 360 | return (rc); 361 | 362 | // Return ok 363 | return (0); 364 | } 365 | 366 | // 367 | // MarkDirty 368 | // 369 | // Desc: Mark a page as being dirty 370 | // The page will then be written back to disk when it is removed from 371 | // the page buffer 372 | // The file handle must refer to an open file 373 | // In: pageNum - number of page to mark dirty 374 | // Ret: PF return code 375 | // 376 | RC PF_FileHandle::MarkDirty(PageNum pageNum) const 377 | { 378 | // File must be open 379 | if (!bFileOpen) 380 | return (PF_CLOSEDFILE); 381 | 382 | // Validate page number 383 | if (!IsValidPageNum(pageNum)) 384 | return (PF_INVALIDPAGE); 385 | 386 | // Tell the buffer manager to mark the page dirty 387 | return (pBufferMgr->MarkDirty(unixfd, pageNum)); 388 | } 389 | 390 | // 391 | // UnpinPage 392 | // 393 | // Desc: Unpin a page from the buffer manager. 394 | // The page is then free to be written back to disk when necessary. 395 | // PF_PageHandle objects referring to this page should not be used 396 | // after making this call. 397 | // The file handle must refer to an open file. 398 | // In: pageNum - number of the page to unpin 399 | // Ret: PF return code 400 | // 401 | RC PF_FileHandle::UnpinPage(PageNum pageNum) const 402 | { 403 | // File must be open 404 | if (!bFileOpen) 405 | return (PF_CLOSEDFILE); 406 | 407 | // Validate page number 408 | if (!IsValidPageNum(pageNum)) 409 | return (PF_INVALIDPAGE); 410 | 411 | // Tell the buffer manager to unpin the page 412 | return (pBufferMgr->UnpinPage(unixfd, pageNum)); 413 | } 414 | 415 | // 416 | // FlushPages 417 | // 418 | // Desc: Flush all dirty unpinned pages from the buffer manager for this file 419 | // In: Nothing 420 | // Ret: PF_PAGEFIXED warning from buffer manager if pages are pinned or 421 | // other PF error 422 | // 423 | RC PF_FileHandle::FlushPages() const 424 | { 425 | // File must be open 426 | if (!bFileOpen) 427 | return (PF_CLOSEDFILE); 428 | 429 | // If the file header has changed, write it back to the file 430 | if (bHdrChanged) { 431 | 432 | // First seek to the appropriate place 433 | if (lseek(unixfd, 0, L_SET) < 0) 434 | return (PF_UNIX); 435 | 436 | // Write header 437 | int numBytes = write(unixfd, 438 | (char *)&hdr, 439 | sizeof(PF_FileHdr)); 440 | if (numBytes < 0) 441 | return (PF_UNIX); 442 | if (numBytes != sizeof(PF_FileHdr)) 443 | return (PF_HDRWRITE); 444 | 445 | // This function is declared const, but we need to change the 446 | // bHdrChanged variable. Cast away the constness 447 | PF_FileHandle *dummy = (PF_FileHandle *)this; 448 | dummy->bHdrChanged = FALSE; 449 | } 450 | 451 | // Tell Buffer Manager to flush pages 452 | return (pBufferMgr->FlushPages(unixfd)); 453 | } 454 | 455 | // 456 | // ForcePages 457 | // 458 | // Desc: If a page is dirty then force the page from the buffer pool 459 | // onto disk. The page will not be forced out of the buffer pool. 460 | // In: The page number, a default value of ALL_PAGES will be used if 461 | // the client doesn't provide a value. This will force all pages. 462 | // Ret: Standard PF errors 463 | // 464 | // 465 | RC PF_FileHandle::ForcePages(PageNum pageNum) const 466 | { 467 | // File must be open 468 | if (!bFileOpen) 469 | return (PF_CLOSEDFILE); 470 | 471 | // If the file header has changed, write it back to the file 472 | if (bHdrChanged) { 473 | 474 | // First seek to the appropriate place 475 | if (lseek(unixfd, 0, L_SET) < 0) 476 | return (PF_UNIX); 477 | 478 | // Write header 479 | int numBytes = write(unixfd, 480 | (char *)&hdr, 481 | sizeof(PF_FileHdr)); 482 | if (numBytes < 0) 483 | return (PF_UNIX); 484 | if (numBytes != sizeof(PF_FileHdr)) 485 | return (PF_HDRWRITE); 486 | 487 | // This function is declared const, but we need to change the 488 | // bHdrChanged variable. Cast away the constness 489 | PF_FileHandle *dummy = (PF_FileHandle *)this; 490 | dummy->bHdrChanged = FALSE; 491 | } 492 | 493 | // Tell Buffer Manager to Force the page 494 | return (pBufferMgr->ForcePages(unixfd, pageNum)); 495 | } 496 | 497 | 498 | // 499 | // IsValidPageNum 500 | // 501 | // Desc: Internal. Return TRUE if pageNum is a valid page number 502 | // in the file, FALSE otherwise 503 | // In: pageNum - page number to test 504 | // Ret: TRUE or FALSE 505 | // 506 | int PF_FileHandle::IsValidPageNum(PageNum pageNum) const 507 | { 508 | return (bFileOpen && 509 | pageNum >= 0 && 510 | pageNum < hdr.numPages); 511 | } 512 | 513 | -------------------------------------------------------------------------------- /src/pf/pf_hashtable.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_hashtable.cc 3 | // Description: PF_HashTable class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include "pf_internal.h" 9 | #include "pf_hashtable.h" 10 | 11 | // 12 | // PF_HashTable 13 | // 14 | // Desc: Constructor for PF_HashTable object, which allows search, insert, 15 | // and delete of hash foreign_table entries. 16 | // In: numBuckets - number of hash foreign_table buckets 17 | // 18 | PF_HashTable::PF_HashTable(int _numBuckets) 19 | { 20 | // Initialize numBuckets local variable from parameter 21 | this->numBuckets = _numBuckets; 22 | 23 | // Allocate memory for hash foreign_table 24 | hashTable = new PF_HashEntry* [numBuckets]; 25 | 26 | // Initialize all buckets to empty 27 | for (int i = 0; i < numBuckets; i++) 28 | hashTable[i] = NULL; 29 | } 30 | 31 | // 32 | // ~PF_HashTable 33 | // 34 | // Desc: Destructor 35 | // 36 | PF_HashTable::~PF_HashTable() 37 | { 38 | // Clear out all buckets 39 | for (int i = 0; i < numBuckets; i++) { 40 | 41 | // Delete all entries in the bucket 42 | PF_HashEntry *entry = hashTable[i]; 43 | while (entry != NULL) { 44 | PF_HashEntry *next = entry->next; 45 | delete entry; 46 | entry = next; 47 | } 48 | } 49 | 50 | // Finally delete the hash foreign_table 51 | delete[] hashTable; 52 | } 53 | 54 | // 55 | // Find 56 | // 57 | // Desc: Find a hash foreign_table entry. 58 | // In: fd - file descriptor 59 | // pageNum - page number 60 | // Out: slot - set to slot associated with fd and pageNum 61 | // Ret: PF return code 62 | // 63 | RC PF_HashTable::Find(int fd, PageNum pageNum, int &slot) 64 | { 65 | // Get which bucket it should be in 66 | int bucket = Hash(fd, pageNum); 67 | 68 | if (bucket<0) 69 | return (PF_HASHNOTFOUND); 70 | 71 | // Go through the linked list of this bucket 72 | for (PF_HashEntry *entry = hashTable[bucket]; 73 | entry != NULL; 74 | entry = entry->next) { 75 | if (entry->fd == fd && entry->pageNum == pageNum) { 76 | 77 | // Found it 78 | slot = entry->slot; 79 | return (0); 80 | } 81 | } 82 | 83 | // Didn't find it 84 | return (PF_HASHNOTFOUND); 85 | } 86 | 87 | // 88 | // Insert 89 | // 90 | // Desc: Insert a hash foreign_table entry 91 | // In: fd - file descriptor 92 | // pagenum - page number 93 | // slot - slot associated with fd and pageNum 94 | // Ret: PF return code 95 | // 96 | RC PF_HashTable::Insert(int fd, PageNum pageNum, int slot) 97 | { 98 | // Get which bucket it should be in 99 | int bucket = Hash(fd, pageNum); 100 | 101 | // Check entry doesn't already exist in the bucket 102 | PF_HashEntry *entry; 103 | for (entry = hashTable[bucket]; 104 | entry != NULL; 105 | entry = entry->next) { 106 | if (entry->fd == fd && entry->pageNum == pageNum) 107 | return (PF_HASHPAGEEXIST); 108 | } 109 | 110 | // Allocate memory for new hash entry 111 | if ((entry = new PF_HashEntry) == NULL) 112 | return (PF_NOMEM); 113 | 114 | // Insert entry at head of list for this bucket 115 | entry->fd = fd; 116 | entry->pageNum = pageNum; 117 | entry->slot = slot; 118 | entry->next = hashTable[bucket]; 119 | entry->prev = NULL; 120 | if (hashTable[bucket] != NULL) 121 | hashTable[bucket]->prev = entry; 122 | hashTable[bucket] = entry; 123 | 124 | // Return ok 125 | return (0); 126 | } 127 | 128 | // 129 | // Delete 130 | // 131 | // Desc: Delete a hash foreign_table entry 132 | // In: fd - file descriptor 133 | // pagenum - page number 134 | // Ret: PF return code 135 | // 136 | RC PF_HashTable::Delete(int fd, PageNum pageNum) 137 | { 138 | // Get which bucket it should be in 139 | int bucket = Hash(fd, pageNum); 140 | 141 | // Find the entry is in this bucket 142 | PF_HashEntry *entry; 143 | for (entry = hashTable[bucket]; 144 | entry != NULL; 145 | entry = entry->next) { 146 | if (entry->fd == fd && entry->pageNum == pageNum) 147 | break; 148 | } 149 | 150 | // Did we find hash entry? 151 | if (entry == NULL) 152 | return (PF_HASHNOTFOUND); 153 | 154 | // Remove this entry 155 | if (entry == hashTable[bucket]) 156 | hashTable[bucket] = entry->next; 157 | if (entry->prev != NULL) 158 | entry->prev->next = entry->next; 159 | if (entry->next != NULL) 160 | entry->next->prev = entry->prev; 161 | delete entry; 162 | 163 | // Return ook 164 | return (0); 165 | } 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/pf/pf_hashtable.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_hashtable.h 3 | // Description: PF_HashTable class interface 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #ifndef PF_HASHTABLE_H 9 | #define PF_HASHTABLE_H 10 | 11 | #include "pf_internal.h" 12 | 13 | // 14 | // HashEntry - Hash foreign_table bucket entries 15 | // 16 | struct PF_HashEntry { 17 | PF_HashEntry *next; // next hash foreign_table element or NULL 18 | PF_HashEntry *prev; // prev hash foreign_table element or NULL 19 | int fd; // file descriptor 20 | PageNum pageNum; // page number 21 | int slot; // slot of this page in the buffer 22 | }; 23 | 24 | // 25 | // PF_HashTable - allow search, insertion, and deletion of hash foreign_table entries 26 | // 27 | class PF_HashTable { 28 | public: 29 | PF_HashTable (int numBuckets); // Constructor 30 | ~PF_HashTable(); // Destructor 31 | RC Find (int fd, PageNum pageNum, int &slot); 32 | // Set slot to the hash foreign_table 33 | // entry for fd and pageNum 34 | RC Insert (int fd, PageNum pageNum, int slot); 35 | // Insert a hash foreign_table entry 36 | RC Delete (int fd, PageNum pageNum); // Delete a hash foreign_table entry 37 | 38 | private: 39 | int Hash (int fd, PageNum pageNum) const 40 | { return ((fd + pageNum) % numBuckets); } // Hash function 41 | int numBuckets; // Number of hash foreign_table buckets 42 | PF_HashEntry **hashTable; // Hash foreign_table 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/pf/pf_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_internal.h 3 | // Description: Declarations internal to the paged file component 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // Jason McHugh (mchughj@cs.stanford.edu) 7 | // 8 | 9 | #ifndef PF_INTERNAL_H 10 | #define PF_INTERNAL_H 11 | 12 | #include 13 | #include 14 | #include "pf.h" 15 | 16 | // 17 | // Constants and defines 18 | // 19 | const int PF_BUFFER_SIZE = 40; // Number of pages in the buffer 20 | const int PF_HASH_TBL_SIZE = 20; // Size of hash foreign_table 21 | 22 | #define CREATION_MASK 0600 // r/w privileges to owner only 23 | #define PF_PAGE_LIST_END -1 // end of list of free pages 24 | #define PF_PAGE_USED -2 // page is being used 25 | 26 | // L_SET is used to indicate the "whence" argument of the lseek call 27 | // defined in "/usr/include/unistd.h". A value of 0 indicates to 28 | // move to the absolute location specified. 29 | #ifndef L_SET 30 | #define L_SET 0 31 | #endif 32 | 33 | // 34 | // PF_PageHdr: Header structure for pages 35 | // 36 | struct PF_PageHdr { 37 | int nextFree; // nextFree can be any of these values: 38 | // - the number of the next free page 39 | // - PF_PAGE_LIST_END if this is last free page 40 | // - PF_PAGE_USED if the page is not free 41 | }; 42 | 43 | // Justify the file header to the length of one page 44 | const int PF_FILE_HDR_SIZE = PF_PAGE_SIZE + sizeof(PF_PageHdr); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/pf/pf_manager.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_manager.cc 3 | // Description: PF_Manager class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "pf_internal.h" 14 | #include "pf_buffermgr.h" 15 | #include "pf.h" 16 | 17 | 18 | // 19 | // PF_Manager 20 | // 21 | // Desc: Constructor - intended to be called once at begin of program 22 | // Handles creation, deletion, opening and closing of files. 23 | // It is associated with a PF_BufferMgr that manages the page 24 | // buffer and executes the page replacement policies. 25 | // 26 | PF_Manager::PF_Manager() 27 | { 28 | // Create Buffer Manager 29 | pBufferMgr = new PF_BufferMgr(PF_BUFFER_SIZE); 30 | } 31 | 32 | // 33 | // ~PF_Manager 34 | // 35 | // Desc: Destructor - intended to be called once at end of program 36 | // Destroys the buffer manager. 37 | // All files are expected to be closed when this method is called. 38 | // 39 | PF_Manager::~PF_Manager() 40 | { 41 | // Destroy the buffer manager objects 42 | delete pBufferMgr; 43 | } 44 | 45 | // 46 | // CreateFile 47 | // 48 | // Desc: Create a new PF file named fileName 49 | // In: fileName - name of file to create 50 | // Ret: PF return code 51 | // 52 | RC PF_Manager::CreateFile (const char *fileName) 53 | { 54 | int fd; // unix file descriptor 55 | int numBytes; // return code form write syscall 56 | 57 | // Create file for exclusive use 58 | 59 | if ((fd = open(fileName, 60 | #ifdef _WIN32 61 | O_BINARY | 62 | #endif 63 | O_CREAT | O_EXCL | O_WRONLY, 64 | CREATION_MASK)) < 0) 65 | return (PF_UNIX); 66 | 67 | // if ((fd = open(fileName, O_BINARY | O_CREAT | O_EXCL | O_WRONLY, CREATION_MASK)) < 0) 68 | // return (PF_UNIX); 69 | // Initialize the file header: must reserve FileHdrSize bytes in memory 70 | // though the actual size of FileHdr is smaller 71 | char hdrBuf[PF_FILE_HDR_SIZE]; 72 | 73 | // So that Purify doesn't complain 74 | memset(hdrBuf, 0, PF_FILE_HDR_SIZE); 75 | 76 | PF_FileHdr *hdr = (PF_FileHdr*)hdrBuf; 77 | hdr->firstFree = PF_PAGE_LIST_END; 78 | hdr->numPages = 0; 79 | 80 | // Write header to file 81 | if((numBytes = write(fd, hdrBuf, PF_FILE_HDR_SIZE)) 82 | != PF_FILE_HDR_SIZE) { 83 | 84 | // Error while writing: close and remove file 85 | close(fd); 86 | unlink(fileName); 87 | 88 | // Return an error 89 | if(numBytes < 0) 90 | return (PF_UNIX); 91 | else 92 | return (PF_HDRWRITE); 93 | } 94 | 95 | // Close file 96 | if(close(fd) < 0) 97 | return (PF_UNIX); 98 | 99 | // Return ok 100 | return (0); 101 | } 102 | 103 | // 104 | // DestroyFile 105 | // 106 | // Desc: Delete a PF file named fileName (fileName must exist and not be open) 107 | // In: fileName - name of file to delete 108 | // Ret: PF return code 109 | // 110 | RC PF_Manager::DestroyFile (const char *fileName) 111 | { 112 | // Remove the file 113 | if (unlink(fileName) < 0) 114 | return (PF_UNIX); 115 | 116 | // Return ok 117 | return (0); 118 | } 119 | 120 | // 121 | // OpenFile 122 | // 123 | // Desc: Open the paged file whose name is "fileName". It is possible to open 124 | // a file more than once, however, it will be treated as 2 separate files 125 | // (different file descriptors; different buffers). Thus, opening a file 126 | // more than once for writing may corrupt the file, and can, in certain 127 | // circumstances, crash the PF layer. Note that even if only one instance 128 | // of a file is for writing, problems may occur because some writes may 129 | // not be seen by a reader of another instance of the file. 130 | // In: fileName - name of file to open 131 | // Out: fileHandle - refer to the open file 132 | // this function modifies local var's in fileHandle 133 | // to point to the file data in the file foreign_table, and to point to the 134 | // buffer manager object 135 | // Ret: PF_FILEOPEN or other PF return code 136 | // 137 | RC PF_Manager::OpenFile (const char *fileName, PF_FileHandle &fileHandle) 138 | { 139 | //printf("PF_Manager::OpenFile fileName = %s\n", fileName); 140 | int rc; // return code 141 | 142 | // Ensure file is not already open 143 | if (fileHandle.bFileOpen) 144 | return (PF_FILEOPEN); 145 | 146 | // Open the file 147 | 148 | if ((fileHandle.unixfd = open(fileName, 149 | #ifdef _WIN32 150 | O_BINARY | 151 | #endif 152 | O_RDWR)) < 0) 153 | return (PF_UNIX); 154 | 155 | // if ((fileHandle.unixfd = open(fileName, O_BINARY | O_RDWR)) < 0) 156 | // return PF_UNIX; 157 | // Read the file header 158 | { 159 | int numBytes = read(fileHandle.unixfd, (char *)&fileHandle.hdr, sizeof(PF_FileHdr)); 160 | //printf("filename = %s header = %s\n", fileName, (char*)&fileHandle.hdr); 161 | if (numBytes != sizeof(PF_FileHdr)) { 162 | rc = (numBytes < 0) ? PF_UNIX : PF_HDRREAD; 163 | goto err; 164 | } 165 | } 166 | 167 | // Set file header to be not changed 168 | fileHandle.bHdrChanged = FALSE; 169 | 170 | // Set local variables in file handle object to refer to open file 171 | fileHandle.pBufferMgr = pBufferMgr; 172 | fileHandle.bFileOpen = TRUE; 173 | 174 | // Return ok 175 | return 0; 176 | 177 | err: 178 | //printf("goto err line = %d\n", __LINE__); 179 | // Close file 180 | close(fileHandle.unixfd); 181 | fileHandle.bFileOpen = FALSE; 182 | 183 | // Return error 184 | return (rc); 185 | } 186 | 187 | // 188 | // CloseFile 189 | // 190 | // Desc: Close file associated with fileHandle 191 | // The file should have been opened with OpenFile(). 192 | // Also, flush all pages for the file from the page buffer 193 | // It is an error to close a file with pages still fixed in the buffer. 194 | // In: fileHandle - handle of file to close 195 | // Out: fileHandle - no longer refers to an open file 196 | // this function modifies local var's in fileHandle 197 | // Ret: PF return code 198 | // 199 | RC PF_Manager::CloseFile(PF_FileHandle &fileHandle) 200 | { 201 | RC rc; 202 | 203 | // Ensure fileHandle refers to open file 204 | if (!fileHandle.bFileOpen) 205 | return (PF_CLOSEDFILE); 206 | 207 | // Flush all buffers for this file and write out the header 208 | if ((rc = fileHandle.FlushPages())) 209 | return (rc); 210 | 211 | // Close the file 212 | if (close(fileHandle.unixfd) < 0) 213 | return (PF_UNIX); 214 | fileHandle.bFileOpen = FALSE; 215 | 216 | // Reset the buffer manager pointer in the file handle 217 | fileHandle.pBufferMgr = NULL; 218 | 219 | // Return ok 220 | return 0; 221 | } 222 | 223 | // 224 | // ClearBuffer 225 | // 226 | // Desc: Remove all entries from the buffer manager. 227 | // This routine will be called via the system command and is only 228 | // really useful if the user wants to run some performance 229 | // comparison starting with an clean buffer. 230 | // In: Nothing 231 | // Out: Nothing 232 | // Ret: Returns the result of PF_BufferMgr::ClearBuffer 233 | // It is a code: 0 for success, something else for a PF error. 234 | // 235 | RC PF_Manager::ClearBuffer() 236 | { 237 | return pBufferMgr->ClearBuffer(); 238 | } 239 | 240 | // 241 | // PrintBuffer 242 | // 243 | // Desc: Display all of the pages within the buffer. 244 | // This routine will be called via the system command. 245 | // In: Nothing 246 | // Out: Nothing 247 | // Ret: Returns the result of PF_BufferMgr::PrintBuffer 248 | // It is a code: 0 for success, something else for a PF error. 249 | // 250 | RC PF_Manager::PrintBuffer() 251 | { 252 | return pBufferMgr->PrintBuffer(); 253 | } 254 | 255 | // 256 | // ResizeBuffer 257 | // 258 | // Desc: Resizes the buffer manager to the size passed in. 259 | // This routine will be called via the system command. 260 | // In: The new buffer size 261 | // Out: Nothing 262 | // Ret: Returns the result of PF_BufferMgr::ResizeBuffer 263 | // It is a code: 0 for success, PF_TOOSMALL when iNewSize 264 | // would be too small. 265 | // 266 | RC PF_Manager::ResizeBuffer(int iNewSize) 267 | { 268 | return pBufferMgr->ResizeBuffer(iNewSize); 269 | } 270 | 271 | //------------------------------------------------------------------------------ 272 | // Three Methods for manipulating raw memory buffers. These memory 273 | // locations are handled by the buffer manager, but are not 274 | // associated with a particular file. These should be used if you 275 | // want memory that is bounded by the size of the buffer pool. 276 | // 277 | // The PF_Manager just passes the calls down to the Buffer manager. 278 | //------------------------------------------------------------------------------ 279 | 280 | RC PF_Manager::GetBlockSize(int &length) const 281 | { 282 | return pBufferMgr->GetBlockSize(length); 283 | } 284 | 285 | RC PF_Manager::AllocateBlock(char *&buffer) 286 | { 287 | return pBufferMgr->AllocateBlock(buffer); 288 | } 289 | 290 | RC PF_Manager::DisposeBlock(char *buffer) 291 | { 292 | return pBufferMgr->DisposeBlock(buffer); 293 | } 294 | 295 | PF_Manager &PF_Manager::getInstance() { 296 | static PF_Manager pf_manager; 297 | return pf_manager; 298 | } 299 | -------------------------------------------------------------------------------- /src/pf/pf_pagehandle.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_pagehandle.cc 3 | // Description: PF_PageHandle class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include "pf_internal.h" 9 | #include "pf.h" 10 | 11 | // 12 | // Defines 13 | // 14 | #define INVALID_PAGE (-1) 15 | 16 | // 17 | // PF_PageHandle 18 | // 19 | // Desc: Default constructor for a page handle object 20 | // A page handle object provides access to the contents of a page 21 | // and the page's page number. The page handle object is constructed 22 | // here but it must be passed to one of the PF_FileHandle methods to 23 | // have it refer to a pinned page before it can be used to access the 24 | // contents of a page. Remember to call PF_FileHandle::UnpinPage() 25 | // to unpin the page when you are finished accessing it. 26 | // 27 | PF_PageHandle::PF_PageHandle() 28 | { 29 | pageNum = INVALID_PAGE; 30 | pPageData = NULL; 31 | } 32 | 33 | // 34 | // ~PF_PageHandle 35 | // 36 | // Desc: Destroy the page handle object. 37 | // If the page handle object refers to a pinned page, the page will 38 | // NOT be unpinned. 39 | // 40 | PF_PageHandle::~PF_PageHandle() 41 | { 42 | // Don't need to do anything 43 | } 44 | 45 | // 46 | // PF_PageHandle 47 | // 48 | // Desc: Copy constructor 49 | // If the incoming page handle object refers to a pinned page, 50 | // the page will NOT be pinned again. 51 | // In: pageHandle - page handle object from which to construct this object 52 | // 53 | PF_PageHandle::PF_PageHandle(const PF_PageHandle &pageHandle) 54 | { 55 | // Just copy the local variables since there is no local memory 56 | // allocation involved 57 | this->pageNum = pageHandle.pageNum; 58 | this->pPageData = pageHandle.pPageData; 59 | } 60 | 61 | // 62 | // operator= 63 | // 64 | // Desc: overload = operator 65 | // If the page handle object on the rhs refers to a pinned page, 66 | // the page will NOT be pinned again. 67 | // In: pageHandle - page handle object to set this object equal to 68 | // Ret: reference to *this 69 | // 70 | PF_PageHandle& PF_PageHandle::operator= (const PF_PageHandle &pageHandle) 71 | { 72 | // Check for self-assignment 73 | if (this != &pageHandle) { 74 | 75 | // Just copy the pointers since there is no local memory 76 | // allocation involved 77 | this->pageNum = pageHandle.pageNum; 78 | this->pPageData = pageHandle.pPageData; 79 | } 80 | 81 | // Return a reference to this 82 | return (*this); 83 | } 84 | 85 | // 86 | // GetData 87 | // 88 | // Desc: Access the contents of a page. The page handle object must refer 89 | // to a pinned page. 90 | // Out: pData - Set pData to point to the page contents 91 | // Ret: PF return code 92 | // 93 | RC PF_PageHandle::GetData(char *&pData) const 94 | { 95 | // Page must refer to a pinned page 96 | if (pPageData == NULL) 97 | return (PF_PAGEUNPINNED); 98 | 99 | // Set pData to point to page contents (after PF header) 100 | pData = pPageData; 101 | 102 | // Return ok 103 | return (0); 104 | } 105 | 106 | // 107 | // GetPageNum 108 | // 109 | // Desc: Access the page number. The page handle object must refer to 110 | // a pinned page. 111 | // Out: pageNum - contains the page number 112 | // Ret: PF return code 113 | // 114 | RC PF_PageHandle::GetPageNum(PageNum &_pageNum) const 115 | { 116 | 117 | // Page must refer to a pinned page 118 | if (pPageData == NULL) 119 | return (PF_PAGEUNPINNED); 120 | 121 | // Set page number 122 | _pageNum = this->pageNum; 123 | 124 | // Return ok 125 | return (0); 126 | } 127 | -------------------------------------------------------------------------------- /src/pf/pf_statistics.cc: -------------------------------------------------------------------------------- 1 | // 2 | // pf_statistics.cc 3 | // 4 | 5 | // This file contains the procedure to display all the statistics for the 6 | // PF layer. 7 | 8 | // Code written by Andre Bergholz, who was the TA for 2000 9 | 10 | // 11 | // This file only makes sense when the PF Statistics layer is defined 12 | // 13 | #ifdef PF_STATS 14 | 15 | #include 16 | #include "pf.h" 17 | #include "statistics.h" 18 | 19 | using namespace std; 20 | 21 | // This is defined within pf_buffermgr.cc 22 | extern StatisticsMgr *pStatisticsMgr; 23 | 24 | void PF_Statistics() 25 | { 26 | // First get all the statistics, must remember to delete memory returned 27 | int *piGP = pStatisticsMgr->Get(PF_GETPAGE); 28 | int *piPF = pStatisticsMgr->Get(PF_PAGEFOUND); 29 | int *piPNF = pStatisticsMgr->Get(PF_PAGENOTFOUND); 30 | int *piRP = pStatisticsMgr->Get(PF_READPAGE); 31 | int *piWP = pStatisticsMgr->Get(PF_WRITEPAGE); 32 | int *piFP = pStatisticsMgr->Get(PF_FLUSHPAGES); 33 | 34 | cout << "PF Layer Statistics\n"; 35 | cout << "-------------------\n"; 36 | 37 | cout << "Total number of calls to GetPage Routine: "; 38 | if (piGP) cout << *piGP; else cout << "None"; 39 | cout << "\n Number found: "; 40 | if (piPF) cout << *piPF; else cout << "None"; 41 | cout << "\n Number not found: "; 42 | if (piPNF) cout << *piPNF; else cout << "None"; 43 | cout << "\n-------------------\n"; 44 | 45 | cout << "Number of read requests: "; 46 | if (piRP) cout << *piRP; else cout << "None"; 47 | cout << "\nNumber of write requests: "; 48 | if (piWP) cout << *piWP; else cout << "None"; 49 | cout << "\n-------------------\n"; 50 | cout << "Number of flushes: "; 51 | if (piFP) cout << *piFP; else cout << "None"; 52 | cout << "\n-------------------\n"; 53 | 54 | // Must delete the memory returned from StatisticsMgr::Get 55 | delete piGP; 56 | delete piPF; 57 | delete piPNF; 58 | delete piRP; 59 | delete piWP; 60 | delete piFP; 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/ql/Aggregation.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2019-01-01. 3 | // 4 | 5 | #include "Aggregation.h" 6 | #include 7 | 8 | Expr *init_statistics(AttrType attrType, AggregationType aggregation) { 9 | Expr *result = nullptr; 10 | switch (aggregation) { 11 | case AggregationType::T_AVG: 12 | case AggregationType::T_SUM: 13 | switch (attrType) { 14 | case AttrType::INT: 15 | result = new Expr(0); 16 | break; 17 | case AttrType::FLOAT: 18 | result = new Expr(0.0f); 19 | break; 20 | default: 21 | break; 22 | // this will never happen 23 | } 24 | break; 25 | case AggregationType::T_MIN: 26 | switch (attrType) { 27 | case AttrType::INT: 28 | result = new Expr(INT_MAX); 29 | break; 30 | case AttrType::FLOAT: 31 | result = new Expr(1e38f); 32 | break; 33 | default: 34 | break; 35 | } 36 | break; 37 | case AggregationType::T_MAX: 38 | switch (attrType) { 39 | case AttrType::INT: 40 | result = new Expr(INT_MIN); 41 | break; 42 | case AttrType::FLOAT: 43 | result = new Expr(-1e38f); 44 | break; 45 | default: 46 | break; 47 | } 48 | break; 49 | case AggregationType::T_NONE: 50 | break; 51 | } 52 | if (result != nullptr) { 53 | result->type_check(); 54 | } 55 | return result; 56 | } 57 | 58 | Aggregation::Aggregation(const BindAttribute &attrInfo, AggregationType type, bool isGroup) { 59 | is_group = isGroup; 60 | this->type = type; 61 | this->attrInfo = attrInfo; 62 | if (this->type != AggregationType::T_NONE) { 63 | if (this->attrInfo.attrType != AttrType::INT and this->attrInfo.attrType != AttrType::FLOAT) { 64 | throw "The type of column " + attrInfo.attrName + " can't be aggregated\n"; 65 | } 66 | } 67 | expr = nullptr; 68 | if (not isGroup) { 69 | expr = init_statistics(attrInfo.attrType, type); 70 | } 71 | if (expr != nullptr) { 72 | expr->type_check(); 73 | } 74 | } 75 | 76 | Aggregation::~Aggregation() { 77 | delete expr; 78 | } 79 | 80 | void Aggregation::accumulate(const std::string &group, const Expr &value) { 81 | if (not value.is_null) { 82 | Expr *result; 83 | if (not is_group) { 84 | result = expr; 85 | } else { 86 | if (groupData.find(group) == groupData.end()) { 87 | result = init_statistics(attrInfo.attrType, type); 88 | groupData[group] = result; 89 | groupCount[group] = 1; 90 | } else { 91 | result = groupData[group]; 92 | groupCount[group] += 1; 93 | } 94 | } 95 | switch (type) { 96 | case AggregationType::T_AVG: 97 | case AggregationType::T_SUM: 98 | *result += value; 99 | break; 100 | case AggregationType::T_MIN: 101 | if (value < *result) { 102 | (*result).assign(value); 103 | } 104 | break; 105 | case AggregationType::T_MAX: 106 | if (*result < value) { 107 | (*result).assign(value); 108 | } 109 | break; 110 | case AggregationType::T_NONE: 111 | break; 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/ql/Aggregation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2019-01-01. 3 | // 4 | 5 | #ifndef MYDB_AGGREGATION_H 6 | #define MYDB_AGGREGATION_H 7 | 8 | #include "../parser/Expr.h" 9 | #include 10 | 11 | class Aggregation { 12 | public: 13 | Aggregation(const BindAttribute &attrInfo, AggregationType type, bool isGroup); 14 | 15 | void accumulate(const std::string &group, const Expr &value); 16 | 17 | ~Aggregation(); 18 | 19 | Expr *expr = nullptr; 20 | std::map groupData; 21 | std::map groupCount; 22 | 23 | private: 24 | AggregationType type; 25 | BindAttribute attrInfo; 26 | bool is_group; 27 | }; 28 | 29 | 30 | #endif //MYDB_AGGREGATION_H 31 | -------------------------------------------------------------------------------- /src/ql/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Aggregation.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/QueryManager.h 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Table.h 6 | PARENT_SCOPE 7 | ) 8 | 9 | set(SOURCE 10 | ${SOURCE} 11 | ${CMAKE_CURRENT_SOURCE_DIR}/Aggregation.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/QueryManager.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/Table.cpp 14 | PARENT_SCOPE 15 | ) -------------------------------------------------------------------------------- /src/ql/QueryManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018-12-09. 3 | // 4 | 5 | #ifndef MYDB_QL_MANAGER_H 6 | #define MYDB_QL_MANAGER_H 7 | 8 | #include "../parser/Tree.h" 9 | #include "../rm/RM_Record.h" 10 | #include "../rm/RM_FileHandle.h" 11 | #include "../sm/sm.h" 12 | #include "Table.h" 13 | #include "../Constants.h" 14 | #include 15 | #include 16 | #include 17 | 18 | class QL_Manager { 19 | private: 20 | SM_Manager sm; 21 | 22 | std::vector recordCaches; 23 | 24 | using CallbackFunc = std::function; 25 | 26 | using MultiTableFunc = std::function &)>; 27 | 28 | 29 | void printException(const AttrBindException &exception); 30 | 31 | int iterateTables(Table &table, Expr *condition, CallbackFunc callback); 32 | 33 | int 34 | iterateTables(std::vector> &tables, int current, Expr *condition, MultiTableFunc callback, 35 | std::list &indexExprs); 36 | 37 | void bindAttribute(Expr *expr, const std::vector> &tables); 38 | 39 | int openTables(const std::vector &tableNames, std::vector> &tables); 40 | 41 | int whereBindCheck(Expr *expr, std::vector> &tables); 42 | 43 | public: 44 | // select attributes on relations with where condition and group attributes 45 | int exeSelect(AttributeList *attributes, IdentList *relations, Expr *whereClause, const std::string &groupAttrName); 46 | 47 | // insert into relation table(可以用columnList指定要插入的列) 48 | int exeInsert(std::string relation, IdentList *columnList, ConstValueLists *insertValueTree); 49 | 50 | // update relation table as setClauses with where condition 51 | int exeUpdate(std::string relation, SetClauseList *setClauses, Expr *whereClause); 52 | 53 | // delete the data in relation table with where condition 54 | int exeDelete(std::string relation, Expr *whereClause); 55 | 56 | static QL_Manager &getInstance(); 57 | }; 58 | 59 | #define QL_TABLE_FAIL (START_QL_WARN + 1) 60 | #define QL_TYPE_CHECK (START_QL_WARN + 2) 61 | 62 | #endif //MYDB_QL_MANAGER_H 63 | -------------------------------------------------------------------------------- /src/ql/Table.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018-12-20. 3 | // 4 | 5 | #ifndef MYDB_TABLE_H 6 | #define MYDB_TABLE_H 7 | 8 | #include "../parser/Tree.h" 9 | #include "../rm/RM_FileHandle.h" 10 | #include "../rm/RecordManager.h" 11 | #include "../rm/RID.h" 12 | #include "../ix/ix.h" 13 | #include "../parser/Expr.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | class Table { 20 | public: 21 | explicit Table(const std::string &tableName); 22 | 23 | ~Table(); 24 | 25 | int getOffset(const std::string &attribute) const; 26 | 27 | int getColumnIndex(const std::string &attribute) const; 28 | 29 | const BindAttribute &getAttrInfo(int index) const; 30 | 31 | int getAttrCount() const; 32 | 33 | bool getIndexAvailable(int index); 34 | 35 | IX_IndexHandle &getIndexHandler(int index); 36 | 37 | std::string checkData(char *data); 38 | 39 | int deleteData(const RID &rid); 40 | 41 | int insertData(const IdentList *columnList, const ConstValueList *constValues); 42 | 43 | int updateData(const RM_Record &record, const std::vector &attrIndexes, SetClauseList *setClauses); 44 | 45 | int insertIndex(char *data, const RID &rid); 46 | 47 | int deleteIndex(char *data, const RID &rid); 48 | 49 | RM_FileHandle &getFileHandler(); 50 | 51 | std::string tableName; 52 | private: 53 | int tryOpenFile(); 54 | 55 | int tryOpenIndex(int indexNo); 56 | 57 | int tryOpenForeignIndex(int constNo); 58 | 59 | std::vector attrInfos; 60 | int recordSize; 61 | 62 | ColumnDecsList columns; 63 | TableConstraintList tableConstraints; 64 | RM_FileHandle fileHandle; 65 | std::vector indexHandles; 66 | std::vector> foreignTables; 67 | std::vector foreignAttrInt; 68 | std::vector constrAttrI; 69 | }; 70 | 71 | 72 | #endif //MYDB_TABLE_H 73 | -------------------------------------------------------------------------------- /src/rm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/RecordManager.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/RID.h 5 | ${CMAKE_CURRENT_SOURCE_DIR}/RM_FileHandle.h 6 | ${CMAKE_CURRENT_SOURCE_DIR}/RM_FileScan.h 7 | PARENT_SCOPE 8 | ) 9 | 10 | set(SOURCE 11 | ${SOURCE} 12 | ${CMAKE_CURRENT_SOURCE_DIR}/RecordManager.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/RM_FileHandle.cpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/RM_FileScan.cpp 15 | PARENT_SCOPE 16 | ) -------------------------------------------------------------------------------- /src/rm/HeaderPage.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_HEADERPAGE_H 6 | #define MYDB_HEADERPAGE_H 7 | 8 | struct HeaderPage { 9 | unsigned recordSize; 10 | unsigned recordNum; 11 | unsigned recordPerPage; 12 | unsigned firstSparePage; 13 | unsigned pageNum; 14 | unsigned slotMapSize; 15 | }; 16 | 17 | #endif //MYDB_HEADERPAGE_H 18 | -------------------------------------------------------------------------------- /src/rm/RID.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_RID_H 6 | #define MYDB_RID_H 7 | 8 | class RID { 9 | public: 10 | RID() = default; 11 | RID(unsigned page_num, unsigned slot_num) : _page_num(page_num), _slot_num(slot_num) {} 12 | RID(const RID &rid) = default; 13 | 14 | // Construct RID from page and 15 | // slot number 16 | unsigned long getPageNum() const { return _page_num; } // Return page number 17 | unsigned getSlotNum() const { return _slot_num; } // Return slot number 18 | private: 19 | unsigned long _page_num = 0; 20 | unsigned _slot_num = 0; 21 | }; 22 | 23 | #endif //MYDB_RID_H 24 | -------------------------------------------------------------------------------- /src/rm/RM_FileHandle.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #include "RM_FileHandle.h" 6 | #include "RecordManager.h" 7 | #include "../utils/MyBitMap.h" 8 | 9 | int RM_FileHandle::getRec(const RID &rid, RM_Record &record) const { 10 | int rc; 11 | PF_PageHandle page_handle; 12 | if ((rc = _pf_file_handle.GetThisPage(rid.getPageNum(), page_handle)) != 0) { 13 | PF_PrintError(rc); 14 | return -1; 15 | } 16 | char *page_data; 17 | page_handle.GetData(page_data); 18 | record.init(reinterpret_cast(page_data) + getOffset(rid.getSlotNum()), _header_page.recordSize, rid); 19 | _pf_file_handle.UnpinPage(rid.getPageNum()); 20 | return 0; 21 | } 22 | 23 | RID RM_FileHandle::insertRec(const char *pData) { 24 | int rc; 25 | if (_header_page.firstSparePage <= 0) { 26 | insertPage(); 27 | } 28 | unsigned page_num = _header_page.firstSparePage; 29 | PF_PageHandle page_handle; 30 | if ((rc = _pf_file_handle.GetThisPage(page_num, page_handle)) != 0) { 31 | PF_PrintError(rc); 32 | } 33 | char *page_data; 34 | page_handle.GetData(page_data); 35 | 36 | MyBitMap bitmap(_header_page.slotMapSize * 8, reinterpret_cast(page_data + 8)); 37 | unsigned slot_num = bitmap.findLeftOne(); 38 | memcpy(page_data + getOffset(slot_num), pData, 39 | _header_page.recordSize); 40 | bitmap.setBit(slot_num, 0); 41 | if (bitmap.findLeftOne() >= _header_page.recordPerPage) { 42 | if (reinterpret_cast(page_data)[1] > 0) { 43 | _header_page.firstSparePage = reinterpret_cast(page_data)[1]; 44 | } else { 45 | _header_page.firstSparePage = 0; 46 | } 47 | reinterpret_cast(page_data)[1] = 0; 48 | } 49 | _pf_file_handle.MarkDirty(page_num); 50 | if ((rc = _pf_file_handle.UnpinPage(page_num)) != 0) { 51 | printf("%d\n", rc); 52 | } 53 | _header_page.recordNum += 1; 54 | _header_modified = true; 55 | return {page_num, slot_num}; 56 | } 57 | 58 | int RM_FileHandle::deleteRec(const RID &rid) { 59 | int rc; 60 | PF_PageHandle page_handle; 61 | if ((rc = _pf_file_handle.GetThisPage(rid.getPageNum(), page_handle)) != 0) { 62 | PF_PrintError(rc); 63 | return -1; 64 | } 65 | char *page_data; 66 | page_handle.GetData(page_data); 67 | MyBitMap bitmap(_header_page.slotMapSize * 8, reinterpret_cast(page_data + 8)); 68 | bool full = false; 69 | if (bitmap.findLeftOne() >= _header_page.recordPerPage) { 70 | full = true; 71 | } 72 | bitmap.setBit(rid.getSlotNum(), 1); 73 | if (full) { 74 | if (_header_page.firstSparePage > 0) { 75 | reinterpret_cast(page_data)[1] = (_header_page.firstSparePage); 76 | } else { 77 | reinterpret_cast(page_data)[1] = 0; 78 | } 79 | _header_page.firstSparePage = rid.getPageNum(); 80 | _header_modified = true; 81 | } 82 | _pf_file_handle.MarkDirty(rid.getPageNum()); 83 | if ((rc = _pf_file_handle.UnpinPage(rid.getPageNum())) != 0) { 84 | PF_PrintError(rc); 85 | return -1; 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | int RM_FileHandle::updateRec(const RM_Record &rec) { 92 | 93 | int rc; 94 | PF_PageHandle page_handle; 95 | rc = _pf_file_handle.GetThisPage(rec.getRID().getPageNum(), page_handle); 96 | if (rc != 0) { 97 | PF_PrintError(rc); 98 | } 99 | char *page_data = nullptr; 100 | rc = page_handle.GetData(page_data); 101 | memcpy(page_data + getOffset(rec.getRID().getSlotNum()), rec.getData(), 102 | _header_page.recordSize); 103 | _pf_file_handle.MarkDirty(rec.getRID().getPageNum()); 104 | if ((rc = _pf_file_handle.UnpinPage(rec.getRID().getPageNum())) != 0) { 105 | PF_PrintError(rc); 106 | } 107 | return 0; 108 | } 109 | 110 | int RM_FileHandle::insertPage() { 111 | int rc; 112 | PF_PageHandle page_handle; 113 | if ((rc = _pf_file_handle.AllocatePage(page_handle)) != 0) { 114 | PF_PrintError(rc); 115 | return -1; 116 | } 117 | char *page_data; 118 | page_handle.GetData(page_data); 119 | reinterpret_cast(page_data)[0] = _header_page.firstSparePage; 120 | _header_page.firstSparePage = _header_page.pageNum; 121 | memset(page_data + 8, 0xff, _header_page.slotMapSize); 122 | long page_num; 123 | page_handle.GetPageNum(page_num); 124 | _pf_file_handle.MarkDirty(page_num); 125 | if ((rc = _pf_file_handle.UnpinPage(page_num)) != 0) { 126 | return rc; 127 | } 128 | _header_page.pageNum += 1; 129 | _header_modified = true; 130 | return 0; 131 | } 132 | 133 | unsigned RM_FileHandle::getOffset(unsigned slot_num) const { 134 | return 8 + _header_page.slotMapSize + slot_num * _header_page.recordSize; 135 | } 136 | 137 | RM_FileHandle::~RM_FileHandle() { 138 | if (_initialized) { 139 | RecordManager::getInstance().closeFile(*this); 140 | } 141 | } 142 | 143 | bool RM_FileHandle::is_initialized() const { 144 | return _initialized; 145 | } 146 | 147 | -------------------------------------------------------------------------------- /src/rm/RM_FileHandle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_RM_FILEHANDLE_H 6 | #define MYDB_RM_FILEHANDLE_H 7 | 8 | #include "HeaderPage.h" 9 | #include "RM_Record.h" 10 | 11 | #include "RID.h" 12 | #include "../Constants.h" 13 | #include "../pf/pf.h" 14 | 15 | class RM_FileHandle { 16 | public: 17 | friend class RecordManager; 18 | friend class RM_FileScan; 19 | 20 | RM_FileHandle() = default; // Constructor 21 | RM_FileHandle(const RM_FileHandle &) = delete; 22 | ~RM_FileHandle(); // Destructor 23 | 24 | int getRec(const RID &rid, RM_Record & record) const; 25 | 26 | // Get a record 27 | RID insertRec(const char *pData); // Insert a new record, 28 | // return record id 29 | int deleteRec(const RID &rid); // Delete a record 30 | int updateRec(const RM_Record &rec); // Update a record 31 | bool is_initialized() const; 32 | private: 33 | int insertPage(); 34 | 35 | unsigned getOffset(unsigned slot_num) const; 36 | 37 | HeaderPage _header_page; 38 | bool _header_modified; 39 | PF_FileHandle _pf_file_handle; 40 | bool _initialized = false; 41 | }; 42 | 43 | #endif //MYDB_RM_FILEHANDLE_H 44 | -------------------------------------------------------------------------------- /src/rm/RM_FileScan.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/29. 3 | // 4 | 5 | #include "RM_FileScan.h" 6 | #include "RM_FileHandle.h" 7 | #include "../pf/pf.h" 8 | #include "../parser/Expr.h" 9 | 10 | int 11 | RM_FileScan::openScan(const RM_FileHandle &fileHandle, Expr *condition, const std::string &tableName) { 12 | _file_handle = &fileHandle; 13 | _condition = condition; 14 | _current_page = 0; 15 | _current_bitdata = nullptr; 16 | this->tableName = tableName; 17 | return 0; 18 | } 19 | 20 | 21 | int RM_FileScan::getNextRec(RM_Record &rec) { 22 | while (true) { 23 | unsigned slot_num = _file_handle->_header_page.recordPerPage + 1; 24 | if (_current_page != 0) { 25 | slot_num = _current_bitmap.findLeftOne(); 26 | } 27 | while (slot_num >= _file_handle->_header_page.recordPerPage) { 28 | _current_page += 1; 29 | if (_current_page >= _file_handle->_header_page.pageNum) { 30 | return 1; 31 | } 32 | PF_PageHandle page_handle; 33 | _file_handle->_pf_file_handle.GetThisPage(_current_page, page_handle); 34 | char *page_data; 35 | page_handle.GetData(page_data); 36 | delete[]_current_bitdata; 37 | _current_bitdata = new char[_file_handle->_header_page.slotMapSize]; 38 | memcpy(_current_bitdata, page_data + 8, _file_handle->_header_page.slotMapSize); 39 | _file_handle->_pf_file_handle.UnpinPage(_current_page); 40 | for (int i = 0; i < _file_handle->_header_page.slotMapSize; ++i) { 41 | _current_bitdata[i] = ~_current_bitdata[i]; 42 | } 43 | _current_bitmap = MyBitMap(_file_handle->_header_page.slotMapSize * 8, 44 | reinterpret_cast(_current_bitdata)); 45 | slot_num = _current_bitmap.findLeftOne(); 46 | } 47 | _file_handle->getRec(RID{_current_page, slot_num}, rec); 48 | _current_bitmap.setBit(slot_num, 0); 49 | char *data = rec.getData(); 50 | if (_condition != nullptr) { 51 | _condition->init_calculate(tableName); 52 | _condition->calculate(data, this->tableName); 53 | if (not _condition->calculated or _condition->is_true()) { 54 | _condition->init_calculate(tableName); 55 | break; 56 | } 57 | } else { 58 | break; 59 | } 60 | 61 | } 62 | return 0; 63 | } 64 | 65 | int RM_FileScan::closeScan() { 66 | delete[] _current_bitdata; 67 | _current_bitdata = nullptr; 68 | return 0; 69 | } 70 | 71 | int 72 | RM_FileScan::openScan(const RM_FileHandle &fileHandle, AttrType attrType, int attrLength, int attrOffset, CompOp compOp, 73 | void *value) { 74 | if (compOp != CompOp::NO_OP) { 75 | Expr *left = new Expr(); 76 | left->attrInfo.attrSize = attrLength; 77 | left->attrInfo.attrOffset = attrOffset; 78 | left->attrInfo.attrType = attrType; 79 | left->attrInfo.notNull = false; 80 | left->nodeType = NodeType::ATTR_NODE; 81 | 82 | Expr *right; 83 | if (value != nullptr) { 84 | switch (attrType) { 85 | case AttrType::INT: 86 | case AttrType::DATE: { 87 | int i = *reinterpret_cast(value); 88 | right = new Expr(i); 89 | break; 90 | } 91 | case AttrType::FLOAT: { 92 | float f = *reinterpret_cast(value); 93 | right = new Expr(f); 94 | break; 95 | } 96 | case AttrType::STRING: { 97 | char *s = reinterpret_cast(value); 98 | right = new Expr(s); 99 | break; 100 | } 101 | case AttrType::BOOL: { 102 | bool b = *reinterpret_cast(value); 103 | right = new Expr(b); 104 | break; 105 | } 106 | case AttrType::NO_ATTR: 107 | case AttrType::VARCHAR: 108 | right = new Expr(); 109 | break; 110 | } 111 | } else { 112 | right = new Expr(); 113 | } 114 | 115 | Expr *condition = new Expr(left, compOp, right); 116 | return openScan(fileHandle, condition, ""); 117 | } else { 118 | return openScan(fileHandle, nullptr, ""); 119 | } 120 | } 121 | 122 | RM_FileScan::~RM_FileScan() { 123 | closeScan(); 124 | } 125 | -------------------------------------------------------------------------------- /src/rm/RM_FileScan.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_RM_FILESCAN_H 6 | #define MYDB_RM_FILESCAN_H 7 | 8 | #include "RM_FileHandle.h" 9 | #include "../utils/MyBitMap.h" 10 | #include "../parser/Expr.h" 11 | #include 12 | 13 | class RM_FileScan { 14 | private: 15 | const RM_FileHandle *_file_handle = nullptr; 16 | Expr *_condition; 17 | unsigned _current_page; 18 | MyBitMap _current_bitmap = MyBitMap(0, 1); 19 | char *_current_bitdata = nullptr; 20 | std::string tableName; 21 | public: 22 | RM_FileScan() = default; // Constructor 23 | ~RM_FileScan(); // Destructor 24 | int openScan(const RM_FileHandle &fileHandle, // Initialize file scan 25 | Expr *condition, const std::string &tableName 26 | ); 27 | 28 | int openScan(const RM_FileHandle &fileHandle, // Initialize file scan 29 | AttrType attrType, 30 | int attrLength, 31 | int attrOffset, 32 | CompOp compOp, 33 | void *value 34 | ); 35 | 36 | int getNextRec(RM_Record &rec); // Get next matching record 37 | int closeScan(); // Terminate file scan 38 | }; 39 | 40 | #endif //MYDB_RM_FILESCAN_H 41 | -------------------------------------------------------------------------------- /src/rm/RM_Record.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_RM_RECORD_H 6 | #define MYDB_RM_RECORD_H 7 | 8 | #include 9 | #include "RID.h" 10 | #include 11 | 12 | struct RM_Record { 13 | public: 14 | RM_Record() : _data(nullptr), _size(0) { 15 | 16 | } 17 | 18 | RM_Record(const char *data, unsigned size, const RID &rid) : _rid(rid), _size(size) { 19 | _data = new char[size]; 20 | memcpy(_data, data, size); 21 | } 22 | 23 | RM_Record(const RM_Record &record) : RM_Record(record._data, record._size, record._rid) {} 24 | 25 | RM_Record(RM_Record &&record) noexcept { 26 | _data = record._data; 27 | _size = record._size; 28 | _rid = record._rid; 29 | record._data = nullptr; 30 | record._size = 0; 31 | } 32 | 33 | 34 | RID getRID() const { 35 | return _rid; 36 | } 37 | 38 | char *getData() const { 39 | return _data; 40 | } 41 | 42 | unsigned getSize() const { 43 | return _size; 44 | } 45 | 46 | void init(const char *data, unsigned size, const RID &rid) { 47 | this->_rid = rid; 48 | 49 | delete[]_data; 50 | 51 | _data = new char[size]; 52 | memcpy(_data, data, size); 53 | } 54 | 55 | ~RM_Record() { 56 | delete[]_data; 57 | 58 | } 59 | 60 | private: 61 | RID _rid; 62 | char *_data; 63 | unsigned _size; 64 | }; 65 | 66 | #endif //MYDB_RM_RECORD_H 67 | -------------------------------------------------------------------------------- /src/rm/RecordManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #include "RecordManager.h" 6 | 7 | 8 | unsigned bitmap_size(unsigned record_per_page) { 9 | return ((record_per_page + 31u) >> 5u) * 4; 10 | } 11 | 12 | unsigned record_per_page(unsigned record_size) { 13 | return (32 * (PF_PAGE_SIZE - 8) - 124) / (record_size * 32 + 4); 14 | } 15 | 16 | RecordManager::RecordManager() : _pf_manager(PF_Manager::getInstance()) { 17 | 18 | } 19 | 20 | int RecordManager::createFile(std::string filename, unsigned record_size) { 21 | int rc; 22 | rc = _pf_manager.CreateFile(filename.c_str()); 23 | if (rc) { 24 | PF_PrintError(rc); 25 | return rc; 26 | } 27 | // Create the Heade Page 28 | PF_FileHandle file_handle; 29 | rc = _pf_manager.OpenFile(filename.c_str(), file_handle); 30 | if (rc) { 31 | PF_PrintError(rc); 32 | return rc; 33 | } 34 | PF_PageHandle page_handle; 35 | rc = file_handle.AllocatePage(page_handle); 36 | if (rc) { 37 | PF_PrintError(rc); 38 | return rc; 39 | } 40 | char *data; 41 | page_handle.GetData(data); 42 | auto header = reinterpret_cast(data); 43 | 44 | //Set up the header page 45 | header->recordSize = record_size; 46 | header->pageNum = 1; 47 | header->recordNum = 0; 48 | header->recordPerPage = record_per_page(record_size); 49 | header->firstSparePage = 0; 50 | header->slotMapSize = bitmap_size(header->recordPerPage) + 4; 51 | 52 | //Write back the header page 53 | long page_num; 54 | page_handle.GetPageNum(page_num); 55 | rc = file_handle.MarkDirty(page_num); 56 | rc = file_handle.UnpinPage(page_num); 57 | if (rc) { 58 | PF_PrintError(rc); 59 | return rc; 60 | } 61 | 62 | //Close the file 63 | if ((rc = _pf_manager.CloseFile(file_handle)) != 0) { 64 | PF_PrintError(rc); 65 | return rc; 66 | } 67 | return 0; 68 | } 69 | 70 | int RecordManager::destroyFile(std::string filename) { 71 | return _pf_manager.DestroyFile(filename.c_str()); 72 | } 73 | 74 | int RecordManager::openFile(std::string filename, RM_FileHandle &file_handle) { 75 | int rc; 76 | 77 | // Create the Heade Page 78 | if ((rc = _pf_manager.OpenFile(filename.c_str(), file_handle._pf_file_handle)) != 0) { 79 | return rc; 80 | } 81 | 82 | PF_PageHandle page_handle; 83 | file_handle._pf_file_handle.GetFirstPage(page_handle); 84 | char *data; 85 | page_handle.GetData(data); 86 | auto header = reinterpret_cast(data); 87 | file_handle._header_page = *header; 88 | file_handle._header_modified = false; 89 | file_handle._initialized = true; 90 | 91 | // Release the Header Page 92 | if ((rc = file_handle._pf_file_handle.UnpinPage(0)) != 0) { 93 | PF_PrintError(rc); 94 | return rc; 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | int RecordManager::closeFile(RM_FileHandle &file_handle) { 101 | int rc; 102 | if (file_handle._header_modified) { 103 | PF_PageHandle page_handle; 104 | file_handle._pf_file_handle.GetFirstPage(page_handle); 105 | char *data; 106 | page_handle.GetData(data); 107 | auto header = reinterpret_cast(data); 108 | *header = file_handle._header_page; 109 | file_handle._pf_file_handle.MarkDirty(0); 110 | file_handle._pf_file_handle.UnpinPage(0); 111 | } 112 | 113 | //Close the file 114 | if ((rc = _pf_manager.CloseFile(file_handle._pf_file_handle)) != 0) { 115 | PF_PrintError(rc); 116 | return rc; 117 | } 118 | 119 | file_handle._initialized = false; 120 | return 0; 121 | } 122 | 123 | RecordManager &RecordManager::getInstance() { 124 | static RecordManager instance; 125 | return instance; 126 | } 127 | -------------------------------------------------------------------------------- /src/rm/RecordManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/28. 3 | // 4 | 5 | #ifndef MYDB_RECORDMANAGER_H 6 | #define MYDB_RECORDMANAGER_H 7 | 8 | #include "RM_FileHandle.h" 9 | #include "RM_FileScan.h" 10 | #include "HeaderPage.h" 11 | #include "../pf/pf.h" 12 | 13 | #include 14 | 15 | class RecordManager { 16 | public: 17 | ~RecordManager() = default; 18 | 19 | int createFile (std::string, unsigned record_size); 20 | int destroyFile(std::string filename); 21 | int openFile(std::string filename, RM_FileHandle &file_handle); 22 | int closeFile(RM_FileHandle &file_handle); 23 | static RecordManager & getInstance(); 24 | private: 25 | RecordManager(); 26 | PF_Manager & _pf_manager; 27 | }; 28 | 29 | #endif //MYDB_RECORDMANAGER_H 30 | -------------------------------------------------------------------------------- /src/sm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/sm.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/sm_internal.h 5 | PARENT_SCOPE 6 | ) 7 | 8 | set(SOURCE 9 | ${SOURCE} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/sm_error.cc 11 | ${CMAKE_CURRENT_SOURCE_DIR}/sm_manager.cc 12 | ${CMAKE_CURRENT_SOURCE_DIR}/sm_internal.cc 13 | PARENT_SCOPE 14 | ) -------------------------------------------------------------------------------- /src/sm/sm.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_H 2 | #define SM_H 3 | 4 | #include "../Constants.h" 5 | #include "../parser/Tree.h" 6 | #include "../pf/pf.h" 7 | #include "../ix/ix.h" 8 | #include "../rm/RecordManager.h" 9 | 10 | 11 | class SM_Manager { 12 | public: 13 | SM_Manager (); // Constructor 14 | ~SM_Manager (); // Destructor 15 | RC CreateDb (const char *dbName); // Create database 16 | RC DropDb (const char *dbName); // Drop database 17 | RC OpenDb (const char *dbName); // Open database 18 | RC CloseDb (); // Close database 19 | RC CreateTable (const char *tableName, 20 | ColumnDecsList *columns, 21 | TableConstraintList *tableConstraints); 22 | RC GetTableInfo(const char *tableName, 23 | ColumnDecsList &columns, 24 | TableConstraintList &tableConstraints); 25 | RC DropTable (const char *relName); // Destroy relation 26 | RC CreateIndex (const char *relName, // Create index 27 | const char *attrName); 28 | RC DropIndex (const char *relName, // Destroy index 29 | const char *attrName); 30 | RC Load (const char *relName, // Load utility 31 | const char *fileName); 32 | RC Help (); // Help for database 33 | RC Help (const char *relName); // Help for relation 34 | RC Print (const char *relName); // Print relation 35 | static SM_Manager* getInstance(); 36 | private: 37 | IX_Manager *ixm; 38 | RecordManager *rmm; 39 | PF_Manager & pfManager; 40 | void GenerateTableMetadataDir(const char *tableName, char *s); 41 | void GenerateTableRecordDir(const char *tableName, char *s); 42 | bool foreignTableExist(const char* relName); 43 | }; 44 | 45 | // 46 | // Print-error function 47 | // 48 | void SM_PrintError(RC rc); 49 | 50 | 51 | #define SM_REL_EXISTS (START_SM_WARN + 0) 52 | #define SM_REL_NOTEXIST (START_SM_WARN + 1) 53 | #define SM_ATTR_NOTEXIST (START_SM_WARN + 2) 54 | #define SM_INDEX_EXISTS (START_SM_WARN + 3) 55 | #define SM_INDEX_NOTEXIST (START_SM_WARN + 4) 56 | #define SM_FILE_FORMAT_INCORRECT (START_SM_WARN + 5) 57 | #define SM_FILE_NOT_FOUND (START_SM_WARN + 6) 58 | #define SM_FOREIGN_REL_NOT_FOUND (START_SM_WARN + 7) 59 | #define SM_FOREIGN_KEY_NOT_FOUND (START_SM_WARN + 8) 60 | #define SM_LASTWARN SM_FILE_NOT_FOUND 61 | 62 | 63 | #define SM_CHDIR_FAILED (START_SM_ERR - 0) 64 | #define SM_CATALOG_CORRUPT (START_SM_ERR - 1) 65 | #define SM_LASTERROR SM_CATALOG_CORRUPT 66 | 67 | #endif -------------------------------------------------------------------------------- /src/sm/sm_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: sm_error.cc 3 | // Description: SM_PrintError function 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include "sm.h" 10 | 11 | using namespace std; 12 | 13 | // 14 | // Error table 15 | // 16 | static const char *SM_WarnMsg[] = { 17 | "relation already exists", 18 | "relation does not exist", 19 | "attribute does not exist for given relation", 20 | "index already exists for given attribute", 21 | "index does not exist for given attribute", 22 | "file to load has incorrect format", 23 | "file not found", 24 | "length of string-typed attribute should not exceed MAXSTRINGLEN=255" 25 | }; 26 | 27 | static const char *SM_ErrorMsg[] = { 28 | "chdir command execution failed", 29 | "database catalog file is corrupt", 30 | }; 31 | 32 | // 33 | // SM_PrintError 34 | // 35 | // Desc: Send a message corresponding to a SM return code to cerr 36 | // In: rc - return code for which a message is desired 37 | // 38 | void SM_PrintError(RC rc) { 39 | // Check the return code is within proper limits 40 | if (rc >= START_SM_WARN && rc <= SM_LASTWARN) 41 | // Print warning 42 | cerr << "SM warning: " << SM_WarnMsg[rc - START_SM_WARN] << "\n"; 43 | // Error codes are negative, so invert everything 44 | else if ((-rc >= -START_SM_ERR) && -rc <= -SM_LASTERROR) { 45 | // Print error 46 | cerr << "SM error: " << SM_ErrorMsg[-rc + START_SM_ERR] << "\n"; 47 | } else if (rc == 0) 48 | cerr << "SM_PrintError called with return code of 0\n"; 49 | else { 50 | // Print error 51 | cerr << "rc was " << rc << endl; 52 | cerr << "START_SM_ERR was " << START_SM_ERR << endl; 53 | cerr << "SM_LASTERROR was " << SM_LASTERROR << endl; 54 | cerr << "SM error: " << rc << " is out of bounds\n"; 55 | } 56 | } -------------------------------------------------------------------------------- /src/sm/sm_internal.cc: -------------------------------------------------------------------------------- 1 | #include "sm_internal.h" 2 | #include "../parser/Expr.h" 3 | #include 4 | #include 5 | #include 6 | 7 | ConstNode::ConstNode(int i) { 8 | is_null = false; 9 | value_i = i; 10 | attrType = AttrType::INT; 11 | } 12 | 13 | ConstNode::ConstNode(float f) { 14 | is_null = false; 15 | value_f = f; 16 | attrType = AttrType::FLOAT; 17 | } 18 | 19 | ConstNode::ConstNode(const char *s) { 20 | //printf("ConstNode::ConstNode s = %s\n", s); 21 | is_null = false; 22 | int length = strlen(s); 23 | for (int i = 0; i < length; ++i) 24 | value_s[i] = s[i]; 25 | value_s[length] = '\0'; 26 | //printf("ConstNode::ConstNode value_s = %s\n", value_s); 27 | //memset(value_s, 0, sizeof value_s); 28 | //strcpy(value_s, s); 29 | attrType = AttrType::STRING; 30 | } 31 | 32 | ConstNode *getConstNodeFromExpr(Expr *expr) { 33 | if (expr->oper.constType == AttrType::INT) { 34 | return new ConstNode(expr->value.i); 35 | } else if (expr->oper.constType == AttrType::FLOAT) { 36 | return new ConstNode(expr->value.f); 37 | } else if (expr->oper.constType == AttrType::STRING) { 38 | return new ConstNode((expr->value_s).c_str()); 39 | } else { 40 | return new ConstNode(); 41 | } 42 | } 43 | 44 | Expr *getExprFromConstNode(ConstNode *constNode) { 45 | if (constNode->attrType == AttrType::INT) { 46 | return new Expr(constNode->value_i); 47 | } else if (constNode->attrType == AttrType::FLOAT) { 48 | return new Expr(constNode->value_f); 49 | } else if (constNode->attrType == AttrType::STRING) { 50 | //printf("getExprFromConstNode value_s src = %s\n", constNode->value_s); 51 | char s[1010] = {}; 52 | strcat(s, "("); 53 | strcat(s, constNode->value_s); 54 | strcat(s, ")"); 55 | assert(strlen(s) == strlen(constNode->value_s) + 2); 56 | Expr *_ = new Expr(s); 57 | return _; 58 | } else { 59 | return new Expr(); 60 | } 61 | } 62 | 63 | void printAttrType(AttrType attrType) { 64 | switch (attrType) { 65 | case AttrType::INT: { 66 | printf("INT"); 67 | break; 68 | } 69 | case AttrType::FLOAT: { 70 | printf("FLOAT"); 71 | break; 72 | } 73 | case AttrType::STRING: { 74 | printf("STRING"); 75 | break; 76 | } 77 | case AttrType::DATE: { 78 | printf("DATE"); 79 | break; 80 | } 81 | case AttrType::VARCHAR: { 82 | printf("VARCHAR"); 83 | break; 84 | } 85 | case AttrType::NO_ATTR: { 86 | printf("NO_ATTR"); 87 | break; 88 | } 89 | default: { 90 | } 91 | } 92 | } 93 | 94 | void printConstraintType(ConstraintType constraintType) { 95 | switch (constraintType) { 96 | case ConstraintType::PRIMARY_CONSTRAINT: { 97 | printf("PRIMARY_CONSTRAINT"); 98 | break; 99 | } 100 | case ConstraintType::FOREIGN_CONSTRAINT: { 101 | printf("FOREIGN_CONSTRAINT"); 102 | break; 103 | } 104 | case ConstraintType::CHECK_CONSTRAINT: { 105 | printf("CHECK_CONSTRAINT"); 106 | break; 107 | } 108 | default: { 109 | } 110 | } 111 | } 112 | 113 | void printConstNodeExpr(Expr *expr) { 114 | switch (expr->oper.constType) { 115 | case AttrType::INT: { 116 | printf("%d", expr->value.i); 117 | break; 118 | } 119 | case AttrType::FLOAT: { 120 | printf("%f", expr->value.f); 121 | break; 122 | } 123 | case AttrType::STRING: { 124 | printf("'%s'", (expr->value_s).c_str()); 125 | break; 126 | } 127 | case AttrType::NO_ATTR: { 128 | printf("NULL"); 129 | break; 130 | } 131 | default: { 132 | } 133 | } 134 | } 135 | 136 | void Debug(const char *file, int line, int err) { 137 | if (err != 0) { 138 | printf("Error in file %s, line = %d code = %d\n", file, line, err); 139 | } 140 | } 141 | 142 | void ColumnList::addColumn(const char *column) { 143 | strcpy(columns[columnCount++], column); 144 | } 145 | 146 | IdentList *getIdentListFromColumnList(ColumnList *columnList) { 147 | IdentList *identList = new IdentList(); 148 | for (int i = 0; i < columnList->columnCount; ++i) 149 | identList->addIdent(columnList->columns[i]); 150 | return identList; 151 | } 152 | 153 | void TableList::operator=(const TableList &_) { 154 | memcpy(tables, _.tables, sizeof(tables)); 155 | tableCount = _.tableCount; 156 | } 157 | 158 | void ConstNode::operator=(const ConstNode &_) { 159 | //printf("ConstNode::operator =, in\n"); 160 | value_i = _.value_i; 161 | value_f = _.value_f; 162 | memset(value_s, 0, sizeof(value_s)); 163 | int length = strlen(_.value_s); 164 | for (int i = 0; i < length; ++i) 165 | value_s[i] = _.value_s[i]; 166 | value_s[length] = '\0'; 167 | //printf("after ConstNode::operator =, value_s = %s\n", value_s); 168 | //memcpy(value_s, _.value_s, sizeof (value_s)); 169 | attrType = _.attrType; 170 | is_null = _.is_null; 171 | //printf("ConstNode::operator =, out\n"); 172 | } 173 | 174 | ColumnList &ColumnList::operator=(const ColumnList &_) { 175 | memcpy(columns, _.columns, sizeof(columns)); 176 | columnCount = _.columnCount; 177 | return *this; 178 | } 179 | 180 | TableCons &TableCons::operator=(const TableCons &_) { 181 | type = _.type; 182 | constSize = _.constSize; 183 | for (int i = 0; i < _.constSize; ++i) 184 | constNodes[i] = _.constNodes[i]; 185 | columnList = _.columnList; 186 | memcpy(column_name, _.column_name, sizeof(column_name)); 187 | memcpy(foreign_table, _.foreign_table, sizeof(foreign_table)); 188 | memcpy(foreign_column, _.foreign_column, sizeof(foreign_column)); 189 | return *this; 190 | } 191 | 192 | TableInfo &TableInfo::operator=(const TableInfo &_) { 193 | attrInfoCount = _.attrInfoCount; 194 | for (int i = 0; i < _.attrInfoCount; ++i) 195 | attrInfos[i] = _.attrInfos[i]; 196 | indexedAttrSize = _.indexedAttrSize; 197 | for (int i = 0; i < indexedAttrSize; ++i) 198 | indexedAttr[i] = _.indexedAttr[i]; 199 | tableConsCount = _.tableConsCount; 200 | return *this; 201 | } -------------------------------------------------------------------------------- /src/sm/sm_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_INTERNAL_H 2 | #define SM_INTERNAL_H 3 | 4 | #include "sm.h" 5 | 6 | #define TABLE_COUNT 21 7 | 8 | struct TableList { 9 | char tables[TABLE_COUNT][MAX_NAME]; 10 | int tableCount; 11 | void operator = (const TableList &); 12 | }; 13 | struct ConstNode { 14 | int value_i = 0; 15 | float value_f = 0.0; 16 | char value_s[MAX_STRING_LEN]; 17 | AttrType attrType = AttrType::NO_ATTR; 18 | bool is_null = true; 19 | ConstNode() = default; 20 | ConstNode(int i); 21 | ConstNode(float f); 22 | ConstNode(const char *s); 23 | void operator = (const ConstNode &); 24 | }; 25 | struct ColumnList { 26 | char columns[MAX_ATTRS][MAX_NAME]; 27 | int columnCount; 28 | void addColumn(const char* column); 29 | ColumnList & operator = (const ColumnList &); 30 | }; 31 | struct TableCons { 32 | ConstraintType type; 33 | ConstNode constNodes[MAX_ATTRS]; 34 | int constSize; 35 | ColumnList columnList; 36 | char column_name[MAX_NAME]; 37 | char foreign_table[MAX_NAME]; 38 | char foreign_column[MAX_NAME]; 39 | TableCons & operator = (const TableCons &); 40 | }; 41 | struct TableInfo { 42 | int attrInfoCount; 43 | AttrInfo attrInfos[MAX_ATTRS]; 44 | int indexedAttrSize; 45 | int indexedAttr[MAX_ATTRS]; 46 | int tableConsCount; 47 | TableInfo & operator = (const TableInfo &); 48 | }; 49 | 50 | ConstNode* getConstNodeFromExpr(Expr *expr); 51 | Expr* getExprFromConstNode(ConstNode *constNode); 52 | void printAttrType(AttrType attrType); 53 | void printConstraintType(ConstraintType constraintType); 54 | void printConstNodeExpr(Expr *expr); 55 | void Debug(const char* file, int line, int err); 56 | IdentList* getIdentListFromColumnList(ColumnList *columnList); 57 | #endif -------------------------------------------------------------------------------- /src/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ${HEADERS} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/MyBitMap.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/FuncTemplate.h 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Date.h 6 | PARENT_SCOPE 7 | ) 8 | 9 | set(SOURCE 10 | ${SOURCE} 11 | ${CMAKE_CURRENT_SOURCE_DIR}/MyBitMap.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/FuncTemplate.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/Date.cpp 14 | PARENT_SCOPE 15 | ) 16 | -------------------------------------------------------------------------------- /src/utils/Date.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2019-01-01. 3 | // 4 | 5 | #include "Date.h" 6 | #include 7 | 8 | bool IsLeapYear(int year) { 9 | return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); 10 | } 11 | 12 | bool IsLegalDate(int year, int mon, int day) { 13 | //大:1 3 5 7 8 10 12 14 | //小:4 6 9 11 15 | //平:2 16 | 17 | if (year < 0 || mon <= 0 || mon > 12 || day <= 0 || day > 31)return false; 18 | 19 | if (1 == mon || 3 == mon || 5 == mon || 7 == mon || 8 == mon || 10 == mon || 12 == mon) { 20 | return true; 21 | } 22 | if (mon != 2) { 23 | return day != 31; 24 | } 25 | if (IsLeapYear(year)) { 26 | return !(30 == day || 31 == day); 27 | } else { 28 | return !(29 == day || 30 == day || 31 == day); 29 | } 30 | } 31 | 32 | int parseData(const char *date) { 33 | int year, month, day; 34 | char *str; 35 | year = static_cast(strtol(date, &str, 10)); 36 | if (*str == '-') { 37 | str++; 38 | month = static_cast(strtol(str, &str, 10)); 39 | if (*str == '-') { 40 | str++; 41 | day = static_cast(strtol(str, &str, 10)); 42 | if (IsLegalDate(year, month, day)) { 43 | return (year - 1) * 31 * 12 + (month - 1) * 31 + (day - 1); 44 | } 45 | } 46 | } 47 | return -1; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/Date.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2019-01-01. 3 | // 4 | 5 | #ifndef MYDB_DATA_H 6 | #define MYDB_DATA_H 7 | 8 | #include 9 | 10 | bool IsLegalDate(int year, int mon, int day); 11 | 12 | int parseData(const char *date); 13 | #endif //MYDB_DATA_H 14 | -------------------------------------------------------------------------------- /src/utils/FuncTemplate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018-12-23. 3 | // 4 | 5 | #include "FuncTemplate.h" 6 | #include 7 | 8 | bool comp_function(const std::string &a, const std::string &b, CompOp compOp) { 9 | if (compOp == CompOp::LIKE_OP) { 10 | std::string regexStr; 11 | char status = 'A'; 12 | for (char i : b) { 13 | if (status == 'A') { 14 | // common status 15 | if (i == '\\') { 16 | status = 'B'; 17 | } else if (i == '[') { 18 | regexStr += "["; 19 | status = 'C'; 20 | } else if (i == '%') { 21 | regexStr += ".*"; 22 | } else if (i == '_') { 23 | regexStr += "."; 24 | } else { 25 | regexStr += i; 26 | } 27 | } else if (status == 'B') { 28 | // after '\' 29 | if (i == '%' || i == '_' || i == '!') { 30 | regexStr += i; 31 | } else { 32 | regexStr += "\\"; 33 | regexStr += i; 34 | } 35 | status = 'A'; 36 | } else { 37 | // after '[' inside [] 38 | if (i == '!') { 39 | regexStr += "^"; 40 | } else { 41 | regexStr += i; 42 | } 43 | status = 'A'; 44 | } 45 | } 46 | std::regex reg(regexStr); 47 | return std::regex_match(a, reg); 48 | } else { 49 | switch (compOp) { 50 | case CompOp::EQ_OP: 51 | return a == b; 52 | case CompOp::GE_OP: 53 | return a >= b; 54 | case CompOp::GT_OP: 55 | return a > b; 56 | case CompOp::LE_OP: 57 | return a <= b; 58 | case CompOp::LT_OP: 59 | return a < b; 60 | case CompOp::NE_OP: 61 | return a != b; 62 | case CompOp::NO_OP: 63 | return true; 64 | default: 65 | return false; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | bool logic_function(bool a, bool b, LogicOp logicOp) { 72 | switch (logicOp) { 73 | case LogicOp::AND_OP: 74 | return a and b; 75 | case LogicOp::OR_OP: 76 | return a or b; 77 | case LogicOp::NOT_OP: 78 | return not a; 79 | case LogicOp::NO_OP: 80 | break; 81 | } 82 | return false; 83 | } 84 | 85 | bool isComparison(CompOp op) { 86 | switch (op) { 87 | case CompOp::EQ_OP: 88 | case CompOp::GE_OP: 89 | case CompOp::GT_OP: 90 | case CompOp::LE_OP: 91 | case CompOp::LT_OP: 92 | case CompOp::NE_OP: 93 | return true; 94 | case CompOp::NO_OP: 95 | case CompOp::LIKE_OP: 96 | case CompOp::IS_OP: 97 | case CompOp::ISNOT_OP: 98 | return false; 99 | } 100 | return false; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /src/utils/FuncTemplate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018-12-23. 3 | // 4 | 5 | #ifndef MYDB_FUNCTEMPLATE_H 6 | #define MYDB_FUNCTEMPLATE_H 7 | 8 | #include "../Constants.h" 9 | 10 | template 11 | bool comp_function(const T &a, const T &b, CompOp compOp) { 12 | switch (compOp) { 13 | case CompOp::EQ_OP: 14 | return a == b; 15 | case CompOp::GE_OP: 16 | return not(a < b); 17 | case CompOp::GT_OP: 18 | return not(a < b) and not(a == b); 19 | case CompOp::LE_OP: 20 | return a < b or a == b; 21 | case CompOp::LT_OP: 22 | return a < b; 23 | case CompOp::NE_OP: 24 | return not(a == b); 25 | case CompOp::NO_OP: 26 | return true; 27 | case CompOp::IS_OP: 28 | // todo implement this 29 | break; 30 | default: 31 | return false; 32 | } 33 | return false; 34 | } 35 | 36 | bool comp_function(const std::string &a, const std::string &b, CompOp compOp); 37 | 38 | template 39 | T arith_function(T a, T b, ArithOp arithOp) { 40 | switch (arithOp) { 41 | case ArithOp::ADD_OP: 42 | return a + b; 43 | case ArithOp::SUB_OP: 44 | return a - b; 45 | case ArithOp::MUL_OP: 46 | return a * b; 47 | case ArithOp::DIV_OP: 48 | return a / b; 49 | case ArithOp::MINUS_OP: 50 | return -a; 51 | case ArithOp::NO_OP: 52 | break; 53 | } 54 | return T{}; 55 | } 56 | 57 | bool logic_function(bool a, bool b, LogicOp logicOp); 58 | bool isComparison(CompOp op); 59 | 60 | #endif //MYDB_FUNCTEMPLATE_H 61 | -------------------------------------------------------------------------------- /src/utils/MyBitMap.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Zhengxiao Du on 2018/10/29. 3 | // 4 | 5 | #include "MyBitMap.h" 6 | 7 | static unsigned char h[61]; 8 | 9 | uint MyBitMap::getMask(int k) 10 | { 11 | uint s = 0; 12 | for (int i = 0; i < k; ++i) 13 | { 14 | s += (1 << i); 15 | } 16 | return s; 17 | } 18 | 19 | int MyBitMap::findLeftOne() 20 | { 21 | bool full = true; 22 | for (int i = 0; i < size; ++i) 23 | { 24 | if(data[i]) 25 | full = false; 26 | } 27 | if(full) { 28 | return size * 32 + 1; 29 | } 30 | int i = _findLeftOne(rootLevel, rootIndex, 0, rootBit); 31 | /* 32 | for (i = 0; i < size;++i){ 33 | if (data[i] !=0)break; 34 | }*/ 35 | //cout << "nPosi " << i << " " << getLeafData(i) << endl; 36 | //cout << i << endl; 37 | //cout << data[0] << endl; 38 | uint lb = lowbit(getLeafData(i)); 39 | int index = h[_hash(lb)]; 40 | return (i << BIAS) + index; 41 | } 42 | 43 | void MyBitMap::initConst() 44 | { 45 | for (int i = 0; i < 32; ++i) 46 | { 47 | unsigned int k = (1 << i); 48 | h[_hash(k)] = i; 49 | } 50 | } 51 | 52 | int MyBitMap::getIndex(uint k) 53 | { 54 | return h[_hash(k)]; 55 | } 56 | 57 | void MyBitMap::getPos(int index, int &pos, int &bit) 58 | { 59 | pos = (index >> BIAS); 60 | bit = index - (pos << BIAS); 61 | } 62 | 63 | int MyBitMap::_findLeftOne(int level, int offset, int pos, int prevLevelCap) 64 | { 65 | uint lb = lowbit(inner[offset + pos]); 66 | int index = h[_hash(lb)]; 67 | /*if (level == 0) { 68 | cout << "level0:" << index << " " << pos << endl; 69 | }*/ 70 | int nPos = (pos << BIAS) + index; 71 | if (level == 0) 72 | { 73 | // cout << "npos " << nPos << endl; 74 | return nPos; 75 | } 76 | return _findLeftOne(level - 1, offset - prevLevelCap, nPos, (prevLevelCap << BIAS)); 77 | } 78 | 79 | uint *MyBitMap::getData() const 80 | { 81 | return data; 82 | } 83 | -------------------------------------------------------------------------------- /src/utils/MyBitMap.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_BIT_MAP 2 | #define MY_BIT_MAP 3 | typedef unsigned int uint; 4 | /* 5 | #define LEAF_BIT 32 6 | #define MAX_LEVEL 5 7 | #define MAX_INNER_NUM 67 8 | #define MOD 61 9 | #define BIAS 5*/ 10 | #include 11 | 12 | using namespace std; 13 | 14 | #define LEAF_BIT 32 15 | #define MAX_LEVEL 5 16 | #define MAX_INNER_NUM 67 17 | //#define MOD 61 18 | #define BIAS 5 19 | 20 | 21 | class MyBitMap { 22 | protected: 23 | // static const int LEAF_BIT = 32; 24 | // static const int MAX_LEVEL = 5; 25 | // static const int MAX_INNER_NUM = 10; 26 | // static const int MOD = 61; 27 | // static unsigned char h[MOD]; 28 | static uint getMask(int k); 29 | 30 | uint *data; 31 | public: 32 | uint *getData() const; 33 | 34 | protected: 35 | int size; 36 | int rootBit; 37 | int rootLevel; 38 | int rootIndex; 39 | uint inner[MAX_INNER_NUM]; 40 | uint innerMask; 41 | uint rootMask; 42 | 43 | //virtual 44 | uint getLeafData(int index) 45 | { 46 | return data[index]; 47 | } 48 | 49 | //virtual 50 | void setLeafData(int index, uint v) 51 | { 52 | data[index] = v; 53 | } 54 | 55 | int setLeafBit(int index, uint k) 56 | { 57 | int pos, bit; 58 | getPos(index, pos, bit); 59 | uint umask = (1 << bit); 60 | uint mask = (~umask); 61 | if (k == 0) 62 | { 63 | umask = 0; 64 | } 65 | uint w = ((getLeafData(pos) & mask) | umask); 66 | setLeafData(pos, w); 67 | return pos; 68 | } 69 | 70 | uint childWord(int start, int bitNum, int i, int j) 71 | { 72 | //cout << start << " " << bitNum << " " << i << " " << j << endl; 73 | int index = (i << BIAS) + j; 74 | if (start == 0) 75 | { 76 | return getLeafData(index); 77 | } else 78 | { 79 | //cout << start - bitNum + index << endl; 80 | return inner[start - bitNum + index]; 81 | } 82 | } 83 | 84 | void init() 85 | { 86 | rootLevel = 0; 87 | int s = size; 88 | rootIndex = 0; 89 | while (s > LEAF_BIT) 90 | { 91 | int wordNum = (s >> BIAS); 92 | //cout << rootIndex << " " << s << " " << wordNum << endl; 93 | for (int i = 0; i < wordNum; ++i) 94 | { 95 | uint w = 0; 96 | //cout << "---------------------------------------" << endl; 97 | for (int j = 0; j < LEAF_BIT; ++j) 98 | { 99 | //cout << i << endl; 100 | uint k = (1 << j); 101 | uint c = childWord(rootIndex, s, i, j); 102 | if (c != 0) 103 | { 104 | w += k; 105 | } 106 | } 107 | inner[rootIndex + i] = w; 108 | } 109 | rootLevel++; 110 | rootIndex += wordNum; 111 | s = wordNum; 112 | } 113 | rootBit = s; 114 | int i = 0; 115 | uint w = 0; 116 | for (int j = 0; j < rootBit; ++j) 117 | { 118 | uint k = (1 << j); 119 | uint c = childWord(rootIndex, s, i, j); 120 | if (c != 0) 121 | { 122 | w += k; 123 | } 124 | } 125 | inner[rootIndex] = w; 126 | innerMask = getMask(BIAS); 127 | rootMask = getMask(s); 128 | } 129 | 130 | int _setBit(uint *start, int index, uint k) 131 | { 132 | int pos, bit; 133 | getPos(index, pos, bit); 134 | uint umask = (1 << bit); 135 | uint mask = (~umask); 136 | if (k == 0) 137 | { 138 | umask = 0; 139 | } 140 | start[pos] = ((start[pos] & mask) | umask); 141 | return pos; 142 | } 143 | 144 | void updateInner(int level, int offset, int index, int levelCap, uint k) 145 | { 146 | //cout << level << " " << rootLevel << endl; 147 | uint *start = (&inner[offset]); 148 | int pos = _setBit(start, index, k); 149 | if (level == rootLevel) 150 | { 151 | return; 152 | } 153 | uint c = 1; 154 | if (start[pos] == 0) 155 | { 156 | c = 0; 157 | } 158 | /* 159 | if (level == 1) { 160 | cout << "level1:" << start[index] << " " << c << endl; 161 | }*/ 162 | updateInner(level + 1, offset + levelCap, pos, (levelCap >> BIAS), c); 163 | } 164 | 165 | int _findLeftOne(int level, int offset, int pos, int prevLevelCap); 166 | 167 | public: 168 | // static const int BIAS;/* = 5;*/ 169 | // static void initConst(); 170 | /* { 171 | for (int i = 0; i < 32; ++ i) { 172 | unsigned int k = (1 << i); 173 | MyBitMap::h[MyBitMap::_hash(k)] = i; 174 | } 175 | } 176 | */ 177 | static int _hash(uint i) 178 | { 179 | return i % 61; 180 | } 181 | 182 | static void initConst(); 183 | 184 | static int getIndex(uint k); 185 | 186 | static uint lowbit(uint k) 187 | { 188 | return (k & (-k)); 189 | } 190 | 191 | static void getPos(int index, int &pos, int &bit); 192 | 193 | uint data0() 194 | { 195 | return data[0]; 196 | } 197 | 198 | void setBit(int index, uint k) 199 | { 200 | //cout << data[0]<> BIAS), c); 211 | } 212 | 213 | int findLeftOne(); 214 | 215 | MyBitMap(int cap, uint k) 216 | { 217 | size = (cap >> BIAS); 218 | data = new uint[size]; 219 | uint fill = 0; 220 | if (k == 1) 221 | { 222 | fill = 0xffffffff; 223 | } 224 | for (int i = 0; i < size; ++i) 225 | { 226 | data[i] = fill; 227 | } 228 | init(); 229 | } 230 | 231 | MyBitMap(int cap, uint *da) 232 | { 233 | data = da; 234 | size = (cap >> BIAS); 235 | init(); 236 | } 237 | 238 | void reLoad(uint *da) 239 | { 240 | data = da; 241 | } 242 | }; 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /src/utils/MyHashMap.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_HASH_MAP 2 | #define MY_HASH_MAP 3 | #include "pagedef.h" 4 | #include "MyLinkList.h" 5 | /* 6 | * hash表的键 7 | */ 8 | struct DataNode { 9 | /* 10 | * 第一个键 11 | */ 12 | int key1; 13 | /* 14 | * 第二个键 15 | */ 16 | int key2; 17 | }; 18 | /* 19 | * 两个键的hash表 20 | * hash表的value是自然数,在缓存管理器中,hash表的value用来表示缓存页面数组的下标 21 | */ 22 | class MyHashMap { 23 | private: 24 | static const int A = 1; 25 | static const int B = 1; 26 | int CAP_, MOD_; 27 | MyLinkList* list; 28 | DataNode* a; 29 | /* 30 | * hash函数 31 | */ 32 | int hash(int k1, int k2) { 33 | return (k1 * A + k2 * B) % MOD_; 34 | } 35 | public: 36 | /* 37 | * @函数名findIndex 38 | * @参数k1:第一个键 39 | * @参数k2:第二个键 40 | * 返回:根据k1和k2,找到hash表中对应的value 41 | * 这里的value是自然数,如果没有找到,则返回-1 42 | */ 43 | int findIndex(int k1, int k2) { 44 | int h = hash(k1, k2); 45 | int p = list->getFirst(h); 46 | while (!list->isHead(p)) { 47 | if (a[p].key1 == k1 && a[p].key2 == k2) { 48 | /* 49 | list.del(p); 50 | list.insertFirst(p); 51 | */ 52 | return p; 53 | } 54 | p = list->next(p); 55 | } 56 | return -1; 57 | } 58 | /* 59 | * @函数名replace 60 | * @参数index:指定的value 61 | * @参数k1:指定的第一个key 62 | * @参数k2:指定的第二个key 63 | * 功能:在hash表中,将指定value对应的两个key设置为k1和k2 64 | */ 65 | void replace(int index, int k1, int k2) { 66 | int h = hash(k1, k2); 67 | //cout << h << endl; 68 | list->insertFirst(h, index); 69 | a[index].key1 = k1; 70 | a[index].key2 = k2; 71 | } 72 | /* 73 | * @函数名remove 74 | * @参数index:指定的value 75 | * 功能:在hash表中,将指定的value删掉 76 | */ 77 | void remove(int index) { 78 | list->del(index); 79 | a[index].key1 = -1; 80 | a[index].key2 = -1; 81 | } 82 | /* 83 | * @函数名getKeys 84 | * @参数index:指定的value 85 | * @参数k1:存储指定value对应的第一个key 86 | * @参数k2:存储指定value对应的第二个key 87 | */ 88 | void getKeys(int index, int& k1, int& k2) { 89 | k1 = a[index].key1; 90 | k2 = a[index].key2; 91 | } 92 | /* 93 | * 构造函数 94 | * @参数c:hash表的容量上限 95 | * @参数m:hash函数的mod 96 | */ 97 | MyHashMap(int c, int m) { 98 | CAP_ = c; 99 | MOD_ = m; 100 | a = new DataNode[c]; 101 | for (int i = 0; i < CAP_; ++ i) { 102 | a[i].key1 = -1; 103 | a[i].key2 = -1; 104 | } 105 | list = new MyLinkList(CAP_, MOD_); 106 | } 107 | }; 108 | #endif 109 | -------------------------------------------------------------------------------- /src/utils/MyLinkList.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_LINK_LIST 2 | #define MY_LINK_LIST 3 | //template 4 | class MyLinkList { 5 | private: 6 | struct ListNode { 7 | int next; 8 | int prev; 9 | }; 10 | int cap; 11 | int LIST_NUM; 12 | ListNode* a; 13 | void link(int prev, int next) { 14 | a[prev].next = next; 15 | a[next].prev = prev; 16 | } 17 | public: 18 | void del(int index) { 19 | if (a[index].prev == index) { 20 | return; 21 | } 22 | link(a[index].prev, a[index].next); 23 | a[index].prev = index; 24 | a[index].next = index; 25 | } 26 | void insert(int listID, int ele) { 27 | del(ele); 28 | int node = listID + cap; 29 | int prev = a[node].prev; 30 | link(prev, ele); 31 | link(ele, node); 32 | } 33 | void insertFirst(int listID, int ele) { 34 | del(ele); 35 | int node = listID + cap; 36 | int next = a[node].next; 37 | link(node, ele); 38 | link(ele, next); 39 | } 40 | int getFirst(int listID) { 41 | return a[listID + cap].next; 42 | } 43 | int next(int index) { 44 | return a[index].next; 45 | } 46 | bool isHead(int index) { 47 | if (index < cap) { 48 | return false; 49 | } else { 50 | return true; 51 | } 52 | } 53 | bool isAlone(int index) { 54 | return (a[index].next == index); 55 | } 56 | MyLinkList(int c, int n) { 57 | cap = c; 58 | LIST_NUM = n; 59 | a = new ListNode[n + c]; 60 | for (int i = 0; i < cap + LIST_NUM; ++ i) { 61 | a[i].next = i; 62 | a[i].prev = i; 63 | } 64 | } 65 | }; 66 | #endif 67 | -------------------------------------------------------------------------------- /src/utils/compare.h: -------------------------------------------------------------------------------- 1 | /* 2 | * parser.h 3 | * 4 | * Created on: 2014年12月7日 5 | * Author: lql 6 | */ 7 | 8 | #ifndef PARSER_H_ 9 | #define PARSER_H_ 10 | 11 | #include "../bplustree/BPlusTree.h" 12 | #include "pagedef.h" 13 | #include 14 | using namespace std; 15 | #define LL_TYPE 0 16 | #define DB_TYPE 1 17 | #define ST_TYPE 2 18 | #define L 0 19 | #define G 3 20 | #define LE 1 21 | #define GE 2 22 | #define E 4 23 | #define IS 5 24 | #define UNI 0 25 | #define UNUNI 1 26 | #define N 0 27 | #define UN 1 28 | #define ALL 0 29 | #define RANGE 1 30 | #define ISNULL 2 31 | #define NOTHING 3 32 | int tmp = 0; 33 | template 34 | int keyu(uchar* a, uchar* b) { 35 | Key c, d; 36 | memcpy(&c, a, sizeof(Key)); 37 | memcpy(&d, b, sizeof(Key)); 38 | return ((c < d) ? -1 : ((c > d) ? 1 : 0)); 39 | } 40 | template 41 | int keyn(uchar* a, uchar* b) { 42 | int res = keyu(a, b); 43 | return (res != 0) ? res : keyu(a + sizeof(Key), b + sizeof(Key)); 44 | } 45 | int su(uchar* a, uchar* b) { 46 | int res = strcmp((char*)a, (char*)b); 47 | return (res > 0) ? 1 : ((res < 0) ? -1 : 0); 48 | } 49 | int sn(uchar* a, uchar* b) { 50 | int res = strcmp((char*)a, (char*)b); 51 | res = (res > 0) ? 1 : ((res < 0) ? -1 : 0); 52 | return (res != 0) ? res : keyu(a + tmp, b + tmp); 53 | } 54 | cf* kcmp[3][2] = { 55 | {&keyu, &keyn}, 56 | {&keyu, &keyn}, 57 | {&su, &sn} 58 | }; 59 | #endif /* PARSER_H_ */ 60 | -------------------------------------------------------------------------------- /src/utils/pagedef.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGE_DEF 2 | #define PAGE_DEF 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../Constants.h" 10 | 11 | #define PAGE_SIZE 8192 12 | /* 13 | * 一个页面中的整数个数 14 | */ 15 | #define PAGE_INT_NUM 2048 16 | /* 17 | * 页面字节数以2为底的指数 18 | */ 19 | #define PAGE_SIZE_IDX 13 20 | #define MAX_FMT_INT_NUM 128 21 | //#define BUF_PAGE_NUM 65536 22 | #define MAX_FILE_NUM 128 23 | #define MAX_TYPE_NUM 256 24 | /* 25 | * 缓存中页面个数上限 26 | */ 27 | #define CAP 60000 28 | /* 29 | * hash算法的模 30 | */ 31 | #define MOD 60000 32 | #define IN_DEBUG 0 33 | #define DEBUG_DELETE 0 34 | #define DEBUG_ERASE 1 35 | #define DEBUG_NEXT 1 36 | /* 37 | * 一个表中列的上限 38 | */ 39 | #define MAX_COL_NUM 31 40 | /* 41 | * 数据库中表的个数上限 42 | */ 43 | #define MAX_TB_NUM 31 44 | #define RELEASE 1 45 | typedef unsigned int uint; 46 | typedef unsigned short ushort; 47 | typedef unsigned char uchar; 48 | typedef unsigned long long ull; 49 | typedef long long ll; 50 | typedef double db; 51 | typedef int INT; 52 | typedef int(cf)(uchar*, uchar*); 53 | #endif 54 | -------------------------------------------------------------------------------- /src/utils/printerr.h: -------------------------------------------------------------------------------- 1 | #ifndef PRINT_ERR_H 2 | #define PRINT_ERR_H 3 | 4 | #include 5 | #include 6 | void printErr(RC code, const char* msg) { 7 | if (code != 0) 8 | printf("code = %d msg = %s\n", code, msg); 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /test/init.sh: -------------------------------------------------------------------------------- 1 | $1 large_dataset/create.sql 2 | $1 large_dataset/customer.sql 3 | $1 large_dataset/restaurant.sql 4 | $1 large_dataset/food.sql 5 | $1 large_dataset/orders.sql 6 | -------------------------------------------------------------------------------- /test/large_dataset/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duzx16/MyDB/7e355398e8be089e2674fbad0fb12211432a22dd/test/large_dataset/.DS_Store -------------------------------------------------------------------------------- /test/large_dataset/create.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE orderDB; 2 | 3 | USE orderDB; 4 | 5 | CREATE TABLE restaurant ( 6 | id INT(10) NOT NULL, 7 | name CHAR(25) NOT NULL, 8 | address CHAR(100), 9 | phone CHAR(20), 10 | rate FLOAT, 11 | PRIMARY KEY (id) 12 | ); 13 | 14 | CREATE TABLE customer( 15 | id INT(10) NOT NULL, 16 | name CHAR(25) NOT NULL, 17 | gender CHAR(1) NOT NULL, 18 | PRIMARY KEY (id), 19 | check (gender in ('F', 'M')) 20 | ); 21 | 22 | CREATE TABLE food( 23 | id INT(10) NOT NULL, 24 | restaurant_id INT(10), 25 | name CHAR(100) NOT NULL, 26 | price FLOAT NOT NULL, 27 | PRIMARY KEY (id), 28 | FOREIGN KEY (restaurant_id) REFERENCES restaurant(id) 29 | ); 30 | 31 | CREATE TABLE orders( 32 | id INT(10) NOT NULL, 33 | customer_id INT(10) NOT NULL, 34 | food_id INT(10) NOT NULL, 35 | date DATE, 36 | quantity INT(10), 37 | PRIMARY KEY (id), 38 | FOREIGN KEY (customer_id) REFERENCES customer(id), 39 | FOREIGN KEY (food_id) REFERENCES food(id) 40 | ); 41 | -------------------------------------------------------------------------------- /test/small_dataset/create.sql: -------------------------------------------------------------------------------- 1 | create database school; 2 | use school; 3 | 4 | create table student( 5 | sid int, 6 | name varchar(20) not null, 7 | sex varchar(20), 8 | age int, 9 | address varchar(20), 10 | primary key(sid), 11 | check(sex in ('man', 'woman'))); 12 | 13 | create table course ( 14 | cid int, 15 | subject varchar(20) not null, 16 | primary key (cid) 17 | ); 18 | 19 | create table grade ( 20 | gid int, 21 | sid int, 22 | cid int, 23 | score int not null, 24 | primary key (gid) 25 | ); -------------------------------------------------------------------------------- /test/small_dataset/test_insert.sql: -------------------------------------------------------------------------------- 1 | use school; 2 | insert into student values (1001, 'Li', 'man', 20, 'sdwf'); 3 | insert into student values (1002, 'Xu', 'man', 23, 'sdgm'); 4 | insert into student values (1003, 'zhao', 'woman', 25, 'sdwf'); 5 | insert into student values (1004, 'Si', 'woman', 24, 'default'); 6 | 7 | insert into course values (1, 'oracle'); 8 | insert into course values (2, 'English'); 9 | insert into course values (3, 'Java'); 10 | insert into course values (4, 'SQL'); 11 | 12 | insert into grade values (101,1001,1,60); 13 | insert into grade values (102,1001,1,70); 14 | insert into grade values (103,1001,1,40); 15 | insert into grade values (104,1001,2,80); 16 | insert into grade values (105,1001,2,90); 17 | insert into grade values (106,1001,3,60); 18 | insert into grade values (107,1002,1,65); 19 | insert into grade(gid, sid, cid) values (108,1003,4); -------------------------------------------------------------------------------- /test/small_dataset/test_select.sql: -------------------------------------------------------------------------------- 1 | use school; 2 | select student.sid, student.name, grade.gid, course.subject, grade.score 3 | from student, course, grade 4 | where student.sid=grade.sid and course.cid=grade.cid; 5 | select cid, avg(score) from grade; 6 | select cid, avg(score) from grade group by cid; 7 | select cid, avg(score) from grade where score>=60 group by cid; 8 | select sid, avg(score) from grade group by sid; 9 | select avg(score) from grade where score>=60; -------------------------------------------------------------------------------- /test/test.sql: -------------------------------------------------------------------------------- 1 | drop database test; 2 | create database test; 3 | use database test; 4 | create table student(id int, score float); 5 | show table student; 6 | insert into student values (1, 1.0); 7 | select * from student; -------------------------------------------------------------------------------- /test/test_delete.sql: -------------------------------------------------------------------------------- 1 | use orderDB; 2 | select * from restaurant where id=4; 3 | insert into restaurant(name, id) values ('test4', 4); 4 | insert into restaurant(name, id) values ('test5', 5); 5 | select * from restaurant where id=4; 6 | delete from restaurant where rate is null; 7 | select * from restaurant where id=4; 8 | delete from restaurant where id=5; 9 | select * from restaurant where id=5; -------------------------------------------------------------------------------- /test/test_insert.sql: -------------------------------------------------------------------------------- 1 | use orderDB; 2 | 3 | -- insert illegal date 4 | insert into orders values (1,309702,201329,'2014-09-31',5); 5 | insert into orders values (2,309702,201329,'2010-02-29',5); 6 | insert into orders values (3,309702,201329,'1900-02-29',5); 7 | 8 | -- insert optional attribute 9 | insert into restaurant values (3, 'test3', 'address','12345678', null); 10 | insert into restaurant(name, id) values ('test1', 0001); 11 | insert into restaurant(name) values ('test2'); 12 | select * from restaurant where id=1; 13 | select * from restaurant where name='test1'; 14 | select * from restaurant where name='test2'; 15 | select * from restaurant where id=3; 16 | delete from restaurant where rate is null; 17 | 18 | -- check in 19 | -- check value ... in 20 | insert into customer values (300001,'CHAD CABELLO','U'); 21 | -- check foreign key 22 | insert into food values (1,1,'Boiled dumplings',64.0236); 23 | -- check primary key 24 | insert into food values (200001,1613,'Boiled dumplings',64.0236); -------------------------------------------------------------------------------- /test/test_select.sql: -------------------------------------------------------------------------------- 1 | use orderDB; 2 | -- select with null condition 3 | select * from orders where id is not null; 4 | -- select with date 5 | select * from orders where date > '2018-01-01'; 6 | -- select with different conditions 7 | select * from orders where customer_id>310375; 8 | select * from orders where customer_id=312195; 9 | -- select with index 10 | create index orders (customer_id); 11 | select * from orders where customer_id=312195; 12 | create index orders (food_id); 13 | select * from orders where food_id>0; 14 | -- select with string like 15 | select * from restaurant where name like 'wu k_ng'; 16 | select * from restaurant where name like 'vivande%'; 17 | 18 | -- select with complex condition 19 | select * from customer where gender='F' or id>314500 and id>314000; 20 | select orders.id, food.name, quantity, price from orders, food where orders.food_id=food.id and quantity * price > 800.0; 21 | 22 | -- select with aggregation 23 | -- illegal 24 | select restaurant_id, avg(price) from food; 25 | select restaurant_id, avg(price) from food group by restaurant_id; 26 | select restaurant_id, avg(price) from food where price>=60 group by restaurant_id; 27 | select avg(price) from food where price>=60; 28 | select food_id, avg(quantity) from orders group by food_id; 29 | 30 | -- select with multiple tables 31 | select * 32 | from orders, customer, food 33 | where food.id=orders.food_id and customer.id=orders.customer_id; 34 | 35 | select * 36 | from orders, food, restaurant 37 | where restaurant.id=food.restaurant_id and food.id=orders.food_id and orders.quantity>1; 38 | -------------------------------------------------------------------------------- /test/test_update.sql: -------------------------------------------------------------------------------- 1 | use orderDB; 2 | update orders set id=-id; 3 | select * from orders where id<0; 4 | update orders set id=-id; 5 | select * from orders where id>0; 6 | 7 | update customer set id=-id where gender='F'; 8 | select * from customer; 9 | update customer set id=-id where id<0; 10 | select * from customer; 11 | -- check value ... in 12 | update customer set gender = 'U'; 13 | -- check foreign key 14 | update food set restaurant_id=1 where restaurant_id=1501; 15 | -- check primary key 16 | update customer set id=300002 where id=300001; 17 | --------------------------------------------------------------------------------