SG-Database
==============
SG-Database一种轻量、易扩展的关系式数据库系统。
其主要优势是,可以简单方便的向其中添加新的数据类型及对应索引。比如当我们想要在数据库中存储一些地图上的点,为了对其进行高效索引,需要建立类似KD-Tree等的索引结构,为了在传统的SQL数据库中实现这种索引,需要对数据库系统进行performance tuning,编程负担较重。而我们的数据库系统提供了更加方便易用的扩展结构,用户可以直接继承数据类型基类Basic实现自己的数据类型,并添加对应索引。添加的组件可以无开销地直接对接数据库,简单方便的实现对数据库系统功能的拓展。
在此基础上,我们实现了传统关系式数据库物理层面大部分功能和优化,如变更日志、IO优化、热区缓存、视图和表抽取、元组批操作等。在高层,我们放弃使用SQL语言,而是采用javascript语言和逻辑规则引擎联合进行对数据操作进行描述。使用javascript可以完成许多SQL语言很难编写或完成不了的数据操作,而且更符合大部分程序员的直觉。规则引擎用于描述WHERE条件,直接与索引模块对接,使得用户编写的索引结构可以直接接入查询功能当中。
由于只用了四天事件,还有一些功能我们没有完成。如数据操作语言(javascript)与数据库的对接、触发器(因为这与数据库查询语言相关)、与连接操作/多表查询有关的一些支持,以及并发控制(这十分重要,我们要多花一些时间进行研究设计)。
动态类型模块
-----------
### 需求
数据库需要存储不同类型的数据。因此数据库系统需要对数据类型提供支持。并要允许用户简单方便的定义自己的新数据类型,无开销地与数据库其它模块实现自动对接。
### 分析
不同数据类型具有不同数据成员,所以必须使用不同的类来存储。但列与表的对象都需要同存同取,不能对每个类型都建立一个对应的列/表类。因此需要引入动态类型特性。
### 设计
我们选择使用继承多态实现动态类型。首先定义数据类型基类(Basic类),其它类型均继承数据类型基类实现。在列类(col类)中,存储数据的对象为基类容器(vector<Basic*>),即将所有数据对象都向上转型为基类指针统一存储。
基类中定义虚函数getType,这个函数返回这个动态类型对象的类型。因此每个子类都需要对该虚函数进行实现。这样在向上转型之后依然可以获取数据的实际类型,以便在使用时进行逆变。
在col对象构造时,需要指定类型(因为关系式数据库中同一列存储的数据类型一定是相同的),当取用其中的数据时,按构造时指定的列类型进行转换。添加元素时也会进行类型检查,如添加的数据类型与列类型不符,将会抛出异常。
为了方便实现动态类型的配套操作,实现typeHelper静态空间,提供关于数据对象拷贝、判等、反序列化等功能。这样,需要使用这些功能时直接用基类指针作为参数调用函数即可,不需要手动转换再根据不同类型分别处理。相应地,在用户添加新数据类型时,也要在这些函数中添加新类型的处理case。
作为例子,我们实现了整型、浮点、字符串、布尔四种实值类型。
### 文档
#### Basic类
动态类型基类,直接创建该类型对象视为null数据
##### 公有成员
``` cpp
virtual TYPE getType()
```
获取数据对象实际类型
``` cpp
virtual string toStr()
```
序列化这个数据对象,转换为字符串
日志模块
-----------
### 需求
记录对用户数据库进行的修改(增删改)操作。基于这些记录,可以在合适的时候将这些修改统一写入硬盘(修改先立刻在内存中实现,再在合适的时候统一在硬盘上的文件实现),降低IO代价。
### 分析
为了降低IO代价,必然要进行定点增量式写入。因此需要将每一次对表的修改(增删改)作为一条日志记录,
### 设计
首先定义日志记录类(record类),其需要记录三种对表的修改模式:插入、删除和修改(元组)。
三种修改所需的信息不同:插入仅需要记录插入的元组,不关心位置(元组是无序的);删除只需要记录删除的元组(相对当前表的)位置(下标);修改需要记录需修改元组的位置(下标)和修改内容(使用vector<Basic*>记录,长度为元组长度,不修改的条目可以使用占位符占位)。因此,三种不同的修改可以使用三种不同的构造函数,在构造函数中记录修改类型(增/删/改),便于在实现修改时,通过修改类型进入不同的分支。
将这些修改写入硬盘时,按日志队列顺序从头到尾实现修改即可。所有修改都已实现到硬盘后,日志队列将被清空。
### 文档
#### record类
日志记录类
##### 私有成员
``` cpp
void setAddTarget(vector<Basic*> addTarget)
```
设置修改的目标元组。
由于日志记录是在调用table修改(增删改)方法时进行同步创建,因此vector<Basic*>中的Basic对象来源于col,col持有所有权,有可能在之后被释放。如果被释放后再用这个记录对硬盘进行操作将造成文件错误。因此需要将vector<Basic*>中每个元素依次复制,复制后的对象由本类持有所有权。
##### 公有成员
``` cpp
int opSub=-1
```
操作元组的下标(相对于创建该记录时刻表的下标)
``` cpp
recType type
```
操作类型,分为增(add)删(del)改(mod)
``` cpp
vector<Basic*> targetTuple
```
增加与修改的目标元组(要插入什么/要改成什么,修改可使用占位符)
``` cpp
record(vector<Basic*> addTarget)
```
“增”类型日志记录的构造函数
``` cpp
record(int opSub)
```
“删”类型日志记录的构造函数
``` cpp
record(int opSub, vector<Basic*> modTarget)
```
“改”类型日志记录的构造函数
``` cpp
record(const record &r)
```
拷贝构造函数
``` cpp
~record()
```
析构函数,释放持有所有权的targetTuple成员中Basic对象
##### 具体说明
* 当`record.type=ADD`的时候,`record.targetTuple`里面所存储的是新添加的元组,`targetTuple[0]`就是这个元素第一列的值,`targetTuple[1]`就是第二个,以此类推。
* 当`record.type=MOD`的时候,`record.opSub`存储的是要修改目标行的元组(一行对应一个元组)。`record.targetTuple`里面存储的是这个元组该进行怎样的修改,注意如果里面元素`getType`结果是`Placeholder`代表这一列不进行修改(例如`targetTuple[1].getType=="Placeholder"`,这就表示第二列不变),当`getType`结果为其他的值时才代替表进行更改(里面存的就是改完的实际值)。
* 当`record.type=DEL`的时候,`opSub`中所存储的就是要删除元素的所在行(下标)。
#### table类中的日志成员
``` cpp
list<record> allRecord
```
存储上次硬盘写入后所有修改的日志记录。调用table的增删改方法时进行同步追加;调用table的硬盘文件更新方法,实现其中所有修改后对其进行清空。
视图与表抽取模块
-----------
### 需求
快速的使用一个表中的部分元组构造视图或复制一张新表。
视图是从一个基本表中导出的虚拟的表。在系统的数据字典中仅存放了视图的定义,不存放视图对应的数据。这些表的数据存放在数据库中。那些用于产生视图的表叫做该视图的基表。
视图看上去非常像数据库的物理表,对它的操作同任何其它的表一样。当通过视图修改数据时,实际上是在改变基表中的数据;相反地,基表�