//储存数据类型和对应数据
struct Data {
int type; //-1:int,0:float,1~255:string的长度+1
int datai;
float dataf;
std::string datas;
};//Where存放一组判断语句
struct Where {
Data data; //数据
WHERE relation_character; //关系
};//索引
struct Index {
int num; //index的总数
short location[10]; //第i个index建立在第location[i]个attribute上
string indexname[10]; //每个index的名字
};//属性
struct Attribute {
int num; //存放table的属性数
string name[32]; //存放每个属性的名字
short type[32]; //存放每个属性的类型,-1:int,0:float,1~255:string的长度+1
bool unique[32]; //判断每个属性是否unique,是为true
short primary_key; //判断是否存在主键,-1为不存在,其他则为主键的所在位置
bool has_index[32]; //判断是否存在索引
};//元祖
class Tuple {
private:
vector<Data> data_; //存储元组里的每个数据的信息
bool isDeleted_;
public:
Tuple() : isDeleted_(false) {};
Tuple(const Tuple& tuple_in); //拷贝元组
void addData(Data data_in); //新增元组
vector<Data> getData() const; //返回数据
int getSize(); //返回元组的数据数量
bool isDeleted();
void setDeleted();
void showTuple(); //显示元组中的所有数据
};class Table {
private:
string title_; //表名
vector<Tuple> tuple_; //存放所有的元组
Index index_; //表的索引信息
public:
Attribute attr_; //属性
//构造函数
Table() {};
Table(string title, Attribute attr);
Table(const Table& table_in);
int addTuple(Tuple tuple_in); // 插入元祖,判断tuple_in 的格式是否符合table中属性格式
int setIndex(short index, string index_name); //插入索引,输入要建立索引的Attribute的编号,以及索引的名字,成功返回1失败返回0
int dropIndex(string index_name); //删除索引,输入建立的索引的名字,成功返回1失败返回0
string getTitle();
Attribute getAttr();
vector <Tuple>& getTuple();//返回元祖
Index getIndex();
short gethasKey();//返回主码所在的属性位置
void showTable(); //打印表属性和元祖
void showTable(int limit);//显示table的部分数据
};class Interpreter {
public:
Interpreter();
void getQuery();
void EXEC();
private:
string query;
int getType(int pos, int& end_pos);//返回数据类型
int getBits(int num);//返回整数的长度
int getBits(float num);//返回小数长度
string getRelation(int pos, int& end_pos);//返回逻辑关系
string getWord(int pos, int& end_pos);//返回单词
string getLower(string s, int p);//变小写(便于识别)
void Normalize();
void EXEC_SELECT();
void EXEC_DROP_TABLE();
void EXEC_DROP_INDEX();
void EXEC_CREATE_TABLE();
void EXEC_CREATE_INDEX();
void EXEC_INSERT();
void EXEC_DELETE();
void EXEC_SHOW();
void EXEC_EXIT();
void EXEC_FILE();
void EXEC_HELP();
};interpreter部分负责程序与使用者之间的交互关系
class API {
public:
API();
~API();
Table selectRecord(string table_name, vector<string> target_attr, vector<Where> where, char operation);
int deleteRecord(string table_name, string target_attr, Where where);
void insertRecord(string table_name, Tuple& tuple);
bool createTable(string table_name, Attribute attribute, int primary, Index index);
bool dropTable(string table_name);
bool createIndex(string table_name, string index_name, string attr_name);
bool dropIndex(string table_name, string index_name);
private:
//用于多条件查询时的and条件合并
Table unionTable(Table& table1, Table& table2, string target_attr, Where where);
//用于多条件查询时的or条件合并
Table joinTable(Table& table1, Table& table2, string target_attr, Where where);
RecordManager& record;
CatalogManager& catalog;
};提供与其他模块的接口
Table selectRecord(string table_name, vector<string> target_attr, vector<Where> where, char operation);输入:表名,where的限制条件(target_attr为要where中出现的属性,where中存放对应属性的限制要求,operation存放各where之间的逻辑运算关系(交集/并集))
输出:select语句对应的table
功能:根据where条件调用recordmanager的选择元组函数,在有operation要求的情况下通过逻辑运算进行筛选并构成对应的table
int deleteRecord(string table_name, string target_attr, Where where);输入:表名,where的限制条件
输出:返回删除的结果,删除成功 -1 失败 -0
功能:根据where条件调用recordmanager的成员函数函数,删除元组
void insertRecord(string table_name, Tuple& tuple);输入:表名,元组
输出:无
功能:向recordmanager中的table中调用成员函数插入元组
bool createTable(string table_name, Attribute attribute, int primary, Index index);输入:表名,元组,主码对应的属性下标(没有为-1),索引
输出:创建结果
功能:向recordmanager和catalogmanager调用成员函数写入table
bool dropTable(string table_name);输入:表名
输出:删除结果
功能:在recordmanager和catalogmanager中调用成员函数删除table
bool createIndex(string table_name, string index_name, string attr_name);输入:表名,元组,索引
输出:创建结果
功能:向indexmanager、recordmanager、catalogmanager中调用对应成员函数建立table的索引
bool dropIndex(string table_name, string index_name);输入:表名,索引名
输出:删除结果
功能:向indexmanager、catalogmanager中调用对应成员函数删除table的索引
Buffer Manager负责缓冲区的管理,主要功能有:
- 根据需要,读取指定的数据到系统缓冲区或将缓冲区中的数据写出到文件
- 实现缓冲区的替换算法,当缓冲区满时选择合适的页进行替换
- 记录缓冲区中各页的状态,如是否被修改过等
- 提供缓冲区页的pin功能,及锁定缓冲区的页,不允许替换出去 为提高磁盘I/O操作的效率,缓冲区与文件系统交互的单位是块,块的大小应为文件系统与磁盘交互单位的整数倍,一般可定为4KB或8KB。
在实际实现过程中,将Block 定义为4KB,将所有数据存入char 的数组中,数组大小为4096。
File 中存入了调用过的所有文件的头
class File{
public:
string filename;
int type;// index or record
Block * head;
File * next;
File * pre;
File(string _filename,int _type):filename(_filename),type(_type)\
,pin(false),head(NULL),next(NULL),pre(NULL){};
};其中,head 表示file 下的块
class Block
{
private:
string filename;
File * file;
bool dirty;
bool pin;
int offsetNum;
int UsingSize;
clock_t time;
Block * next;
Block * pre;
char *data;
friend File;
public:
Block();
~Block();
void clear(); // 清楚里面的内容
friend class buffermanager;
void SetPin();
void ClearPin();
void SetDirty();
void ClearDirty();
void SetClock();
void WriteBack(); //写回
void ReadIn();//通过offsetNum读入,每次读入4096个字节
void write(int offset, const char * data,int length);//写入数据,通过offset定位
void SetUsingSize(int size);//设置大小
char * FetchRecord( int offset , int size );//获取一条记录
char * GetContent();//获取所有记录
};class buffermanager
{
private:
Block block_pool [BLOCK_NUMBER];
int total_block;
public:
File * FileHead;
buffermanager():FileHead(NULL),total_block(0){};
~buffermanager();
File * GetFile( string tabame, int type );//读入文件,获得某个文件文件的头
Block * GetBlock( File * file, Block * position) //获得一个block
Block * GetNextBlock( File * file , Block * position);// 获得offsetNum + 1的块
Block * GetBlockByNum( File * file , int offsetNum );//通过offsetNum获得块
Block * GetEmptyBlock();//获得一个空的块
Block * GetReplaceBlock();
Block * GetBlockHead( File * file );//获得第一个块
void DeleteRecord( Block * b, int offset );
void DeleteFileFromList( string filename );//关掉文件
void CloseFile( File * file ); // 关掉某个文件
void ShowInfo(Block * tmp); // 调试用
void WriteBackAll(); //写回所有数据
};-
Block_pool 是一个块池,通过block_pool 实现内存管理,total_block 为现有的块数。
-
FileHead 是文件链表的头,这个文件链表中存放着File 的相关信息,如表名,是index还是record,
File * GetFile( string tabame, int type );输入:表名,表的类型,0为record,1为index
输出:对应的File*
功能:在需要访问某张表时,第一步需要通过GetFile() 获得一个File 的指针,这个File指针包含了该表的基本信息,在后面的函数中,都需要根据这个指针来填表中的内容。
Block * GetBlock( File * file, Block * position) //获得一个block输入:file 指针,block指针position 输出:一个块的地址 功能:获得一个块,并将这一个块连入File中的Block的链表。这个块读入的数据是position中第offsetNum + 1个BLOCKSIZE的数据
Block * GetNextBlock( File * file , Block * position);// 获得offsetNum输入:file 指针,block指针position 输出:一个块的地址 功能:获得一个块,块中数据为position的下一个BLOCKSIZE的数据,与GetBlock()类似,但是实际使用时有区别。GetNextBlock() 为返回offsetNum + 1的块,当链表中已经存在这个offsetNum + 1 的这个块,那么返回这个块,如果不存在,则读入,再返回
Block * GetBlockByNum( File * file , Block * position);// 获得offsetNum输入:file 指针,block指针position 输出:一个块的地址 功能:通过块号,获得一个块的地址。
Block * GetEmptyBlock();//获得一个空的块输入: 输出:一个块的地址 功能:获得一个块,这个块不读取内容。
Block * GetBlockHead( File * file );//获得第一个块输入:一个文件的指针 输出:获得第一个块 功能:获得一个表的第一个BLOCKSIZE的内容的块
Block * GetReplaceBlock();输入: 输出:一个待替换的块 功能:通过LRU算法,获得一个可以被替换的块,上层无需考虑此函数。
void DeleteFileFromList( string filename );//关掉文件
void CloseFile( File * file ); // 关掉某个文件功能:给出一个文件名,删除这个文件节点,CloseFile为辅助函数
块需要读入相应offsetNum 的内容,例如,offsetNum = 1,读入第2 (offsetNum+1) 个4kb的内容
如果进行查询操作,可使用GetNextBlock()以此获得块,Block中end 标志是否为最后一个块
如果进行插入或者删除操作,可使用Block的Write 函数,插入可给一条 record 的char * 的数组,并且给予插入的大小,删除可给全空的char * , 之后需要调用SetDirty(),SetUsingSize()对块的属性进行修改。Buffer Manager 不考虑记录的连续性的问题,需要Record Manager 进行对记录的移动。
如果进行更改,也可调用write ,只不过只需要SetDirty()即可,不需要SetUsingSize()
Record 文件夹中存入了数据库中表的记录。其中,一个表的记录存入一个文件中,如 students 表的信息存入 "students.db"。
在每个文件中,每条记录的格式如下保存,其中,deleted位占用一个byte,表示数据是否被删除了。
[deleted] [ record ] [deleted][ record ]
Record文件夹中,还存在FreeList,表示这个文件中哪些位置可以存储。