diff --git a/MicroSQL.vcxproj b/MicroSQL.vcxproj index c5e2905..a9aeaea 100644 --- a/MicroSQL.vcxproj +++ b/MicroSQL.vcxproj @@ -27,6 +27,7 @@ + diff --git a/MicroSQL.vcxproj.filters b/MicroSQL.vcxproj.filters index afb7ee4..80ea2e4 100644 --- a/MicroSQL.vcxproj.filters +++ b/MicroSQL.vcxproj.filters @@ -95,5 +95,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/README.docx b/README.docx new file mode 100644 index 0000000..c920d30 Binary files /dev/null and b/README.docx differ diff --git a/README.md b/README.md index 18ab69d..af48ebb 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,241 @@ - -# MicroSQL 开发文档 - ----------------------------- - -##Page/PageFile - -PF模块中除了构造函数与析构函数之外的每个函数都返回一个整数. 返回值为0表示正常完成. 所有非0返回值均暗示了有异常情况或错误发生. 返回值为正数表示遇到异常(比如读到文件尾或关闭一个未打开的文件等对程序影响不大的错误); 返回值为负表示发生了系统无法自动处理的错误. - -###页缓冲池 -获取一个文件中某页面上的数据需要先把页面存入缓冲池(维护在内存中), 然后在缓冲池中操作(读写)数据. 当一个内存中的页面和数据正在被操作时, 必须先将页面的状态设为locked (固定). 当某个进程对页面的所有操作均结束后, 必须立即把页面状态改为unlocked, 但此时并不需要把页面移出缓冲池. -只有当需要读一个新的页, 并且缓冲池中的内存不足时才会选择一个unlocked的页面移出. 所使用的选择算法为Least-Recently-Used (LRU). 当一个页面被移出缓冲池时, 只有该页面被标记为dirty才会将硬盘上页面对应的文件重写以更新页面文件, 即非dirty的页面被移出缓冲池时不需要做任何工作. 但也提供了接口使得PF模块可以强制将一个非dirty的页面数据写入硬盘, 以及在不移出页面的情况下将所有在缓冲池中的dirty页面的数据写入硬盘. -PageManager必须保证每个页面只能同时被一个事务使用, 即一个locked状态下的页面不能被locked. - -###页面号 -一个文件中的页面需要用页面号标识, 页面号对应其在文件中的位置. 当新建一个文件并分配页面时, 页面号是顺序增长的. 但一个页面被删除之后, 新分配的页面其页面号不一定是顺序的, 而是在之前分配过的页面上寻找一个最近被删除的页面(用栈存储删除的页面号)来存放该页面数据. 若栈为空时才在之前的页面之后再分配一个新的页面. - ------------------------- - -> 注: -> 1. 每个页面的大小(字节)用PF_PAGE_SIZE表示, 默认为4K (4096字节) -> 2. 内存池中的页面数用PF_BUFFER_SIZE表示, 默认为40 -> 3. 所有作为存储函数返回结果的指针传进来时应保证其原来的数据(如果有的话)已经无需再使用了 - - -##RecordManager -RM模块提供的类和方法用于把记录存储在文件中, 相当于PF模块的客户端. 在这个模块中需要调用PF模块中已经实现的函数. - -###文件头 -为了便于管理文件内容, 可以把每个文件的第一个Page作为一个特殊的Header Page, 用于存储空闲空间的信息, 文件存储的记录数, 每个页面存储的记录数, 文件的当前页面数以及其他与整个文件有关的信息. 每个页面也包含一个页面头. - -###记录标识符 -RecordIdentifier类对一个指定文件中的所有记录提供唯一标识, 因此一条指定记录的标识符应该是保持不变的. 即一个标识符的属性在记录更新或其他记录插入/删除的条件下都是恒定的. 每个文件的页面有数量不等的Slot(根据每个文件的Record的大小确定), 但一个文件内的所有页面Slot数相同. 在这个系统中只用页面号(PageNum)以及槽号(SlotNum)来构成为RecordIdentifier. - -###记录空闲空间 -当需要插入记录时, 不应该线性搜索有空余的页面. 为了提高搜索效率, 可以使用一个页的链表来存储有空余Slot的页面. - - -每个文件中存储的记录必须是等长的, 这样能更方便地管理每个页面上的记录和空闲空间, 而且保证了每个记录的位置都能够方便地访问到. 最好每个表单独存储在一个文件中. - - + +# MicroSQL 开发文档 + +---------------------------- + +##一. 总体框架 + +#### 1.1 概述 + +MicroSQL是一个轻量级的关系型数据库, 框架的设计思路主要根据斯坦福大学的[Database System Implementation(CS346)](https://web.stanford.edu/class/cs346/2015/)课程提供的Redbase数据库框架来设计. +主要包含5个模块: + - PageFile(PF) + - RecordManager(RM) + - IndexManager(IX) + - SystemManager(SM) + - QueryManager(QM) + 在具体的实现中, 为了遵循模块化程序设计的原则, 将各个模块按照不同的功能分成多个类来实现, 具体实现细节将在后面介绍 + +#### 1.2 层次结构 + +#### 1.3 基本功能 + - 支持的数据类型: INT, FLOAT, STRING(需要指定大小). + - 数据库的建立和删除,以及数据库的统计信息查询 + - 表的建立和删除,以及表的统计信息查询 + - 索引的建立和删除 + - 使用与MySQL语法相似的SQL语句对表或数据库中的数据执行增、删、改、查操作 + +#### 1.4 设计语言和运行环境 +开发环境:Visual Studio 2015 +编程语言:C++(主要使用C++11推荐语法) + +## 二. 各模块的具体功能 +#### 2.1 Page File Manager(PF) +PF模块是系统的最底层模块, 主要向高层次的结构(Index Manager和Record Manager)提供以页为基本单元的文件IO操作. 在该模块中, 主要实现了创建, 删除, 打开和关闭文件四个操作. +对于单个文件(PageFile类)的操作, 必须先从PF模块打开一个PageFile实例为操作对象, 以页(Page)为最小访问单元. PageFile提供了从文件中获取一个新的Page, 获取一个特定页号的Page, 强制更新(Force)文件中特定页号的Page等方法. +实际上, 由于文件系统IO速度比较慢, 为了提高获取数据的速度, PM模块还需要对每个文件维护一个Buffer(BufferManager类). 每个BufferManager实例管理一个PageFile实例以及一个Buffer(用哈希表结构存储在内存中), 上层模块只能通过BufferManager来间接地访问PageFile获取所需Page. + +#### 2.2 Record Manager(RM) +RM模块是管理记录文件的模块, 主要面向Query Manager和System Manager两个模块. RM模块只提供了对记录文件的创建, 删除, 打开和关闭四个操作. +在MicroSQL的实现中, 每个表中有若干条记录, 一个表中的所有记录必须存在同一个记录文件(RecordFile)中. 对单个记录文件的操作, 必须通过RecordFile实例进行操作. RecordFile提供了对该文件中记录的增删改查四个操作. + +#### 2.3 Index Manager(IX) +IX模块是管理索引的模块, 主要提供了对某个表的属性进行创建, 删除索引, 并对已创建的索引的打开, 关闭四个操作. 关于索引的具体实现, MicroSQL的索引与大多数数据库引擎类似, 使用B+树作为索引的数据结构, 每个B+树的结点存储在一个Page中, 以达到较高的索引效率. +其他模块对于单个索引的数据插入, 删除, 查询, 必须先通过Index Manager获取一个IndexHandle实例, 并通过这个IndexHandle实例进行操作. + +#### 2.4 System Manager(SM) +SM模块直接面向Command Parser,提供一些可能会影响整个数据库系统的操作,包括: + + - 创建、删除一个数据库/关系表/索引等(DDL) + - 维护系统中各个数据库和表的Catalog + - 从文件中导入数据 + - 设置数据库或表的参数 + - 输出关系表 + +#### 2.5 Query Manager(QM) +QM模块与SM模块类似,主要向Command Parser提供了用于执行特定的SQL操作。当前的实现支持的SQL操作有Select(包括多表联查),Insert,Delete,Update四种操作。操作的执行主要先通过System Manager获取查询表的结构, 分析后通过调用Record Manager的方法查询并解析查询的结果, 返回给Command Parser. + + +## 三. 重要接口 + +### 2.1 PF模块 +#### PageManager类 + +`RETCODE CreateFile (const char * fileName); // Create a new file` +创建一个新的文件(索引的结点或是记录文件) + +`RETCODE DestroyFile (const char * fileName); // Destroy a file` +删除一个文件 + +`RETCODE OpenFile (const char * fileName, PageFilePtr & fileHandle); // Open a file` +打开一个文件并获取该文件的PageFile实例 + +`RETCODE CloseFile (PageFilePtr &fileHandle); // Close a file` +关闭一个文件并释放PageFile实例 + +#### PageFile类(只能由BufferManager调用) +`RETCODE GetThisPage (PageNum pageNum, PagePtr &pageHandle) ;` +获取该文件中特定一页, 返回Page实例 + +`RETCODE AllocatePage (PagePtr &pageHandle);` +分配一个新的页, 返回Page实例 + +`RETCODE DisposePage (PageNum pageNum);` +释放(不再使用)特定的页 + +`RETCODE ForcePage (PageNum page, const PagePtr & pageHande);` +强制把一个页写入文件中的特定页中 + +#### BufferManager类(由其他类调用, 间接操作PageFile) + +`RETCODE GetPage (PageNum page, PagePtr & pBuffer); ` +获取一个特定的页, 返回Page实例 + +`RETCODE MarkDirty (PageNum page);` +把一个页标记为Dirty(修改过) + +`RETCODE LockPage (PageNum page);` +把一个页锁定在Buffer中 + +`RETCODE UnlockPage (PageNum page);` +把一个已锁定的页释放 + +`RETCODE ForcePage (PageNum page);` +强制把一个页写入文件 + +`RETCODE FlushPages ( );` +把Buffer中的所有页写入文件 + +`RETCODE AllocatePage (PagePtr & page); ` +分配一个新的页, 返回Page实例 + +`RETCODE DisposePage (PageNum page);` +释放特定的页 + +### 2.2 RM模块 + + +### 2.3 IX模块 + + +### 2.4 SM模块 +#### SystemManager 类 + +`RETCODE CreateDb (const char * dbName, PageFileManagerPtr & pfMgr);` +创建一个新的数据库 + +`RETCODE OpenDb (const char *dbName); ` +打开一个数据库 + +`RETCODE CloseDb ( ); ` +关闭当前打开的数据库 + +`RETCODE CreateTable (const char *relName, Create relation, int attrCount, AttrInfo *attributes);` +(在当前打开的数据库中)创建表 + +`RETCODE DropTable (const char *relName);` +删除表 + +`RETCODE CreateIndex (const char *relName, const char *attrName);` +对某个表的一个属性建立索引 + +`RETCODE DropIndex (const char *relName, Destroy index, const char *attrName);` +删除索引 + +#### 2.5 Query Manager(QM) + +`RETCODE Select ( +int nSelAttrs, // 查询的属性个数 +const RelAttr selAttrs[], // 查询的属性 +int nRelations, // 查询的关系表个数 +const char * const relations[], // 查询的关系表 +int nConditions, // 查询的条件个数 +const Condition conditions[]); // 查询的具体条件` +根据传入的参数执行查询操作 + +`RETCODE Insert (const char *relName, // relation to insert into +int nValues, // # values to insert +const Value values[]); // values to insert` + +`RETCODE Delete (const char *relName, // relation to delete from +int nConditions, // # conditions in Where clause +const Condition conditions[]); // conditions in Where clause` + + +`RETCODE Update (const char *relName, // relation to update +const RelAttr &updAttr, // attribute to update +const int bIsValue, // 0/1 if RHS of = is attribute/value +const RelAttr &rhsRelAttr, // attr on RHS of = +const Value &rhsValue, // value on RHS of = +int nConditions, // # conditions in Where clause +const Condition conditions[]); // conditions in Where clause` + + + +## 四. 使用语法 +#### Select + + +#### Insert + + +#### Update + + +#### Delete + + +## 五. 实际测试 + + +## 六. 组内分工 + + + +##Page/PageFile + +PF模块中除了构造函数与析构函数之外的每个函数都返回一个整数. 返回值为0表示正常完成. 所有非0返回值均暗示了有异常情况或错误发生. 返回值为正数表示遇到异常(比如读到文件尾或关闭一个未打开的文件等对程序影响不大的错误); 返回值为负表示发生了系统无法自动处理的错误. + +###页缓冲池 +获取一个文件中某页面上的数据需要先把页面存入缓冲池(维护在内存中), 然后在缓冲池中操作(读写)数据. 当一个内存中的页面和数据正在被操作时, 必须先将页面的状态设为locked (固定). 当某个进程对页面的所有操作均结束后, 必须立即把页面状态改为unlocked, 但此时并不需要把页面移出缓冲池. +只有当需要读一个新的页, 并且缓冲池中的内存不足时才会选择一个unlocked的页面移出. 所使用的选择算法为Least-Recently-Used (LRU). 当一个页面被移出缓冲池时, 只有该页面被标记为dirty才会将硬盘上页面对应的文件重写以更新页面文件, 即非dirty的页面被移出缓冲池时不需要做任何工作. 但也提供了接口使得PF模块可以强制将一个非dirty的页面数据写入硬盘, 以及在不移出页面的情况下将所有在缓冲池中的dirty页面的数据写入硬盘. +PageManager必须保证每个页面只能同时被一个事务使用, 即一个locked状态下的页面不能被locked. + +###页面号 +一个文件中的页面需要用页面号标识, 页面号对应其在文件中的位置. 当新建一个文件并分配页面时, 页面号是顺序增长的. 但一个页面被删除之后, 新分配的页面其页面号不一定是顺序的, 而是在之前分配过的页面上寻找一个最近被删除的页面(用栈存储删除的页面号)来存放该页面数据. 若栈为空时才在之前的页面之后再分配一个新的页面. + +------------------------ + +> 注: +> 1. 每个页面的大小(字节)用PF_PAGE_SIZE表示, 默认为4K (4096字节) +> 2. 内存池中的页面数用PF_BUFFER_SIZE表示, 默认为40 +> 3. 所有作为存储函数返回结果的指针传进来时应保证其原来的数据(如果有的话)已经无需再使用了 + + +##RecordManager +RM模块提供的类和方法用于把记录存储在文件中, 相当于PF模块的客户端. 在这个模块中需要调用PF模块中已经实现的函数. + +###文件头 +为了便于管理文件内容, 可以把每个文件的第一个Page作为一个特殊的Header Page, 用于存储空闲空间的信息, 文件存储的记录数, 每个页面存储的记录数, 文件的当前页面数以及其他与整个文件有关的信息. 每个页面也包含一个页面头. + +###记录标识符 +RecordIdentifier类对一个指定文件中的所有记录提供唯一标识, 因此一条指定记录的标识符应该是保持不变的. 即一个标识符的属性在记录更新或其他记录插入/删除的条件下都是恒定的. 每个文件的页面有数量不等的Slot(根据每个文件的Record的大小确定), 但一个文件内的所有页面Slot数相同. 在这个系统中只用页面号(PageNum)以及槽号(SlotNum)来构成为RecordIdentifier. + +###记录空闲空间 +当需要插入记录时, 不应该线性搜索有空余的页面. 为了提高搜索效率, 可以使用一个页的链表来存储有空余Slot的页面. + + +每个文件中存储的记录必须是等长的, 这样能更方便地管理每个页面上的记录和空闲空间, 而且保证了每个记录的位置都能够方便地访问到. 最好每个表单独存储在一个文件中. + + +###访问记录 +当主程序需要访问表Table中一条标识符为Id的记录时, 分两个步骤: 首先通过RecordManager->OpenFile(Table)申请一个RecordFile(实际上, 只能通过这个类来访问数据库的记录), 通过RecordFile->GetRec(Id)来获取一个Record对象. +但是由于这样效率比较低下, 因此主要还是用FileScan类来让客户端与数据库进行交互 + +##Index Manager + + +##System Manager + + +##Query Manager diff --git a/src/IndexScan.hpp b/src/IndexScan.hpp index d423f88..96baf04 100644 --- a/src/IndexScan.hpp +++ b/src/IndexScan.hpp @@ -19,7 +19,6 @@ class IndexScan { private: - //std::vector<> }; @@ -28,3 +27,15 @@ IndexScan::IndexScan ( ) { IndexScan::~IndexScan ( ) { } + +inline RETCODE IndexScan::OpenScan (const IndexHandle & indexHandle, CompOp compOp, void * value) { + +} + +inline RETCODE IndexScan::GetNextEntry (RecordIdentifier & rid) { + return RETCODE ( ); +} + +inline RETCODE IndexScan::CloseScan ( ) { + return RETCODE ( ); +} diff --git a/src/Printer.hpp b/src/Printer.hpp new file mode 100644 index 0000000..0eaac3e --- /dev/null +++ b/src/Printer.hpp @@ -0,0 +1,347 @@ +#pragma once + +#include "Utils.hpp" +#include +#include +#include +#include + +#ifndef mmin +#define mmin(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef mmax +#define mmax(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +const int MAXPRINTSTRING = ( 2 * Utils::MAXNAMELEN) + 5; + +// Print some number of spaces +void Spaces (int maxLength, int printedSoFar); + +struct DataAttrInfo; +class Tuple; + +class Printer { +public: + // Constructor. Takes as arguments an array of attributes along with + // the length of the array. + Printer (const DataAttrInfo *attributes, const int attrCount); + Printer (const Tuple& t); + + ~Printer ( ); + + void PrintHeader (std::ostream &c) const; + + // Two flavors for the Print routine. The first takes a char* to the + // data and is useful when the data corresponds to a single record in + // a table -- since in this situation you can just send in the + // RecData. The second will be useful in the QL layer. + void Print (std::ostream &c, const char * const data); + void Print (std::ostream &c, const void * const data[]); + void Print (std::ostream &c, const Tuple& t); + + void PrintFooter (std::ostream &c) const; + +private: + void Init (const DataAttrInfo *attributes_, const int attrCount_); + +private: + DataAttrInfo *attributes; + int attrCount; + + // An array of strings for the header information + char **psHeader; + // Number of spaces between each attribute + int *spaces; + + // The number of tuples printed + int iCount; +}; + + +// +// printer.cc +// + +// This file contains the interface for the Printer class and some +// functions that will be used by both the SM and QL components. + +#include +#include +#include +#include "printer.hpp" + +using namespace std; + +// +// void Spaces(int maxLength, int printedSoFar) +// +// This method will output some spaces so that print entry will align everythin +// nice and neat. +// +void Spaces (int maxLength, int printedSoFar) { + for ( int i = printedSoFar; i < maxLength; i++ ) + std::cout << " "; +} + +// +// ------------------------------------------------------------------------------ +// +Printer::Printer (const Tuple& t) { + Init (t.GetAttributes ( ), t.GetAttrCount ( )); +} + +// +// Printer +// +// This class handles the printing of tuples. +// +// DataAttrInfo - describes all of the attributes. Defined +// within sm.h +// attrCount - the number of attributes +// +Printer::Printer (const DataAttrInfo *attributes_, const int attrCount_) { + Init (attributes_, attrCount_); +} + +void Printer::Init (const DataAttrInfo *attributes_, const int attrCount_) { + attrCount = attrCount_; + attributes = new DataAttrInfo[attrCount]; + + for ( int i = 0; i < attrCount; i++ ) { + attributes[i] = attributes_[i]; + // std::cout << "Printer::init i, offset " << attributes[i].offset << endl; + } + // Number of tuples printed + iCount = 0; + + // Figure out what the header information will look like. Normally, + // we can just use the attribute name, but if that appears more than + // once, then we should use "relation.attribute". + + // this line broke when using CC + // changing to use malloc and free instead of new and delete + // psHeader = (char **) new (char *)[attrCount]; + psHeader = ( char** ) malloc (attrCount * sizeof (char*)); + + // Also figure out the number of spaces between each attribute + spaces = new int[attrCount]; + + for ( int i = 0; i < attrCount; i++ ) { + // Try to find the attribute in another column + int bFound = 0; + psHeader[i] = new char[MAXPRINTSTRING]; + memset (psHeader[i], 0, MAXPRINTSTRING); + + for ( int j = 0; j < attrCount; j++ ) + if ( j != i && + strcmp (attributes[i].attrName, + attributes[j].attrName) == 0 ) { + bFound = 1; + break; + } + + if ( bFound ) + sprintf_s (psHeader[i], MAXPRINTSTRING,"%s.%s", + attributes[i].relName, attributes[i].attrName); + else + strcpy_s (psHeader[i], MAXPRINTSTRING, attributes[i].attrName); + + if ( attributes[i].attrType == STRING ) + spaces[i] = mmin (attributes[i].attrLength, MAXPRINTSTRING); + else + spaces[i] = mmax (12, strlen (psHeader[i])); + + // We must subtract out those characters that will be for the + // header. + spaces[i] -= strlen (psHeader[i]); + + // If there are negative (or zero) spaces, then insert a single + // space. + if ( spaces[i] < 1 ) { + // The psHeader will give us the space we need + spaces[i] = 0; + strcat_s (psHeader[i], MAXPRINTSTRING," "); + } + } +} + + +// +// Destructor +// +Printer::~Printer ( ) { + for ( int i = 0; i < attrCount; i++ ) + delete[] psHeader[i]; + + delete[] spaces; + //delete [] psHeader; + free (psHeader); + delete[] attributes; +} + +// +// PrintHeader +// +void Printer::PrintHeader (std::ostream &c) const { + int dashes = 0; + int iLen; + int i, j; + + for ( i = 0; i < attrCount; i++ ) { + // Print out the header information name + c << psHeader[i]; + iLen = strlen (psHeader[i]); + dashes += iLen; + + for ( j = 0; j < spaces[i]; j++ ) + c << " "; + + dashes += spaces[i]; + } + + c << "\n"; + for ( i = 0; i < dashes; i++ ) c << "-"; + c << "\n"; +} + +// +// PrintFooter +// +void Printer::PrintFooter (std::ostream &c) const { + c << "\n"; + c << iCount << " tuple(s).\n"; +} + +// +// Print +// +// data - this is an array of void *. This print routine is used by +// the QL Layer. +// +// Unfortunately, this is essentially the same as the other Print +// routine. +// +void Printer::Print (std::ostream &c, const void * const data[]) { + char str[MAXPRINTSTRING], strSpace[50]; + int i, a; + float b; + + // Increment the number of tuples printed + iCount++; + + for ( i = 0; iMAXPRINTSTRING ) { + strcpy_s (str, MAXPRINTSTRING - 1, ( char * ) data[i]); + str[MAXPRINTSTRING - 3] = '.'; + str[MAXPRINTSTRING - 2] = '.'; + c << str; + Spaces (MAXPRINTSTRING, strlen (str)); + } else { + strcpy_s (str, attributes[i].attrLength, ( char * ) data[i]); + c << str; + if ( attributes[i].attrLength < ( int ) strlen (psHeader[i]) ) + Spaces (strlen (psHeader[i]), strlen (str)); + else + Spaces (attributes[i].attrLength, strlen (str)); + } + } + if ( attributes[i].attrType == INT ) { + memcpy (&a, data[i], sizeof (int)); + sprintf_s (strSpace, MAXPRINTSTRING,"%d", a); + c << a; + if ( strlen (psHeader[i]) < 12 ) + Spaces (12, strlen (strSpace)); + else + Spaces (strlen (psHeader[i]), strlen (strSpace)); + } + if ( attributes[i].attrType == FLOAT ) { + memcpy (&b, data[i], sizeof (float)); + sprintf_s (strSpace, MAXPRINTSTRING,"%f", b); + c << strSpace; + if ( strlen (psHeader[i]) < 12 ) + Spaces (12, strlen (strSpace)); + else + Spaces (strlen (psHeader[i]), strlen (strSpace)); + } + } + c << "\n"; +} + +void Printer::Print (std::ostream &c, const Tuple& t) { + const char * data; + t.GetData (data); + // std::cout << "Printer::Print(tuple) " << t << endl; + Print (c, data); +} + +// +// Print +// +// data - the actual data for the tuple to be printed +// +// The routine tries to make things line up nice, however no +// attempt is made to keep the tuple constrained to some number of +// characters. +// +void Printer::Print (std::ostream &c, const char * const data) { + char str[MAXPRINTSTRING], strSpace[50]; + int i, a; + float b; + + if ( data == NULL ) + return; + + // Increment the number of tuples printed + iCount++; + + for ( i = 0; iMAXPRINTSTRING ) { + strcpy_s (str, MAXPRINTSTRING - 1, data + attributes[i].offset); + str[MAXPRINTSTRING - 3] = '.'; + str[MAXPRINTSTRING - 2] = '.'; + c << str; + Spaces (MAXPRINTSTRING, strlen (str)); + } else { + strcpy_s (str, attributes[i].attrLength, data + attributes[i].offset); + c << str; + if ( attributes[i].attrLength < ( int ) strlen (psHeader[i]) ) + Spaces (strlen (psHeader[i]), strlen (str)); + else + Spaces (attributes[i].attrLength, strlen (str)); + } + } + if ( attributes[i].attrType == INT ) { + memcpy (&a, ( data + attributes[i].offset ), sizeof (int)); + sprintf_s (strSpace, MAXPRINTSTRING,"%d", a); + c << a; + if ( strlen (psHeader[i]) < 12 ) + Spaces (12, strlen (strSpace)); + else + Spaces (strlen (psHeader[i]), strlen (strSpace)); + } + if ( attributes[i].attrType == FLOAT ) { + memcpy (&b, ( data + attributes[i].offset ), sizeof (float)); + sprintf_s (strSpace, MAXPRINTSTRING, "%f", b); + c << strSpace; + if ( strlen (psHeader[i]) < 12 ) + Spaces (12, strlen (strSpace)); + else + Spaces (strlen (psHeader[i]), strlen (strSpace)); + } + } + c << "\n"; +} diff --git a/src/QueryManager.hpp b/src/QueryManager.hpp index f816c85..b9e759f 100644 --- a/src/QueryManager.hpp +++ b/src/QueryManager.hpp @@ -6,6 +6,7 @@ #include "Utils.hpp" #include "Iterator.hpp" +#include "Printer.hpp" #include "RecordFile.hpp" #include "SystemManager.hpp" #include "IndexManager.hpp" @@ -101,6 +102,47 @@ inline RETCODE QueryManager::Select (int nSelAttrs, const RelAttr selAttrs[], in inline RETCODE QueryManager::Insert (const char * relName, int nValues, const Value values[]) { + RETCODE rc = smm->SemCheck (relName); + if ( rc != 0 ) return rc; + + int attrCount; + DataAttrInfo* attr; + rc = smm->GetFromTable (relName, attrCount, attr); + + if ( nValues != attrCount ) { + delete[] attr; + return RETCODE::INVALIDINSERT; + } + + int size = 0; + for ( int i = 0; i < nValues; i++ ) { + if ( values[i].type != attr[i].attrType ) { + delete[] attr; + return RETCODE::TYPEMISMATCH; + } + size += attr[i].attrLength; + } + + char * buf = new char[size]; + int offset = 0; + for ( int i = 0; i < nValues; i++ ) { + assert (values[i].data != NULL); + memcpy (buf + offset, + values[i].data, + attr[i].attrLength); + offset += attr[i].attrLength; + } + + rc = smm->LoadRecord (relName, size, buf); + if ( rc != 0 ) return rc; + + Printer p (attr, attrCount); + p.PrintHeader (cout); + p.Print (cout, buf); + p.PrintFooter (cout); + + delete[] attr; + delete[] buf; return RETCODE::COMPLETE; diff --git a/src/SystemManager.hpp b/src/SystemManager.hpp index 95c70a4..c672a66 100644 --- a/src/SystemManager.hpp +++ b/src/SystemManager.hpp @@ -13,6 +13,9 @@ #include class SystemManager { + + friend class QueryManager; + public: SystemManager (const IndexManagerPtr & ixm, const RecordFileManagerPtr & rm); diff --git a/src/Utils.hpp b/src/Utils.hpp index fa4ac5b..864a9ed 100644 --- a/src/Utils.hpp +++ b/src/Utils.hpp @@ -8,14 +8,14 @@ #include #include #include - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif /* @@ -97,6 +97,7 @@ enum RETCODE { TYPEMISMATCH, ENTRYNOTFOUND, BADOP, + INVALIDINSERT, }; enum AttrType {